Wordcount (including UI), date of creation
authorNiki Roo <niki@nikiroo.be>
Sat, 4 Mar 2017 10:56:36 +0000 (11:56 +0100)
committerNiki Roo <niki@nikiroo.be>
Sat, 4 Mar 2017 10:56:36 +0000 (11:56 +0100)
- Remember the date of creation (.info)
- Remember the word count (.info)
- UI: show either author or word count below the book title
- Note: those new informations requires a redownload

changelog.md
src/be/nikiroo/fanfix/data/Chapter.java
src/be/nikiroo/fanfix/data/MetaData.java
src/be/nikiroo/fanfix/data/Paragraph.java
src/be/nikiroo/fanfix/output/InfoCover.java
src/be/nikiroo/fanfix/reader/LocalReaderBook.java
src/be/nikiroo/fanfix/reader/LocalReaderFrame.java
src/be/nikiroo/fanfix/reader/LocalReaderGroup.java
src/be/nikiroo/fanfix/supported/BasicSupport.java
src/be/nikiroo/fanfix/supported/InfoReader.java
src/be/nikiroo/fanfix/test/BasicSupportTest.java

index edecf23f6705e0d4c3e78e34d0cff4a6e3c0c0bb..70b64c737562bbdb1ba36ea854b80171b72489a3 100644 (file)
@@ -1,5 +1,10 @@
 # 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
index 551606348843446c00ddc59fc53827e7a3e35aa9..839d67b7c0ad2a079cbbc28ddf7e16019031b85f 100644 (file)
@@ -14,6 +14,7 @@ public class Chapter implements Iterable<Paragraph> {
        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.
@@ -92,6 +93,25 @@ public class Chapter implements Iterable<Paragraph> {
                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.
         */
index 153638c98d4eafaff1126fbcc6d2188d1f941b04..06cf625d3a66e59056d124e0172ab14c7f482e0a 100644 (file)
@@ -25,6 +25,8 @@ public class MetaData implements Cloneable, Comparable<MetaData> {
        private String publisher;
        private String type;
        private boolean imageDocument;
+       private long words;
+       private String creationDate;
 
        /**
         * The title of the story.
@@ -315,6 +317,44 @@ public class MetaData implements Cloneable, Comparable<MetaData> {
                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);
index feb949caf91c87d79e2471f6eed2dc6aceef8fa3..e409c286ae2949be6242c772b6fb7a1783e6a51f 100644 (file)
@@ -28,19 +28,7 @@ public class Paragraph {
 
        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.
@@ -52,8 +40,23 @@ public class Paragraph {
         *            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;
        }
 
        /**
@@ -94,6 +97,25 @@ public class Paragraph {
                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.
         */
index 1a63dce664d465f4e982fb331861fd574ad695ac..7db2a1cf5f5f9d2c5aba9b0bfb8696d11190d9f2 100644 (file)
@@ -54,6 +54,9 @@ class InfoCover {
                                }
                                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();
                        }
index 9a4290de201912ca0e12f6b0357e1ced2cbc1176..8c470785dbf72f99d742125a5195492ace180dc7 100644 (file)
@@ -95,14 +95,26 @@ class LocalReaderBook extends JPanel {
         *            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()));
@@ -114,7 +126,7 @@ class LocalReaderBook extends JPanel {
                                                                + "%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);
index d50669db377e9ee7dc20247cc9663c2c40279f88..a90360eed234bde1c99349844d687b02832bf70a 100644 (file)
@@ -61,6 +61,7 @@ class LocalReaderFrame extends JFrame {
        private ProgressBar pgBar;
        private JMenuBar bar;
        private LocalReaderBook selectedBook;
+       private boolean words; // words or authors (secondary info on books)
 
        /**
         * Create a new {@link LocalReaderFrame}.
@@ -191,13 +192,13 @@ class LocalReaderFrame extends JFrame {
                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();
@@ -255,6 +256,28 @@ class LocalReaderFrame extends JFrame {
 
                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);
 
index 991aaee713eb357666bead680a20969621af21ce..8667ffd4ac73f995c2f2b100038c9d843d71a243 100644 (file)
@@ -27,6 +27,7 @@ public class LocalReaderGroup extends JPanel {
        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}.
@@ -78,7 +79,7 @@ public class LocalReaderGroup extends JPanel {
         */
        public void setActionListener(BookActionListener action) {
                this.action = action;
-               refreshBooks(stories);
+               refreshBooks(stories, words);
        }
 
        /**
@@ -86,9 +87,12 @@ public class LocalReaderGroup extends JPanel {
         * 
         * @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();
@@ -98,7 +102,7 @@ public class LocalReaderGroup extends JPanel {
                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);
                                }
index b528cac819acea887acfc28f861b9c32221d1971..f255d5ed1c30eefb6767d5795c66dcca23baa39c 100644 (file)
@@ -10,6 +10,7 @@ import java.io.InputStreamReader;
 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;
@@ -333,6 +334,10 @@ public abstract class BasicSupport {
 
                        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) {
@@ -408,14 +413,19 @@ public abstract class BasicSupport {
                                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();
                                        }
@@ -554,7 +564,13 @@ public abstract class BasicSupport {
                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;
@@ -929,7 +945,8 @@ public abstract class BasicSupport {
 
                        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;
@@ -952,7 +969,13 @@ public abstract class BasicSupport {
                                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)));
                                        }
@@ -986,6 +1009,7 @@ public abstract class BasicSupport {
                boolean tentativeCloseQuote = false;
                char prev = '\0';
                int dashCount = 0;
+               long words = 1;
 
                StringBuilder builder = new StringBuilder();
                for (char car : line.toCharArray()) {
@@ -1019,6 +1043,10 @@ public abstract class BasicSupport {
                        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;
 
@@ -1171,7 +1199,7 @@ public abstract class BasicSupport {
                        type = ParagraphType.QUOTE;
                }
 
-               return new Paragraph(type, line);
+               return new Paragraph(type, line, words);
        }
 
        /**
index c23024059dcae1b4dc8fe6b20b3f9a982f2212ae..9372eddfebb44c85a336c97fef578bbf972b0e5f 100644 (file)
@@ -54,6 +54,12 @@ public class InfoReader {
                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()));
index d8565cba85e33c31cf3709e05d1c124af4293621..f989663e24b0ae142f03448c1dc7d592b7204ba6 100644 (file)
@@ -50,7 +50,7 @@ public class BasicSupportTest extends TestLauncher {
                                                        }
 
                                                        @Override
-                                                       protected List<Paragraph> requotify(Paragraph para) {
+                                                       public List<Paragraph> requotify(Paragraph para) {
                                                                List<Paragraph> paras = new ArrayList<Paragraph>(
                                                                                1);
                                                                paras.add(para);
@@ -223,6 +223,79 @@ public class BasicSupportTest extends TestLauncher {
                                                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);
+                                       }
+                               });
                        }
                });
 
@@ -372,5 +445,11 @@ public class BasicSupportTest extends TestLauncher {
                public Paragraph processPara(String line) {
                        return super.processPara(line);
                }
+
+               @Override
+               // and make it public!
+               public List<Paragraph> requotify(Paragraph para) {
+                       return super.requotify(para);
+               }
        }
 }