CLI search, step 1
authorNiki Roo <niki@nikiroo.be>
Thu, 11 Apr 2019 17:55:52 +0000 (19:55 +0200)
committerNiki Roo <niki@nikiroo.be>
Thu, 11 Apr 2019 17:55:52 +0000 (19:55 +0200)
src/be/nikiroo/fanfix/Main.java
src/be/nikiroo/fanfix/bundles/resources_core.properties
src/be/nikiroo/fanfix/bundles/resources_core_fr.properties
src/be/nikiroo/fanfix/reader/BasicReader.java
src/be/nikiroo/fanfix/reader/Reader.java
src/be/nikiroo/fanfix/reader/cli/CliReader.java
src/be/nikiroo/fanfix/reader/tui/TuiReader.java
src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java
src/be/nikiroo/fanfix/reader/ui/GuiReader.java
src/be/nikiroo/fanfix/searchable/BasicSearchable.java
src/be/nikiroo/fanfix/searchable/Fanfiction.java

index 953bc45a50b776a99b533fab67bbd9dc5dafa7d9..bbb335e97afb9395b8cabb7196e5f1845ca0fcf7 100644 (file)
@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.List;
 
 import be.nikiroo.fanfix.bundles.StringId;
@@ -20,6 +21,7 @@ import be.nikiroo.fanfix.output.BasicOutput.OutputType;
 import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.reader.Reader;
 import be.nikiroo.fanfix.reader.Reader.ReaderType;
+import be.nikiroo.fanfix.searchable.BasicSearchable;
 import be.nikiroo.fanfix.supported.BasicSupport;
 import be.nikiroo.fanfix.supported.SupportType;
 import be.nikiroo.utils.Progress;
@@ -33,7 +35,7 @@ import be.nikiroo.utils.serial.server.ServerObject;
  */
 public class Main {
        private enum MainAction {
-               IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, STOP_SERVER, REMOTE, SET_SOURCE, SET_TITLE, SET_AUTHOR
+               IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, STOP_SERVER, REMOTE, SET_SOURCE, SET_TITLE, SET_AUTHOR, SEARCH, SEARCH_TAG
        }
 
        /**
@@ -59,6 +61,11 @@ public class Main {
         * </li>
         * <li>--read-url [URL] ([chapter number]): convert on the fly and read the
         * story, without saving it</li>
+        * <li>--search WEBSITE [free text] ([page] ([item])): search for the given terms, 
+        * show the given page (page 0 means "how many page do we have", starts at page 1)</li>
+        * <li>--search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known 
+        * tags or search the stories for the given tag(s), show the given page of results
+        * (page 0 means "how many page do we have", starts at page 1)</li>
         * <li>--list ([type]): list the stories present in the library</li>
         * <li>--set-source [id] [new source]: change the source of the given story</li>
         * <li>--set-title [id] [new title]: change the title of the given story</li>
@@ -88,6 +95,11 @@ public class Main {
                Boolean plusInfo = null;
                String host = null;
                Integer port = null;
+               SupportType searchOn = null;
+               String search = null;
+               List<String> tags = new ArrayList<String>();
+               Integer page = null;
+               Integer item = null;
 
                boolean noMoreActions = false;
 
@@ -200,6 +212,64 @@ public class Main {
                                        exitCode = 255;
                                }
                                break;
+                       case SEARCH:
+                               if (searchOn == null) {
+                                       searchOn = SupportType.valueOfAllOkUC(args[i]);
+                                       
+                                       if (searchOn == null) {
+                                               Instance.getTraceHandler().error(
+                                                               "Website not known: <" + args[i] + ">");
+                                               exitCode = 255;
+                                       }
+                                       
+                                       if (BasicSearchable.getSearchable(searchOn) == null) {
+                                               Instance.getTraceHandler().error(
+                                                               "Website not supported: " + searchOn);
+                                               exitCode = 255;
+                                       }
+                               } else if (search == null) {
+                                       search = args[i];
+                               } else if (page == null) {
+                                       try {
+                                               page = Integer.parseInt(args[i]);
+                                       } catch (NumberFormatException e) {
+                                               Instance.getTraceHandler().error(
+                                                               new Exception("Incorrect page number: <"
+                                                                               + args[i] + ">", e));
+                                               exitCode = 255;
+                                       }
+                               } else if (item == null) {
+                                       try {
+                                               item = Integer.parseInt(args[i]);
+                                       } catch (NumberFormatException e) {
+                                               Instance.getTraceHandler().error(
+                                                               new Exception("Incorrect item number: <"
+                                                                               + args[i] + ">", e));
+                                               exitCode = 255;
+                                       }
+                               } else {
+                                       exitCode = 255;
+                               }
+                               break;
+                       case SEARCH_TAG:
+                               if (searchOn == null) {
+                                       searchOn = SupportType.valueOfAllOkUC(args[i]);
+                                       
+                                       if (searchOn == null) {
+                                               Instance.getTraceHandler().error(
+                                                               "Website not known: <" + args[i] + ">");
+                                               exitCode = 255;
+                                       }
+                                       
+                                       if (BasicSearchable.getSearchable(searchOn) == null) {
+                                               Instance.getTraceHandler().error(
+                                                               "Website not supported: " + searchOn);
+                                               exitCode = 255;
+                                       }
+                               } else {
+                                       tags.add(args[i]);
+                               }
+                               break;
                        case HELP:
                                exitCode = 255;
                                break;
@@ -282,8 +352,8 @@ public class Main {
                                System.err.println("\tVersion " + v);
                                System.err.println("\t-------------");
                                System.err.println("");
-                               for (String item : updates.getChanges().get(v)) {
-                                       System.err.println("\t- " + item);
+                               for (String it : updates.getChanges().get(v)) {
+                                       System.err.println("\t- " + it);
                                }
                                System.err.println("");
                        }
@@ -357,6 +427,73 @@ public class Main {
                                        break;
                                }
                                exitCode = read(urlString, chapString, false);
+                               break;
+                       case SEARCH:
+                               if (searchOn == null || search == null) {
+                                       exitCode = 255;
+                                       break;
+                               }
+                               
+                               if (page == null) {
+                                       page = 1;
+                               }
+                               if (item == null) {
+                                       item = 0;
+                               }
+                               
+                               if (BasicReader.getReader() == null) {
+                                       Instance.getTraceHandler()
+                                                       .error(new Exception(
+                                                                       "No reader type has been configured"));
+                                       exitCode = 10;
+                                       break;
+                               }
+                               
+                               try {
+                                       BasicReader.getReader().search(searchOn, search, page, item);
+                               } catch (IOException e1) {
+                                       Instance.getTraceHandler().error(e1);
+                               }
+                               
+                               break;
+                       case SEARCH_TAG:
+                               if (searchOn == null) {
+                                       exitCode = 255;
+                                       break;
+                               }
+                               
+                               item = 0;
+                               page = 1;
+
+                               try {
+                                       item = Integer.parseInt(tags.get(tags.size()-1));
+                                       tags.remove(tags.size() - 1);
+                                       
+                                       try {
+                                               int tmp = Integer.parseInt(tags.get(tags.size()-1));
+                                               tags.remove(tags.size() - 1);
+                                               
+                                               page = item;
+                                               item = tmp;
+                                       } catch (Exception e) {
+                                       }
+                               } catch (Exception e) {
+                               }
+                               
+                               if (BasicReader.getReader() == null) {
+                                       Instance.getTraceHandler()
+                                                       .error(new Exception(
+                                                                       "No reader type has been configured"));
+                                       exitCode = 10;
+                                       break;
+                               }
+                               
+                               try {
+                                       BasicReader.getReader().searchTag(searchOn, page, item, tags.toArray(new String[]{}));
+                               } catch (IOException e1) {
+                                       Instance.getTraceHandler().error(e1);
+                               }
+                               
                                break;
                        case HELP:
                                syntax(true);
index 62fd158ecddf0288ddc1b7e684b0aa420e4665ce..6a08973286267c44d6a6db68b817c53aa631feb7 100644 (file)
@@ -16,6 +16,11 @@ HELP_SYNTAX = Valid options:\n\
 \t--read [id] ([chapter number]): read the given story from the library\n\
 \t--read-url [URL] ([chapter number]): convert on the fly and read the \n\
 \t\tstory, without saving it\n\
+\t--search WEBSITE [free text] ([page] ([item])): search for the given terms, show the\n\
+\t\tgiven page (page 0 means "how many page do we have", starts at page 1)\n\
+\t--search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known tags or \n\
+\t\tsearch the stories for the given tag(s), show the given page of results\n\
+\t\t(page 0 means "how many page do we have", starts at page 1)\n\
 \t--list ([type]) : list the stories present in the library\n\
 \t--set-source [id] [new source]: change the source of the given story\n\
 \t--set-title [id] [new title]: change the title of the given story\n\
index 092bd336248e211b2c4d573b0136597355c5a026..149391e7320648574c2ce77847df1481fba242ee 100644 (file)
@@ -15,6 +15,11 @@ HELP_SYNTAX = Options reconnues :\n\
 \t--convert [URL] [output_type] [target] (+info): convertir l'histoire vers le fichier donné, et forcer l'ajout d'un fichier .info si +info est utilisé\n\
 \t--read [id] ([chapter number]): afficher l'histoire "id"\n\
 \t--read-url [URL] ([chapter number]): convertir l'histoire et la lire Ã  la volée, sans la sauver\n\
+\t--search WEBSITE [texte libre] ([page] ([item])): cherche des histoires, affiche la\n\
+\t\tpage de résultats demandée (la page 0 affiche le nombre de pages, la première page est la page 1)\n\
+\t--search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known tags or \n\
+\t\tcherche des histoires avec le/les tag(s) demandé(s), affiche la page de résultats demandée\n\
+\t\t(la page 0 affiche le nombre de pages, la première page est la page 1)\n\
 \t--list ([type]): lister les histoires presentes dans la librairie et leurs IDs\n\
 \t--set-source [id] [nouvelle source]: change la source de l'histoire\n\
 \t--set-title [id] [nouveau titre]: change le titre de l'histoire\n\
index c2a650cf73af2d809d3c609f3bf957c246dbc312..c749a59cdc6e1c1149282a78c6b777ef1f680a2b 100644 (file)
@@ -351,7 +351,13 @@ public abstract class BasicReader implements Reader {
                }
        }
 
+       /**
+        * @deprecated use StringUtils when updated
+        */
+       @Deprecated
        static private String format(long value) {
+               //TODO: use StringUtils
+               
                String display = "";
                String suffix = "";
 
index b001e304842f4956095050160d5bf65b8795d3f3..59ed024f42380a8a50f2b3e5fa199146d9092459 100644 (file)
@@ -6,6 +6,7 @@ import java.net.URL;
 import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.fanfix.library.BasicLibrary;
+import be.nikiroo.fanfix.supported.SupportType;
 import be.nikiroo.utils.Progress;
 
 /**
@@ -169,6 +170,58 @@ public interface Reader {
         */
        public void browse(String source);
 
+       /**
+        * Search for the given terms and find stories that correspond if possible.
+        * 
+        * @param searchOn
+        *            the website to search on
+        * @param keywords
+        *            the words to search for (cannot be NULL)
+        * @param page
+        *            the page of results to show (0 = request the maximum number of
+        *            pages, pages start at 1)
+        * @param item
+        *            the item to select (0 = do not select a specific item but show
+        *            all the page, items start at 1)
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       public void search(SupportType searchOn, String keywords, int page, int item)
+                       throws IOException;
+
+       /**
+        * Search based upon a hierarchy of tags, or search for (sub)tags.
+        * <p>
+        * We use the tags <tt>DisplayName</tt>.
+        * <p>
+        * If no tag is given, the main tags will be shown.
+        * <p>
+        * If a non-leaf tag is given, the subtags will be shown.
+        * <p>
+        * If a leaf tag is given (or a full hierarchy ending with a leaf tag),
+        * stories will be shown.
+        * <p>
+        * You can select the story you want with the <tt>item</tt> number.
+        * 
+        * @param searchOn
+        *            the website to search on
+        * @param page
+        *            the page of results to show (0 = request the maximum number of
+        *            pages, pages start at 1)
+        * @param item
+        *            the item to select (0 = do not select a specific item but show
+        *            all the page, items start at 1)
+        * @param tags
+        *            the tags display names to search for (this is a tag
+        *            <b>hierarchy</b>, <b>NOT</b> a multiple tags choice)
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       public void searchTag(SupportType searchOn, int page, int item,
+                       String... tags) throws IOException;
+
        /**
         * Open the {@link Story} with an external reader (the program should be
         * passed the main file associated with this {@link Story}).
index 9ec37a583d983cee46333f32f793ea1dc04dc3b1..7a4abfb28c91490f92dd50140c10eb9e1404e134 100644 (file)
@@ -1,6 +1,7 @@
 package be.nikiroo.fanfix.reader.cli;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 import be.nikiroo.fanfix.Instance;
@@ -10,6 +11,9 @@ import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.data.Paragraph;
 import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.fanfix.reader.BasicReader;
+import be.nikiroo.fanfix.searchable.BasicSearchable;
+import be.nikiroo.fanfix.searchable.SearchableTag;
+import be.nikiroo.fanfix.supported.SupportType;
 
 /**
  * Command line {@link Story} reader.
@@ -95,4 +99,100 @@ class CliReader extends BasicReader {
                                        + author);
                }
        }
+       
+       @Override
+       public void search(SupportType searchOn, String keywords, int page, int item) throws IOException {
+               
+       }
+       
+       @Override
+       public void searchTag(SupportType searchOn, int page, int item, String... tags) throws IOException {
+               BasicSearchable search = BasicSearchable.getSearchable(searchOn);
+               List<SearchableTag> stags = search.getTags();
+               
+               
+               SearchableTag stag = null;
+               for (String tag : tags) {
+                       stag = null;
+                       for (int i = 0 ; i < stags.size() ; i++) {
+                               if (stags.get(i).getName().equalsIgnoreCase(tag)) {
+                                       stag = stags.get(i);
+                                       break;
+                               }
+                       }
+                       
+                       if (stag != null) {
+                               search.fillTag(stag);
+                               stags = stag.getChildren();
+                       } else {
+                               stags = new ArrayList<SearchableTag>();
+                               break;
+                       }
+               }
+
+               if (stag != null) {
+                       if (page <= 0) {
+                               if (stag.isLeaf()) {
+                                       System.out.println(stag.getPages());
+                               } else {
+                                       System.out.println(stag.getCount());
+                               }
+                       } else {
+                               List<MetaData> metas = null;
+                               List<SearchableTag> subtags = null;
+                               int count;
+                               
+                               if (stag.isLeaf()) {
+                                       metas = search.search(stag, page);
+                                       count = metas.size();
+                               } else {
+                                       subtags = stag.getChildren();
+                                       count = subtags.size();
+                               }
+                               
+                               if (item > 0) {
+                                       if (item <= count) {
+                                               if (metas != null) {
+                                                       MetaData meta = metas.get(item - 1);
+                                                       System.out.println(item + ": " + meta.getTitle());
+                                                       System.out.println(meta.getUrl());
+                                                       System.out.println();
+                                                       System.out.println("Tags: " + meta.getTags());
+                                                       System.out.println();
+                                                       for (Paragraph para : meta.getResume()) {
+                                                               System.out.println(para.getContent());
+                                                               System.out.println("");
+                                                       }
+                                               } else {
+                                                       SearchableTag subtag = subtags.get(item - 1);
+                                                       // TODO: display fixed info, not debug
+                                                       System.out.println(subtag);
+                                               }
+                                       } else {
+                                               System.out.println("Invalid item: only " + count + " items found");
+                                       }
+                               } else {
+                                       if (metas != null) {
+                                               int i = 0;
+                                               for (MetaData meta : metas) {
+                                                       System.out.println((i + 1) + ": " + meta.getTitle());
+                                                       i++;
+                                               }
+                                       } else {
+                                               for (SearchableTag subtag : subtags) {
+                                                       if (subtag.getCount() > 0) {
+                                                               System.out.println(subtag.getName() + " (" + subtag.getCount() + ")");
+                                                       } else {
+                                                               System.out.println(subtag.getName());
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               } else {
+                       for (SearchableTag s : stags) {
+                               System.out.println(s.getName());
+                       }
+               }
+       }
 }
index f94f7838c2552e41d7061cd59e3c5bb3328ca7a9..cc39dcde77e7173229c5cd597942e36721e6462b 100644 (file)
@@ -7,6 +7,7 @@ import jexer.TApplication.BackendType;
 import be.nikiroo.fanfix.Instance;
 import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.reader.Reader;
+import be.nikiroo.fanfix.supported.SupportType;
 
 /**
  * This {@link Reader}is based upon the TUI widget library 'jexer'
@@ -70,4 +71,16 @@ class TuiReader extends BasicReader {
                        Instance.getTraceHandler().error(e);
                }
        }
+       
+       @Override
+       public void search(SupportType searchOn, String keywords, int page, int item) {
+               // TODO: !!!
+               throw new java.lang.IllegalStateException("Not implemented yet.");      
+       }
+       
+       @Override
+       public void searchTag(SupportType searchOn, int page, int item, String... tags) {
+               // TODO: !!!
+               throw new java.lang.IllegalStateException("Not implemented yet.");
+       }
 }
index f08b84ccbbf17328283b059fae0ec960fe29cee1..177c12ab867094cbd61292d88314662a569b0ecc 100644 (file)
@@ -25,6 +25,7 @@ import be.nikiroo.fanfix.library.BasicLibrary;
 import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.reader.Reader;
 import be.nikiroo.fanfix.reader.tui.TuiReaderMainWindow.Mode;
+import be.nikiroo.fanfix.supported.SupportType;
 import be.nikiroo.utils.Progress;
 
 /**
@@ -124,6 +125,16 @@ class TuiReaderApplication extends TApplication implements Reader {
        public void setChapter(int chapter) {
                reader.setChapter(chapter);
        }
+       
+       @Override
+       public void search(SupportType searchOn, String keywords, int page, int item) {
+               reader.search(searchOn, keywords, page,item);   
+       }
+       
+       @Override
+       public void searchTag(SupportType searchOn, int page, int item, String... tags) {
+               reader.searchTag(searchOn, page, item, tags);
+       }
 
        /**
         * Open the given {@link Story} for reading. This may or may not start an
index f4a932b3b899ef3fc48826bbe0b3aaa913a68d90..2ca61919cc5adb1aa714f3618e38e7b7f3f85bfb 100644 (file)
@@ -25,6 +25,7 @@ import be.nikiroo.fanfix.library.BasicLibrary;
 import be.nikiroo.fanfix.library.CacheLibrary;
 import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.reader.Reader;
+import be.nikiroo.fanfix.supported.SupportType;
 import be.nikiroo.utils.Progress;
 import be.nikiroo.utils.Version;
 import be.nikiroo.utils.ui.UIUtils;
@@ -217,6 +218,18 @@ class GuiReader extends BasicReader {
                        super.start(target, program, sync);
                }
        }
+       
+       @Override
+       public void search(SupportType searchOn, String keywords, int page, int item) {
+               // TODO: !!!
+               throw new java.lang.IllegalStateException("Not implemented yet.");
+       }
+       
+       @Override
+       public void searchTag(SupportType searchOn, int page, int item, String... tags) {
+               // TODO: !!!
+               throw new java.lang.IllegalStateException("Not implemented yet.");
+       }
 
        /**
         * Delete the {@link Story} from the cache if it is present, but <b>NOT</b>
index c6394430e6e08c8b34ff49e2a0d2b819f6da47bc..ebc509611856b0a6f6d699a64c7a96592a68428a 100644 (file)
@@ -83,7 +83,7 @@ public abstract class BasicSearchable {
         * @throws IOException
         *             in case of I/O error
         */
-       abstract protected void fillTag(SearchableTag tag) throws IOException;
+       abstract public void fillTag(SearchableTag tag) throws IOException;
 
        /**
         * Search for the given term and return a list of stories satisfying this
index bcc4759d1390f4f765963136eeb23b890fd07559..71732fc5d5d16ba178e775d6fff87bfd2eff1b4c 100644 (file)
@@ -94,7 +94,7 @@ class Fanfiction extends BasicSearchable {
        }
 
        @Override
-       protected void fillTag(SearchableTag tag) throws IOException {
+       public void fillTag(SearchableTag tag) throws IOException {
                if (tag.getId() == null || tag.isComplete()) {
                        return;
                }