# Fanfix
+## Version (in progress)
+
+- Remember the word count and the date of creation of Fanfix stories
+- UI: option to show the word count instead of the author below the book title
+
## Version 1.3.1
- UI: can now display books by Author
private int number;
private List<Paragraph> paragraphs = new ArrayList<Paragraph>();
private List<Paragraph> empty = new ArrayList<Paragraph>();
+ private long words;
/**
* Create a new {@link Chapter} with the given information.
return paragraphs == null ? empty.iterator() : paragraphs.iterator();
}
+ /**
+ * The number of words in this {@link Chapter}.
+ *
+ * @return the number of words
+ */
+ public long getWords() {
+ return words;
+ }
+
+ /**
+ * The number of words in this {@link Chapter}.
+ *
+ * @param words
+ * the number of words to set
+ */
+ public void setWords(long words) {
+ this.words = words;
+ }
+
/**
* Display a DEBUG {@link String} representation of this object.
*/
private String publisher;
private String type;
private boolean imageDocument;
+ private long words;
+ private String creationDate;
/**
* The title of the story.
this.imageDocument = imageDocument;
}
+ /**
+ * The number of words in the related {@link Story}.
+ *
+ * @return the number of words
+ */
+ public long getWords() {
+ return words;
+ }
+
+ /**
+ * The number of words in the related {@link Story}.
+ *
+ * @param words
+ * the number of words to set
+ */
+ public void setWords(long words) {
+ this.words = words;
+ }
+
+ /**
+ * The (Fanfix) {@link Story} creation date.
+ *
+ * @return the creationDate
+ */
+ public String getCreationDate() {
+ return creationDate;
+ }
+
+ /**
+ * The (Fanfix) {@link Story} creation date.
+ *
+ * @param creationDate
+ * the creationDate to set
+ */
+ public void setCreationDate(String creationDate) {
+ this.creationDate = creationDate;
+ }
+
public int compareTo(MetaData o) {
String oUuid = o == null ? null : o.getUuid();
return getUuid().compareTo(oUuid);
private ParagraphType type;
private String content;
-
- /**
- * Create a new {@link Paragraph} with the given values.
- *
- * @param type
- * the {@link ParagraphType}
- * @param content
- * the content of this paragraph
- */
- public Paragraph(ParagraphType type, String content) {
- this.type = type;
- this.content = content;
- }
+ private long words;
/**
* Create a new {@link Paragraph} with the given image.
* the content image of this paragraph
*/
public Paragraph(URL imageUrl) {
- this.type = ParagraphType.IMAGE;
- this.content = imageUrl.toString();
+ this(ParagraphType.IMAGE, imageUrl.toString(), 0);
+ }
+
+ /**
+ * Create a new {@link Paragraph} with the given values.
+ *
+ * @param type
+ * the {@link ParagraphType}
+ * @param content
+ * the content of this paragraph
+ * @param words
+ * the number of words
+ */
+ public Paragraph(ParagraphType type, String content, long words) {
+ this.type = type;
+ this.content = content;
+ this.words = words;
}
/**
this.content = content;
}
+ /**
+ * The number of words in this {@link Paragraph}.
+ *
+ * @return the number of words
+ */
+ public long getWords() {
+ return words;
+ }
+
+ /**
+ * The number of words in this {@link Paragraph}.
+ *
+ * @param words
+ * the number of words to set
+ */
+ public void setWords(long words) {
+ this.words = words;
+ }
+
/**
* Display a DEBUG {@link String} representation of this object.
*/
}
writeMeta(infoWriter, "EPUBCREATOR", BasicOutput.EPUB_CREATOR);
writeMeta(infoWriter, "PUBLISHER", meta.getPublisher());
+ writeMeta(infoWriter, "WORDCOUNT",
+ Long.toString(meta.getWords()));
+ writeMeta(infoWriter, "CREATION_DATE", meta.getCreationDate());
} finally {
infoWriter.close();
}
* the story {@code}link MetaData}
* @param cached
* TRUE if it is locally cached
+ * @param seeWordcount
+ * TRUE to see word counts, FALSE to see authors
*/
- public LocalReaderBook(MetaData meta, boolean cached) {
+ public LocalReaderBook(MetaData meta, boolean cached, boolean seeWordCount) {
this.cached = cached;
this.meta = meta;
- String optAuthor = meta.getAuthor();
- if (optAuthor != null && !optAuthor.isEmpty()) {
- optAuthor = "(" + optAuthor + ")";
+ String optSecondary = meta.getAuthor();
+ if (seeWordCount) {
+ if (meta.getWords() >= 4000) {
+ optSecondary = (meta.getWords() / 1000) + "k words";
+ } else if (meta.getWords() > 0) {
+ optSecondary = meta.getWords() + " words";
+ } else {
+ optSecondary = "empty";
+ }
+ }
+
+ if (optSecondary != null && !optSecondary.isEmpty()) {
+ optSecondary = "(" + optSecondary + ")";
}
icon = new JLabel(generateCoverIcon(meta.getCover()));
+ "%s" + "<br>" + "<span style='color: %s;'>"
+ "%s" + "</span>" + "</body>" + "</html>",
TEXT_WIDTH, TEXT_HEIGHT, meta.getTitle(), AUTHOR_COLOR,
- optAuthor));
+ optSecondary));
setLayout(new BorderLayout(10, 10));
add(icon, BorderLayout.CENTER);
private ProgressBar pgBar;
private JMenuBar bar;
private LocalReaderBook selectedBook;
+ private boolean words; // words or authors (secondary info on books)
/**
* Create a new {@link LocalReaderFrame}.
for (LocalReaderGroup group : booksByType.keySet()) {
List<MetaData> stories = Instance.getLibrary().getListByType(
booksByType.get(group));
- group.refreshBooks(stories);
+ group.refreshBooks(stories, words);
}
for (LocalReaderGroup group : booksByAuthor.keySet()) {
List<MetaData> stories = Instance.getLibrary().getListByAuthor(
booksByAuthor.get(group));
- group.refreshBooks(stories);
+ group.refreshBooks(stories, words);
}
pane.repaint();
bar.add(edit);
+ JMenu view = new JMenu("View");
+ view.setMnemonic(KeyEvent.VK_V);
+ JMenuItem vauthors = new JMenuItem("Author");
+ vauthors.setMnemonic(KeyEvent.VK_A);
+ vauthors.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ words = false;
+ refreshBooks();
+ }
+ });
+ view.add(vauthors);
+ JMenuItem vwords = new JMenuItem("Word count");
+ vwords.setMnemonic(KeyEvent.VK_W);
+ vwords.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ words = true;
+ refreshBooks();
+ }
+ });
+ view.add(vwords);
+ bar.add(view);
+
JMenu sources = new JMenu("Sources");
sources.setMnemonic(KeyEvent.VK_S);
private List<MetaData> stories;
private List<LocalReaderBook> books;
private JPanel pane;
+ private boolean words; // words or authors (secondary info on books)
/**
* Create a new {@link LocalReaderGroup}.
*/
public void setActionListener(BookActionListener action) {
this.action = action;
- refreshBooks(stories);
+ refreshBooks(stories, words);
}
/**
*
* @param stories
* the stories
+ * @param seeWordcount
+ * TRUE to see word counts, FALSE to see authors
*/
- public void refreshBooks(List<MetaData> stories) {
+ public void refreshBooks(List<MetaData> stories, boolean seeWordcount) {
this.stories = stories;
+ this.words = seeWordcount;
books = new ArrayList<LocalReaderBook>();
invalidate();
if (stories != null) {
for (MetaData meta : stories) {
LocalReaderBook book = new LocalReaderBook(meta,
- reader.isCached(meta.getLuid()));
+ reader.isCached(meta.getLuid()), seeWordcount);
if (backgroundColor != null) {
book.setBackground(backgroundColor);
}
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Story story = new Story();
MetaData meta = getMeta(url, getInput());
+ if (meta.getCreationDate() == null
+ || meta.getCreationDate().isEmpty()) {
+ meta.setCreationDate(StringUtils.fromTime(new Date().getTime()));
+ }
story.setMeta(meta);
if (meta != null && meta.getCover() == null) {
Progress pgChaps = new Progress(0, chapters.size());
pg.addProgress(pgChaps, 80);
+ long words = 0;
for (Entry<String, URL> chap : chapters) {
setCurrentReferer(chap.getValue());
InputStream chapIn = Instance.getCache().open(
chap.getValue(), this, true);
try {
- story.getChapters().add(
- makeChapter(url, i, chap.getKey(),
- getChapterContent(url, chapIn, i)));
+ Chapter cc = makeChapter(url, i, chap.getKey(),
+ getChapterContent(url, chapIn, i));
+ words += cc.getWords();
+ story.getChapters().add(cc);
+ if (story.getMeta() != null) {
+ story.getMeta().setWords(words);
+ }
} finally {
chapIn.close();
}
Chapter chap = new Chapter(number, chapterName);
if (content != null) {
- chap.setParagraphs(makeParagraphs(source, content));
+ List<Paragraph> paras = makeParagraphs(source, content);
+ long words = 0;
+ for (Paragraph para : paras) {
+ words += para.getWords();
+ }
+ chap.setParagraphs(paras);
+ chap.setWords(words);
}
return chap;
if (!singleQ && !doubleQ) {
line = openDoubleQuote + line + closeDoubleQuote;
- newParas.add(new Paragraph(ParagraphType.QUOTE, line));
+ newParas.add(new Paragraph(ParagraphType.QUOTE, line, para
+ .getWords()));
} else {
char open = singleQ ? openQuote : openDoubleQuote;
char close = singleQ ? closeQuote : closeDoubleQuote;
if (posDot >= 0) {
String rest = line.substring(posDot + 1).trim();
line = line.substring(0, posDot + 1).trim();
- newParas.add(new Paragraph(ParagraphType.QUOTE, line));
+ long words = 1;
+ for (char car : line.toCharArray()) {
+ if (car == ' ') {
+ words++;
+ }
+ }
+ newParas.add(new Paragraph(ParagraphType.QUOTE, line, words));
if (!rest.isEmpty()) {
newParas.addAll(requotify(processPara(rest)));
}
boolean tentativeCloseQuote = false;
char prev = '\0';
int dashCount = 0;
+ long words = 1;
StringBuilder builder = new StringBuilder();
for (char car : line.toCharArray()) {
case '\t':
case '\n': // just in case
case '\r': // just in case
+ if (builder.length() > 0
+ && builder.charAt(builder.length() - 1) != ' ') {
+ words++;
+ }
builder.append(' ');
break;
type = ParagraphType.QUOTE;
}
- return new Paragraph(type, line);
+ return new Paragraph(type, line, words);
}
/**
meta.setImageDocument(getInfoTagBoolean(in, "IMAGES_DOCUMENT", false));
meta.setCover(BasicSupport.getImage(null, sourceInfoFile,
getInfoTag(in, "COVER")));
+ try {
+ meta.setWords(Long.parseLong(getInfoTag(in, "WORDCOUNT")));
+ } catch (NumberFormatException e) {
+ meta.setWords(0);
+ }
+ meta.setCreationDate(getInfoTag(in, "CREATION_DATE"));
if (meta.getCover() == null) {
meta.setCover(BasicSupport.getDefaultCover(meta.getSubject()));
}
@Override
- protected List<Paragraph> requotify(Paragraph para) {
+ public List<Paragraph> requotify(Paragraph para) {
List<Paragraph> paras = new ArrayList<Paragraph>(
1);
paras.add(para);
assertEquals(text, para.getContent());
}
});
+
+ addTest(new TestCase("BasicSupport.processPara() words count") {
+ @Override
+ public void test() throws Exception {
+ BasicSupportEmpty support = new BasicSupportEmpty() {
+ @Override
+ protected boolean isHtml() {
+ return true;
+ }
+ };
+
+ Paragraph para;
+
+ para = support.processPara("«Yes, my Lord!»");
+ assertEquals(3, para.getWords());
+
+ para = support.processPara("One, twee, trois.");
+ assertEquals(3, para.getWords());
+ }
+ });
+
+ addTest(new TestCase("BasicSupport.requotify() words count") {
+ @Override
+ public void test() throws Exception {
+ BasicSupportEmpty support = new BasicSupportEmpty();
+
+ char openDoubleQuote = Instance.getTrans().getChar(
+ StringId.OPEN_DOUBLE_QUOTE);
+ char closeDoubleQuote = Instance.getTrans().getChar(
+ StringId.CLOSE_DOUBLE_QUOTE);
+
+ String content = null;
+ Paragraph para = null;
+ List<Paragraph> paras = null;
+ long words = 0;
+
+ content = "One, twee, trois.";
+ para = new Paragraph(ParagraphType.NORMAL, content,
+ content.split(" ").length);
+ paras = support.requotify(para);
+ words = 0;
+ for (Paragraph p : paras) {
+ words += p.getWords();
+ }
+ assertEquals("Bad words count in a single paragraph",
+ para.getWords(), words);
+
+ content = "Such WoW! So Web2.0! With Colours!";
+ para = new Paragraph(ParagraphType.NORMAL, content,
+ content.split(" ").length);
+ paras = support.requotify(para);
+ words = 0;
+ for (Paragraph p : paras) {
+ words += p.getWords();
+ }
+ assertEquals("Bad words count in a single paragraph",
+ para.getWords(), words);
+
+ content = openDoubleQuote + "Such a good idea!"
+ + closeDoubleQuote
+ + ", she said. This ought to be a new para.";
+ para = new Paragraph(ParagraphType.QUOTE, content,
+ content.split(" ").length);
+ paras = support.requotify(para);
+ words = 0;
+ for (Paragraph p : paras) {
+ words += p.getWords();
+ }
+ assertEquals(
+ "Bad words count in a requotified paragraph",
+ para.getWords(), words);
+ }
+ });
}
});
public Paragraph processPara(String line) {
return super.processPara(line);
}
+
+ @Override
+ // and make it public!
+ public List<Paragraph> requotify(Paragraph para) {
+ return super.requotify(para);
+ }
}
}