Version 1.2.4: fixes, new "Re-download" UI option
authorNiki Roo <niki@nikiroo.be>
Sun, 26 Feb 2017 12:33:18 +0000 (13:33 +0100)
committerNiki Roo <niki@nikiroo.be>
Sun, 26 Feb 2017 12:33:18 +0000 (13:33 +0100)
- new UI option: Re-download
- fix UI: books are now sorted (will not jump around after
  refresh/redownload)
- fixes on quote character handling
- fixes on Chapter detection

12 files changed:
VERSION
changelog.md
src/be/nikiroo/fanfix/Instance.java
src/be/nikiroo/fanfix/Library.java
src/be/nikiroo/fanfix/data/MetaData.java
src/be/nikiroo/fanfix/reader/LocalReaderBook.java
src/be/nikiroo/fanfix/reader/LocalReaderFrame.java
src/be/nikiroo/fanfix/supported/BasicSupport.java
src/be/nikiroo/fanfix/supported/Fanfiction.java
src/be/nikiroo/fanfix/supported/Text.java
src/be/nikiroo/fanfix/test/BasicSupportTest.java
src/be/nikiroo/fanfix/test/Test.java

diff --git a/VERSION b/VERSION
index 0495c4a88caed0f036ffab0948c17d4b5fdc96c1..e8ea05db81420da0a67f51ada616e6aca2881745 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.3
+1.2.4
index a22be0eaa20351f6a817f5eb4b3b24d9d70dab31..02bbb87ef0d72a158d06348bf0c18b117660c2c1 100644 (file)
@@ -1,5 +1,39 @@
 # Fanfix
 
+## Version 1.2.4
+
+- new UI option: Re-download
+- fix UI: books are now sorted (will not jump around after refresh/redownload)
+- fixes on quote character handling
+- fixes on Chapter detection
+
+## Version 1.2.3
+
+- Include the original (info_text) files when saving to HTML
+- New input type supported: HTML files made by Fanfix
+
+## Version 1.2.2
+
+- New "Save as..." GUI option
+- GUI fixes (icon refresh)
+- Fix handling of TABs in user messages
+- LocalReader can now be used with --read
+- Some fixes in CSS
+
+## Version 1.2.1
+
+- Some GUI menu functions added
+- Right-click popup menu added
+- GUI fixes, especially for the LocalReader library
+- New green round icon to denote "cached" (into LocalReader library) files
+
+## Version 1.2.0
+
+- Progress reporting system in GUI, too
+- CSS style changes
+- unit tests added
+- Some GUI menu functions added (delete, refresh, a place-holder for export)
+
 ## Version 1.1.0
 
 - new Progress reporting system (currently only in CLI mode)
index aacf0c50860064a0f980077ad641ab36aa5d837d..33c65e003aeddc21a7f70e36b342b123a9e6f5ac 100644 (file)
@@ -30,11 +30,15 @@ public class Instance {
                // Most of the rest is dependent upon this:
                config = new ConfigBundle();
 
-               String configDir = System.getenv("CONFIG_DIR");
+               String configDir = System.getProperty("CONFIG_DIR");
+               if (configDir == null) {
+                       configDir = System.getenv("CONFIG_DIR");
+               }
                if (configDir == null) {
                        configDir = new File(System.getProperty("user.home"), ".fanfix")
                                        .getPath();
                }
+               
                if (configDir != null) {
                        if (!new File(configDir).exists()) {
                                new File(configDir).mkdirs();
index eb9c9a39f3d4ae76298fd636c0e3e6ad43a962cc..4db3868b5928701ecda2a53e6f522294aee00cc9 100644 (file)
@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -90,6 +91,7 @@ public class Library {
                        }
                }
 
+               Collections.sort(list);
                return list;
        }
 
index 55c18de7d0596d0ba43b925ae00c0773100806a8..153638c98d4eafaff1126fbcc6d2188d1f941b04 100644 (file)
@@ -9,7 +9,7 @@ import java.util.List;
  * 
  * @author niki
  */
-public class MetaData implements Cloneable {
+public class MetaData implements Cloneable, Comparable<MetaData> {
        private String title;
        private String author;
        private String date;
@@ -315,6 +315,11 @@ public class MetaData implements Cloneable {
                this.imageDocument = imageDocument;
        }
 
+       public int compareTo(MetaData o) {
+               String oUuid = o == null ? null : o.getUuid();
+               return getUuid().compareTo(oUuid);
+       }
+
        @Override
        public MetaData clone() {
                MetaData meta = null;
index d19b6034d67eeb60b435e002f853d5b973122ebc..9a4290de201912ca0e12f6b0357e1ced2cbc1176 100644 (file)
@@ -85,7 +85,7 @@ class LocalReaderBook extends JPanel {
        private Date lastClick;
 
        private List<BookActionListener> listeners;
-       private String luid;
+       private MetaData meta;
        private boolean cached;
 
        /**
@@ -98,7 +98,7 @@ class LocalReaderBook extends JPanel {
         */
        public LocalReaderBook(MetaData meta, boolean cached) {
                this.cached = cached;
-               luid = meta.getLuid();
+               this.meta = meta;
 
                String optAuthor = meta.getAuthor();
                if (optAuthor != null && !optAuthor.isEmpty()) {
@@ -229,12 +229,12 @@ class LocalReaderBook extends JPanel {
        }
 
        /**
-        * The Library UID of the book represented by this item.
+        * The Library {@code}link MetaData} of the book represented by this item.
         * 
-        * @return the LUID
+        * @return the meta
         */
-       public String getLuid() {
-               return luid;
+       public MetaData getMeta() {
+               return meta;
        }
 
        /**
index 7d5e5ddcca45ab18b0fc36cf87b8cf2e7f3fcdec..7ce16cc48a8bc487c9ccfa7e5c37479b850f010d 100644 (file)
@@ -136,7 +136,8 @@ class LocalReaderFrame extends JFrame {
                                        popup.add(createMenuItemOpenBook());
                                        popup.addSeparator();
                                        popup.add(createMenuItemExport());
-                                       popup.add(createMenuItemRefresh());
+                                       popup.add(createMenuItemClearCache());
+                                       popup.add(createMenuItemRedownload());
                                        popup.addSeparator();
                                        popup.add(createMenuItemDelete());
                                        popup.show(e.getComponent(), e.getX(), e.getY());
@@ -165,13 +166,13 @@ class LocalReaderFrame extends JFrame {
                JMenu file = new JMenu("File");
                file.setMnemonic(KeyEvent.VK_F);
 
-               JMenuItem imprt = new JMenuItem("Import URL", KeyEvent.VK_U);
+               JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U);
                imprt.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                imprt(true);
                        }
                });
-               JMenuItem imprtF = new JMenuItem("Import File", KeyEvent.VK_F);
+               JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
                imprtF.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                imprt(false);
@@ -198,7 +199,8 @@ class LocalReaderFrame extends JFrame {
                JMenu edit = new JMenu("Edit");
                edit.setMnemonic(KeyEvent.VK_E);
 
-               edit.add(createMenuItemRefresh());
+               edit.add(createMenuItemClearCache());
+               edit.add(createMenuItemRedownload());
                edit.addSeparator();
                edit.add(createMenuItemDelete());
 
@@ -274,7 +276,8 @@ class LocalReaderFrame extends JFrame {
                                                public void run() {
                                                        try {
                                                                Instance.getLibrary().export(
-                                                                               selectedBook.getLuid(), type, path, pg);
+                                                                               selectedBook.getMeta().getLuid(), type,
+                                                                               path, pg);
                                                        } catch (IOException e) {
                                                                Instance.syserr(e);
                                                        }
@@ -315,14 +318,14 @@ class LocalReaderFrame extends JFrame {
         * 
         * @return the item
         */
-       private JMenuItem createMenuItemRefresh() {
+       private JMenuItem createMenuItemClearCache() {
                JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C);
                refresh.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
                                        outOfUi(null, new Runnable() {
                                                public void run() {
-                                                       reader.refresh(selectedBook.getLuid());
+                                                       reader.refresh(selectedBook.getMeta().getLuid());
                                                        selectedBook.setCached(false);
                                                        SwingUtilities.invokeLater(new Runnable() {
                                                                public void run() {
@@ -338,6 +341,29 @@ class LocalReaderFrame extends JFrame {
                return refresh;
        }
 
+       /**
+        * Create the redownload (then delete original) menu item.
+        * 
+        * @return the item
+        */
+       private JMenuItem createMenuItemRedownload() {
+               JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R);
+               refresh.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               if (selectedBook != null) {
+                                       imprt(selectedBook.getMeta().getUrl(), new Runnable() {
+                                               public void run() {
+                                                       reader.delete(selectedBook.getMeta().getLuid());
+                                                       selectedBook = null;
+                                               }
+                                       });
+                               }
+                       }
+               });
+
+               return refresh;
+       }
+
        /**
         * Create the delete menu item.
         * 
@@ -350,7 +376,7 @@ class LocalReaderFrame extends JFrame {
                                if (selectedBook != null) {
                                        outOfUi(null, new Runnable() {
                                                public void run() {
-                                                       reader.delete(selectedBook.getLuid());
+                                                       reader.delete(selectedBook.getMeta().getLuid());
                                                        selectedBook = null;
                                                        SwingUtilities.invokeLater(new Runnable() {
                                                                public void run() {
@@ -395,7 +421,7 @@ class LocalReaderFrame extends JFrame {
                outOfUi(pg, new Runnable() {
                        public void run() {
                                try {
-                                       reader.open(book.getLuid(), pg);
+                                       reader.open(book.getMeta().getLuid(), pg);
                                        SwingUtilities.invokeLater(new Runnable() {
                                                public void run() {
                                                        book.setCached(true);
@@ -454,6 +480,8 @@ class LocalReaderFrame extends JFrame {
 
        /**
         * Import a {@link Story} into the main {@link Library}.
+        * <p>
+        * Should be called inside the UI thread.
         * 
         * @param askUrl
         *            TRUE for an {@link URL}, false for a {@link File}
@@ -461,7 +489,7 @@ class LocalReaderFrame extends JFrame {
        private void imprt(boolean askUrl) {
                JFileChooser fc = new JFileChooser();
 
-               final String url;
+               String url;
                if (askUrl) {
                        url = JOptionPane.showInputDialog(LocalReaderFrame.this,
                                        "url of the story to import?", "Importing from URL",
@@ -473,35 +501,54 @@ class LocalReaderFrame extends JFrame {
                }
 
                if (url != null && !url.isEmpty()) {
-                       final Progress pg = new Progress("Importing " + url);
-                       outOfUi(pg, new Runnable() {
-                               public void run() {
-                                       Exception ex = null;
-                                       try {
-                                               Instance.getLibrary()
-                                                               .imprt(BasicReader.getUrl(url), pg);
-                                       } catch (IOException e) {
-                                               ex = e;
-                                       }
+                       imprt(url, null);
+               }
+       }
 
-                                       final Exception e = ex;
+       /**
+        * Actually import the {@link Story} into the main {@link Library}.
+        * <p>
+        * Should be called inside the UI thread.
+        * 
+        * @param url
+        *            the {@link Story} to import by {@link URL}
+        * @param onSuccess
+        *            Action to execute on success
+        */
+       private void imprt(final String url, final Runnable onSuccess) {
+               final Progress pg = new Progress("Importing " + url);
+               outOfUi(pg, new Runnable() {
+                       public void run() {
+                               Exception ex = null;
+                               try {
+                                       Instance.getLibrary().imprt(BasicReader.getUrl(url), pg);
+                               } catch (IOException e) {
+                                       ex = e;
+                               }
 
-                                       final boolean ok = (e == null);
-                                       SwingUtilities.invokeLater(new Runnable() {
-                                               public void run() {
-                                                       if (!ok) {
-                                                               JOptionPane.showMessageDialog(
-                                                                               LocalReaderFrame.this, e.getMessage(),
-                                                                               "Cannot import: " + url,
-                                                                               JOptionPane.ERROR_MESSAGE);
-                                                       } else {
+                               final Exception e = ex;
+
+                               final boolean ok = (e == null);
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               if (!ok) {
+                                                       JOptionPane.showMessageDialog(
+                                                                       LocalReaderFrame.this, "Cannot import: "
+                                                                                       + url, e.getMessage(),
+                                                                       JOptionPane.ERROR_MESSAGE);
+
+                                                       setEnabled(true);
+                                               } else {
+                                                       refreshBooks(type);
+                                                       if (onSuccess != null) {
+                                                               onSuccess.run();
                                                                refreshBooks(type);
                                                        }
                                                }
-                                       });
-                               }
-                       });
-               }
+                                       }
+                               });
+                       }
+               });
        }
 
        /**
index 169cc5f02d7a0a85cc4328227bd646c408d5a8e1..2b4715ab0beec0af04f5d52a70f88f55a61193fd 100644 (file)
@@ -150,7 +150,7 @@ public abstract class BasicSupport {
 
        private InputStream in;
        private SupportType type;
-       private URL currentReferer; // with on 'r', as in 'HTTP'...
+       private URL currentReferer; // with only one 'r', as in 'HTTP'...
 
        // quote chars
        private char openQuote = Instance.getTrans().getChar(
@@ -913,7 +913,7 @@ public abstract class BasicSupport {
         * 
         * @return the processed {@link Paragraph}
         */
-       private Paragraph processPara(String line) {
+       protected Paragraph processPara(String line) {
                line = ifUnhtml(line).trim();
 
                boolean space = true;
@@ -936,11 +936,16 @@ public abstract class BasicSupport {
 
                        if (tentativeCloseQuote) {
                                tentativeCloseQuote = false;
-                               if ((car >= 'a' && car <= 'z') || (car >= 'A' && car <= 'Z')
-                                               || (car >= '0' && car <= '9')) {
+                               if (Character.isLetterOrDigit(car)) {
                                        builder.append("'");
                                } else {
-                                       builder.append(closeQuote);
+                                       // handle double-single quotes as double quotes
+                                       if (prev == car) {
+                                               builder.append(closeDoubleQuote);
+                                               continue;
+                                       } else {
+                                               builder.append(closeQuote);
+                                       }
                                }
                        }
 
@@ -956,9 +961,21 @@ public abstract class BasicSupport {
                        case '\'':
                                if (space || (brk && quote)) {
                                        quote = true;
-                                       builder.append(openQuote);
-                               } else if (prev == ' ') {
-                                       builder.append(openQuote);
+                                       // handle double-single quotes as double quotes
+                                       if (prev == car) {
+                                               builder.deleteCharAt(builder.length() - 1);
+                                               builder.append(openDoubleQuote);
+                                       } else {
+                                               builder.append(openQuote);
+                                       }
+                               } else if (prev == ' ' || prev == car) {
+                                       // handle double-single quotes as double quotes
+                                       if (prev == car) {
+                                               builder.deleteCharAt(builder.length() - 1);
+                                               builder.append(openDoubleQuote);
+                                       } else {
+                                               builder.append(openQuote);
+                                       }
                                } else {
                                        // it is a quote ("I'm off") or a 'quote' ("This
                                        // 'good' restaurant"...)
@@ -1011,7 +1028,13 @@ public abstract class BasicSupport {
                                        quote = true;
                                        builder.append(openQuote);
                                } else {
-                                       builder.append(openQuote);
+                                       // handle double-single quotes as double quotes
+                                       if (prev == car) {
+                                               builder.deleteCharAt(builder.length() - 1);
+                                               builder.append(openDoubleQuote);
+                                       } else {
+                                               builder.append(openQuote);
+                                       }
                                }
                                space = false;
                                brk = false;
@@ -1024,7 +1047,13 @@ public abstract class BasicSupport {
                        case '」':
                                space = false;
                                brk = false;
-                               builder.append(closeQuote);
+                               // handle double-single quotes as double quotes
+                               if (prev == car) {
+                                       builder.deleteCharAt(builder.length() - 1);
+                                       builder.append(closeDoubleQuote);
+                               } else {
+                                       builder.append(closeQuote);
+                               }
                                break;
 
                        case '«':
index 7d4285b33e86109650e397c1d58358d54a9633a9..a73f7dee497142ee3af31212ca04b5c1f52c2211 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Map.Entry;
 import java.util.Scanner;
 
 import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.bundles.Config;
 import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.utils.StringUtils;
 
@@ -309,7 +310,21 @@ class Fanfiction extends BasicSupport {
                                if (builder.length() == 0) {
                                        int pos = line.indexOf("<hr");
                                        if (pos >= 0) {
-                                               line = line.substring(pos);
+                                               boolean chaptered = false;
+                                               for (String lang : Instance.getConfig()
+                                                               .getString(Config.CHAPTER).split(",")) {
+                                                       String chapterWord = Instance.getConfig()
+                                                                       .getStringX(Config.CHAPTER, lang);
+                                                       int posChap = line.indexOf(chapterWord + " ");
+                                                       if (posChap < pos) {
+                                                               chaptered = true;
+                                                               break;
+                                                       }
+                                               }
+
+                                               if (chaptered) {
+                                                       line = line.substring(pos);
+                                               }
                                        }
                                }
 
index 2dd052470278886f30b715f541ca571d7967ae50..3b486ce8d0c4a36b8610a3bd10da9943e3dbfb6c 100644 (file)
@@ -87,7 +87,12 @@ class Text extends BasicSupport {
                        chapter0 = scan.next();
                }
 
-               String lang = detectChapter(chapter0);
+               String lang = detectChapter(chapter0, 0);
+               if (lang == null) {
+                       // No description??
+                       lang = detectChapter(chapter0, 1);
+               }
+
                if (lang == null) {
                        lang = "EN";
                } else {
@@ -170,35 +175,30 @@ class Text extends BasicSupport {
                @SuppressWarnings("resource")
                Scanner scan = new Scanner(in, "UTF-8");
                scan.useDelimiter("\\n");
-               boolean descSkipped = false;
                boolean prevLineEmpty = false;
                while (scan.hasNext()) {
                        String line = scan.next();
-                       if (prevLineEmpty && detectChapter(line) != null) {
-                               if (descSkipped) {
-                                       String chapName = Integer.toString(chaps.size());
-                                       int pos = line.indexOf(':');
-                                       if (pos >= 0 && pos + 1 < line.length()) {
-                                               chapName = line.substring(pos + 1).trim();
+                       if (prevLineEmpty && detectChapter(line, chaps.size() + 1) != null) {
+                               String chapName = Integer.toString(chaps.size() + 1);
+                               int pos = line.indexOf(':');
+                               if (pos >= 0 && pos + 1 < line.length()) {
+                                       chapName = line.substring(pos + 1).trim();
+                               }
+                               final URL value = source;
+                               final String key = chapName;
+                               chaps.add(new Entry<String, URL>() {
+                                       public URL setValue(URL value) {
+                                               return null;
                                        }
-                                       final URL value = source;
-                                       final String key = chapName;
-                                       chaps.add(new Entry<String, URL>() {
-                                               public URL setValue(URL value) {
-                                                       return null;
-                                               }
 
-                                               public URL getValue() {
-                                                       return value;
-                                               }
+                                       public URL getValue() {
+                                               return value;
+                                       }
 
-                                               public String getKey() {
-                                                       return key;
-                                               }
-                                       });
-                               } else {
-                                       descSkipped = true;
-                               }
+                                       public String getKey() {
+                                               return key;
+                                       }
+                               });
                        }
 
                        prevLineEmpty = line.trim().isEmpty();
@@ -219,7 +219,7 @@ class Text extends BasicSupport {
                        String line = scan.next();
                        if (detectChapter(line, number) != null) {
                                inChap = true;
-                       } else if (inChap && detectChapter(line) != null) {
+                       } else if (inChap && detectChapter(line, number + 1) != null) {
                                break;
                        } else if (inChap) {
                                builder.append(line);
@@ -248,19 +248,6 @@ class Text extends BasicSupport {
                return false;
        }
 
-       /**
-        * Check if the given line looks like a starting chapter in a supported
-        * language, and return the language if it does (or NULL if not).
-        * 
-        * @param line
-        *            the line to check
-        * 
-        * @return the language or NULL
-        */
-       private String detectChapter(String line) {
-               return detectChapter(line, null);
-       }
-
        /**
         * Check if the given line looks like the given starting chapter in a
         * supported language, and return the language if it does (or NULL if not).
@@ -270,7 +257,7 @@ class Text extends BasicSupport {
         * 
         * @return the language or NULL
         */
-       private String detectChapter(String line, Integer number) {
+       private String detectChapter(String line, int number) {
                line = line.toUpperCase();
                for (String lang : Instance.getConfig().getString(Config.CHAPTER)
                                .split(",")) {
@@ -279,19 +266,16 @@ class Text extends BasicSupport {
                        if (chapter != null && !chapter.isEmpty()) {
                                chapter = chapter.toUpperCase() + " ";
                                if (line.startsWith(chapter)) {
-                                       if (number != null) {
-                                               // We want "[CHAPTER] [number]: [name]", with ": [name]"
-                                               // optional
-                                               String test = line.substring(chapter.length()).trim();
-                                               if (test.startsWith(Integer.toString(number))) {
-                                                       test = test.substring(
-                                                                       Integer.toString(number).length()).trim();
-                                                       if (test.isEmpty() || test.startsWith(":")) {
-                                                               return lang;
-                                                       }
+                                       // We want "[CHAPTER] [number]: [name]", with ": [name]"
+                                       // optional
+                                       String test = line.substring(chapter.length()).trim();
+                                       if (test.startsWith(Integer.toString(number))) {
+                                               test = test
+                                                               .substring(Integer.toString(number).length())
+                                                               .trim();
+                                               if (test.isEmpty() || test.startsWith(":")) {
+                                                       return lang;
                                                }
-                                       } else {
-                                               return lang;
                                        }
                                }
                        }
index ed585d983fb2512f3494f3871c0ee4740f4c5132..d8565cba85e33c31cf3709e05d1c124af4293621 100644 (file)
@@ -1,5 +1,6 @@
 package be.nikiroo.fanfix.test;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -7,14 +8,28 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map.Entry;
 
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.bundles.StringId;
 import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.data.Paragraph;
 import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
+import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.fanfix.supported.BasicSupport;
+import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
+import be.nikiroo.utils.IOUtils;
 import be.nikiroo.utils.test.TestCase;
 import be.nikiroo.utils.test.TestLauncher;
 
 public class BasicSupportTest extends TestLauncher {
+       // quote chars
+       private char openQuote = Instance.getTrans().getChar(
+                       StringId.OPEN_SINGLE_QUOTE);
+       private char closeQuote = Instance.getTrans().getChar(
+                       StringId.CLOSE_SINGLE_QUOTE);
+       private char openDoubleQuote = Instance.getTrans().getChar(
+                       StringId.OPEN_DOUBLE_QUOTE);
+       private char closeDoubleQuote = Instance.getTrans().getChar(
+                       StringId.CLOSE_DOUBLE_QUOTE);
 
        public BasicSupportTest(String[] args) {
                super("BasicSupport", args);
@@ -113,6 +128,189 @@ public class BasicSupportTest extends TestLauncher {
                                                                .getType());
                                        }
                                });
+
+                               addTest(new TestCase("BasicSupport.processPara() quotes") {
+                                       @Override
+                                       public void test() throws Exception {
+                                               BasicSupportEmpty support = new BasicSupportEmpty() {
+                                                       @Override
+                                                       protected boolean isHtml() {
+                                                               return true;
+                                                       }
+                                               };
+
+                                               Paragraph para;
+
+                                               // sanity check
+                                               para = support.processPara("");
+                                               assertEquals(ParagraphType.BLANK, para.getType());
+                                               //
+
+                                               para = support.processPara("\"Yes, my Lord!\"");
+                                               assertEquals(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openDoubleQuote + "Yes, my Lord!"
+                                                               + closeDoubleQuote, para.getContent());
+
+                                               para = support.processPara("«Yes, my Lord!»");
+                                               assertEquals(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openDoubleQuote + "Yes, my Lord!"
+                                                               + closeDoubleQuote, para.getContent());
+
+                                               para = support.processPara("'Yes, my Lord!'");
+                                               assertEquals(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openQuote + "Yes, my Lord!" + closeQuote,
+                                                               para.getContent());
+
+                                               para = support.processPara("‹Yes, my Lord!›");
+                                               assertEquals(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openQuote + "Yes, my Lord!" + closeQuote,
+                                                               para.getContent());
+                                       }
+                               });
+
+                               addTest(new TestCase(
+                                               "BasicSupport.processPara() double-simple quotes") {
+                                       @Override
+                                       public void setUp() throws Exception {
+                                               super.setUp();
+
+                                       };
+
+                                       @Override
+                                       public void tearDown() throws Exception {
+
+                                               super.tearDown();
+                                       }
+
+                                       @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(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openDoubleQuote + "Yes, my Lord!"
+                                                               + closeDoubleQuote, para.getContent());
+
+                                               para = support.processPara("‹‹Yes, my Lord!››");
+                                               assertEquals(ParagraphType.QUOTE, para.getType());
+                                               assertEquals(openDoubleQuote + "Yes, my Lord!"
+                                                               + closeDoubleQuote, para.getContent());
+                                       }
+                               });
+
+                               addTest(new TestCase("BasicSupport.processPara() apostrophe") {
+                                       @Override
+                                       public void test() throws Exception {
+                                               BasicSupportEmpty support = new BasicSupportEmpty() {
+                                                       @Override
+                                                       protected boolean isHtml() {
+                                                               return true;
+                                                       }
+                                               };
+
+                                               Paragraph para;
+
+                                               String text = "Nous étions en été, mais cela aurait être l'hiver quand nous n'étions encore qu'à Aubeuge";
+                                               para = support.processPara(text);
+                                               assertEquals(ParagraphType.NORMAL, para.getType());
+                                               assertEquals(text, para.getContent());
+                                       }
+                               });
+                       }
+               });
+
+               addSeries(new TestLauncher("Text", args) {
+                       {
+                               addTest(new TestCase("Chapter detection simple") {
+                                       private File tmp;
+
+                                       @Override
+                                       public void setUp() throws Exception {
+                                               tmp = File.createTempFile("fanfix-text-file_", ".test");
+                                               IOUtils.writeSmallFile(tmp.getParentFile(),
+                                                               tmp.getName(), "TITLE"
+                                                                               + "\n"//
+                                                                               + "By nona"
+                                                                               + "\n" //
+                                                                               + "\n" //
+                                                                               + "Chapter 0: Resumé" + "\n" + "\n"
+                                                                               + "'sume." + "\n" + "\n"
+                                                                               + "Chapter 1: chap1" + "\n" + "\n"
+                                                                               + "Fanfan." + "\n" + "\n"
+                                                                               + "Chapter 2: Chap2" + "\n" + "\n" //
+                                                                               + "Tulipe." + "\n");
+                                       };
+
+                                       @Override
+                                       public void tearDown() throws Exception {
+                                               tmp.delete();
+                                       };
+
+                                       @Override
+                                       public void test() throws Exception {
+                                               BasicSupport support = BasicSupport
+                                                               .getSupport(SupportType.TEXT);
+
+                                               Story story = support
+                                                               .process(tmp.toURI().toURL(), null);
+
+                                               assertEquals(2, story.getChapters().size());
+                                               assertEquals(1, story.getChapters().get(1)
+                                                               .getParagraphs().size());
+                                               assertEquals("Tulipe.", story.getChapters().get(1)
+                                                               .getParagraphs().get(0).getContent());
+                                       }
+                               });
+
+                               addTest(new TestCase("Chapter detection with String 'Chapter'") {
+                                       private File tmp;
+
+                                       @Override
+                                       public void setUp() throws Exception {
+                                               tmp = File.createTempFile("fanfix-text-file_", ".test");
+                                               IOUtils.writeSmallFile(tmp.getParentFile(),
+                                                               tmp.getName(), "TITLE"
+                                                                               + "\n"//
+                                                                               + "By nona"
+                                                                               + "\n" //
+                                                                               + "\n" //
+                                                                               + "Chapter 0: Resumé" + "\n" + "\n"
+                                                                               + "'sume." + "\n" + "\n"
+                                                                               + "Chapter 1: chap1" + "\n" + "\n"
+                                                                               + "Chapter fout-la-merde" + "\n"
+                                                                               + "\n"//
+                                                                               + "Fanfan." + "\n" + "\n"
+                                                                               + "Chapter 2: Chap2" + "\n" + "\n" //
+                                                                               + "Tulipe." + "\n");
+                                       };
+
+                                       @Override
+                                       public void tearDown() throws Exception {
+                                               tmp.delete();
+                                       };
+
+                                       @Override
+                                       public void test() throws Exception {
+                                               BasicSupport support = BasicSupport
+                                                               .getSupport(SupportType.TEXT);
+
+                                               Story story = support
+                                                               .process(tmp.toURI().toURL(), null);
+
+                                               assertEquals(2, story.getChapters().size());
+                                               assertEquals(1, story.getChapters().get(1)
+                                                               .getParagraphs().size());
+                                               assertEquals("Tulipe.", story.getChapters().get(1)
+                                                               .getParagraphs().get(0).getContent());
+                                       }
+                               });
                        }
                });
        }
@@ -168,5 +366,11 @@ public class BasicSupportTest extends TestLauncher {
                public void fixBlanksBreaks(List<Paragraph> paras) {
                        super.fixBlanksBreaks(paras);
                }
+
+               @Override
+               // and make it public!
+               public Paragraph processPara(String line) {
+                       return super.processPara(line);
+               }
        }
 }
index 36ef701573bf9d1be5411b4216b977bbf50f307a..ed70d1557b4e7103bc539396c9a8494bb17a2058 100644 (file)
@@ -1,5 +1,13 @@
 package be.nikiroo.fanfix.test;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import be.nikiroo.fanfix.bundles.ConfigBundle;
+import be.nikiroo.utils.IOUtils;
+import be.nikiroo.utils.resources.Bundles;
 import be.nikiroo.utils.test.TestLauncher;
 
 /**
@@ -18,8 +26,34 @@ public class Test extends TestLauncher {
         * 
         * @param args
         *            the arguments passed to the {@link TestLauncher}s.
+        * @throws IOException
         */
-       static public void main(String[] args) {
-               System.exit(new Test(args).launch());
+       static public void main(String[] args) throws IOException {
+               File tmpConfig = File.createTempFile("fanfix-config_", ".test");
+               File tmpCache = File.createTempFile("fanfix-cache_", ".test");
+               tmpConfig.delete();
+               tmpConfig.mkdir();
+               tmpCache.delete();
+               tmpCache.mkdir();
+
+               FileOutputStream out = new FileOutputStream(new File(tmpConfig,
+                               "config.properties"));
+               Properties props = new Properties();
+               props.setProperty("CACHE_DIR", tmpCache.getAbsolutePath());
+               props.store(out, null);
+               out.close();
+
+               ConfigBundle config = new ConfigBundle();
+               Bundles.setDirectory(tmpConfig.getAbsolutePath());
+               config.updateFile(tmpConfig.getPath());
+
+               System.setProperty("CONFIG_DIR", tmpConfig.getAbsolutePath());
+
+               int result = new Test(args).launch();
+
+               IOUtils.deltree(tmpConfig);
+               IOUtils.deltree(tmpCache);
+
+               System.exit(result);
        }
 }