breadcrumbs panel
authorNiki Roo <niki@nikiroo.be>
Fri, 24 Apr 2020 15:43:40 +0000 (17:43 +0200)
committerNiki Roo <niki@nikiroo.be>
Fri, 24 Apr 2020 15:43:40 +0000 (17:43 +0200)
13 files changed:
src/be/nikiroo/fanfix_swing/gui/BooksPanel.java
src/be/nikiroo/fanfix_swing/gui/BreadCrumbsPanel.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/BrowserPanel.java
src/be/nikiroo/fanfix_swing/gui/MainFrame.java
src/be/nikiroo/fanfix_swing/gui/PropertiesFrame.java
src/be/nikiroo/fanfix_swing/gui/SearchBar.java
src/be/nikiroo/fanfix_swing/gui/browser/BasicTab.java
src/be/nikiroo/fanfix_swing/gui/importer/ImporterFrame.java
src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java
src/be/nikiroo/fanfix_swing/gui/utils/DataNodeBook.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/utils/DataTreeAuthors.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/utils/DataTreeSources.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/utils/DataTreeTag.java [new file with mode: 0644]

index a2462fa57119688493949ab4707bc7c004d35b03..4f169544d7e32283d0b020c9383d6709ada658a9 100644 (file)
@@ -26,13 +26,13 @@ import be.nikiroo.fanfix_swing.gui.book.BookInfo;
 import be.nikiroo.fanfix_swing.gui.book.BookLine;
 import be.nikiroo.fanfix_swing.gui.book.BookPopup;
 import be.nikiroo.fanfix_swing.gui.book.BookPopup.Informer;
-import be.nikiroo.fanfix_swing.gui.utils.DelayWorker;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel.Predicate;
 import be.nikiroo.utils.compat.JList6;
 import be.nikiroo.utils.compat.ListCellRenderer6;
-import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
-import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
+import be.nikiroo.utils.ui.DelayWorker;
+import be.nikiroo.utils.ui.ListModel;
+import be.nikiroo.utils.ui.ListModel.Predicate;
+import be.nikiroo.utils.ui.ListenerPanel;
+import be.nikiroo.utils.ui.UIUtils;
 
 public class BooksPanel extends ListenerPanel {
        static public final String INVALIDATE_CACHE = "invalidate_cache";
@@ -67,7 +67,7 @@ public class BooksPanel extends ListenerPanel {
 
                list = initList();
                setListMode(listMode);
-               add(UiHelper.scroll(list), BorderLayout.CENTER);
+               add(UIUtils.scroll(list, false), BorderLayout.CENTER);
        }
 
        // null or empty -> all sources
diff --git a/src/be/nikiroo/fanfix_swing/gui/BreadCrumbsPanel.java b/src/be/nikiroo/fanfix_swing/gui/BreadCrumbsPanel.java
new file mode 100644 (file)
index 0000000..cae9e0d
--- /dev/null
@@ -0,0 +1,79 @@
+package be.nikiroo.fanfix_swing.gui;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.library.BasicLibrary;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo.Type;
+import be.nikiroo.fanfix_swing.gui.utils.DataNodeBook;
+import be.nikiroo.fanfix_swing.gui.utils.DataTreeAuthors;
+import be.nikiroo.fanfix_swing.gui.utils.DataTreeSources;
+import be.nikiroo.fanfix_swing.gui.utils.DataTreeTag;
+import be.nikiroo.utils.ui.BreadCrumbsBar;
+import be.nikiroo.utils.ui.DataNode;
+import be.nikiroo.utils.ui.DataTree;
+
+public class BreadCrumbsPanel extends BreadCrumbsBar<DataNodeBook> {
+       public BreadCrumbsPanel() {
+               super(new DataTree<DataNodeBook>() {
+                       @Override
+                       protected DataNode<DataNodeBook> extractData() throws IOException {
+                               List<? extends DataNode<DataNodeBook>> children = null;
+
+                               children = new DataTreeSources().loadData().getChildren();
+                               DataNode<DataNodeBook> sources = new DataNode<DataNodeBook>(
+                                               children, new DataNodeBook(Type.SOURCE, "Sources", true,
+                                                               !children.isEmpty()));
+                               children = new DataTreeAuthors().loadData().getChildren();
+                               DataNode<DataNodeBook> authors = new DataNode<DataNodeBook>(
+                                               children, new DataNodeBook(Type.AUTHOR, "Authors", true,
+                                                               !children.isEmpty()));
+                               children = new DataTreeTag().loadData().getChildren();
+                               DataNode<DataNodeBook> tags = new DataNode<DataNodeBook>(
+                                               children, new DataNodeBook(Type.TAG, "Tags", true,
+                                                               !children.isEmpty()));
+
+                               return new DataNode<DataNodeBook>(
+                                               Arrays.asList(sources, authors, tags),
+                                               new DataNodeBook(null, false));
+                       }
+
+                       @Override
+                       protected boolean checkFilter(String filter,
+                                       DataNodeBook userData) {
+                               return userData.toString().contains(filter.toLowerCase());
+                       }
+               });
+       }
+
+       public BookInfo getHighlight() {
+               DataNode<DataNodeBook> node = getSelectedNode();
+
+               if (node != null && node.getUserData() != null) {
+                       BasicLibrary lib = Instance.getInstance().getLibrary();
+
+                       DataNodeBook book = node.getUserData();
+                       if (book.getType() != null) {
+                               switch (book.getType()) {
+                               case SOURCE:
+                                       return BookInfo.fromSource(lib,
+                                                       book.isRoot() ? null : book.getPath());
+                               case AUTHOR:
+                                       return BookInfo.fromAuthor(lib,
+                                                       book.isRoot() ? null : book.getPath());
+                               case TAG:
+                                       return BookInfo.fromTag(lib,
+                                                       book.isRoot() ? null : book.getPath());
+
+                               default:
+                                       break;
+                               }
+                       }
+               }
+
+               return null;
+       }
+}
index 8ac78396d6b2cfb2c991441704aff5be76c16b66..d526e20bff9499e814a5584e7384568be12cd082 100644 (file)
@@ -20,8 +20,8 @@ import be.nikiroo.fanfix_swing.gui.browser.AuthorTab;
 import be.nikiroo.fanfix_swing.gui.browser.BasicTab;
 import be.nikiroo.fanfix_swing.gui.browser.SourceTab;
 import be.nikiroo.fanfix_swing.gui.browser.TagsTab;
-import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
+import be.nikiroo.utils.ui.ListenerPanel;
 
 /**
  * Panel dedicated to browse the stories through different means: by authors, by
@@ -158,7 +158,7 @@ public class BrowserPanel extends ListenerPanel {
         * Return a special "all" {@link BookInfo} of the correct type when nothing
         * is selected.
         * 
-        * @return the {@link BookInfo} to highlight, can be NULL
+        * @return the {@link BookInfo} to highlight, cannot be NULL
         */
        public BookInfo getHighlight() {
                String selected1 = null;
@@ -182,7 +182,7 @@ public class BrowserPanel extends ListenerPanel {
                }
 
                // ...what?
-               return null;
+               return BookInfo.fromSource(lib, selected1);
        }
 
        /**
index f74db0ae1846a3d674a56a2bf7e14ac17310800b..7e568beeb516dfeb69cb6b0db7261cc28c5d25c0 100644 (file)
@@ -3,15 +3,17 @@ package be.nikiroo.fanfix_swing.gui;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.swing.JComponent;
 import javax.swing.JFrame;
-import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.JSplitPane;
 
+import be.nikiroo.fanfix_swing.gui.book.BookInfo;
 import be.nikiroo.fanfix_swing.gui.importer.ImporterFrame;
 import be.nikiroo.utils.Version;
 
@@ -19,6 +21,7 @@ public class MainFrame extends JFrame {
        private BooksPanel books;
        private DetailsPanel details;
        private BrowserPanel browser;
+       private BreadCrumbsPanel goBack;
        private ImporterFrame importer = new ImporterFrame();
 
        public MainFrame(boolean sidePanel, boolean detailsPanel) {
@@ -26,11 +29,12 @@ public class MainFrame extends JFrame {
                setSize(800, 600);
                setJMenuBar(createMenuBar());
 
-               sidePanel = true;
-               detailsPanel = true;
+               // TODO: setSidePanel() and setDetailsPanel();
 
                browser = new BrowserPanel();
                books = new BooksPanel(true);
+               details = new DetailsPanel();
+               goBack = new BreadCrumbsPanel();
 
                JComponent other = null;
                boolean orientationH = true;
@@ -38,15 +42,14 @@ public class MainFrame extends JFrame {
                        other = browser;
                } else if (sidePanel && detailsPanel) {
                        JComponent side = browser;
-                       details = new DetailsPanel();
                        other = split(side, details, false, 0.5, 1);
                } else if (!sidePanel && !detailsPanel) {
                        orientationH = false;
-                       other = new JLabel("<< Go back");
+                       other = goBack;
+                       goBack.setVertical(false);
                } else if (!sidePanel && detailsPanel) {
-                       JComponent goBack = new JLabel("<< Go back");
-                       details = new DetailsPanel();
                        other = split(goBack, details, false, 0.5, 1);
+                       goBack.setVertical(true);
                }
 
                browser.addActionListener(new ActionListener() {
@@ -58,6 +61,35 @@ public class MainFrame extends JFrame {
                                details.setBook(browser.getHighlight());
                        }
                });
+               goBack.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               BookInfo book = goBack.getHighlight();
+                               List<String> sources = new ArrayList<String>();
+                               List<String> authors = new ArrayList<String>();
+                               List<String> tags = new ArrayList<String>();
+
+                               if (book != null && book.getMainInfo() != null) {
+                                       switch (book.getType()) {
+                                       case SOURCE:
+                                               sources.add(book.getMainInfo());
+                                               break;
+                                       case AUTHOR:
+                                               authors.add(book.getMainInfo());
+                                               break;
+                                       case TAG:
+                                               tags.add(book.getMainInfo());
+                                               break;
+
+                                       default:
+                                               break;
+                                       }
+                               }
+
+                               books.loadData(sources, authors, tags);
+                               details.setBook(book);
+                       }
+               });
                books.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
index db0654bf1ecf02f4d952dc15334299730f539dba..915c255067a5692f6c833946038cac04f38ab748 100644 (file)
@@ -30,7 +30,7 @@ public class PropertiesFrame extends JFrame {
                setTitle(Instance.getInstance().getTransGui().getString(
                                StringIdGui.TITLE_STORY, meta.getLuid(), meta.getTitle()));
 
-               PropertiesPane desc = new PropertiesPane(lib, meta);
+               PropertiesPanel desc = new PropertiesPanel(lib, meta);
                setSize(800, (int) desc.getPreferredSize().getHeight()
                                + 2 * desc.getBorderThickness());
 
index 0be2f080f1d4b70255acc268f8e8549e0e23f4f3..76fb2a7411dbd96fd677d34d8f14ec9495ec5de9 100644 (file)
@@ -11,11 +11,11 @@ import javax.swing.JButton;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
 
-import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
 import be.nikiroo.fanfix_swing.images.IconGenerator;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
+import be.nikiroo.utils.ui.ListenerPanel;
 
 /**
  * A generic search/filter bar.
index a4d675187443e7af2eb2ffdcb5ea97637ad9a7d3..6edb0f3f4250695c2849639212c8f56e5bbfac4b 100644 (file)
@@ -22,13 +22,13 @@ import javax.swing.tree.TreeCellRenderer;
 import javax.swing.tree.TreePath;
 
 import be.nikiroo.fanfix_swing.gui.SearchBar;
-import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
-import be.nikiroo.fanfix_swing.gui.utils.TreeCellSpanner;
-import be.nikiroo.fanfix_swing.gui.utils.TreeSnapshot;
-import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
 import be.nikiroo.fanfix_swing.images.IconGenerator;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
+import be.nikiroo.utils.ui.ListenerPanel;
+import be.nikiroo.utils.ui.TreeCellSpanner;
+import be.nikiroo.utils.ui.TreeSnapshot;
+import be.nikiroo.utils.ui.UIUtils;
 
 public abstract class BasicTab<T> extends ListenerPanel {
        private int totalCount = 0;
@@ -86,7 +86,7 @@ public abstract class BasicTab<T> extends ListenerPanel {
                        }
                });
 
-               add(UiHelper.scroll(tree), BorderLayout.CENTER);
+               add(UIUtils.scroll(tree, false), BorderLayout.CENTER);
 
                searchBar = new SearchBar();
                add(searchBar, BorderLayout.NORTH);
index 7db93374e4d8a8366e19368042affaeaec1dce76..6c0b9134f789a62173b5037ee926d0a2779f9a12 100644 (file)
@@ -23,10 +23,10 @@ import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.supported.BasicSupport;
 import be.nikiroo.fanfix_swing.Actions;
 import be.nikiroo.fanfix_swing.gui.SearchBar;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel.Predicate;
 import be.nikiroo.utils.Progress;
 import be.nikiroo.utils.compat.JList6;
+import be.nikiroo.utils.ui.ListModel;
+import be.nikiroo.utils.ui.ListModel.Predicate;
 
 public class ImporterFrame extends JFrame {
        private ListModel<ImporterItem> data;
index f3f6b0429bfe2d9912d0a2df6b696eb1b7adb76d..532c5f63f40aec7c6c11dc079898b6ecb2d9d8c2 100644 (file)
@@ -13,11 +13,11 @@ import javax.swing.ListCellRenderer;
 import javax.swing.SwingUtilities;
 
 import be.nikiroo.fanfix_swing.gui.utils.CoverImager;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel;
-import be.nikiroo.fanfix_swing.gui.utils.ListModel.Hoverable;
-import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.utils.Progress;
 import be.nikiroo.utils.Progress.ProgressListener;
+import be.nikiroo.utils.ui.ListModel;
+import be.nikiroo.utils.ui.ListenerPanel;
+import be.nikiroo.utils.ui.ListModel.Hoverable;
 
 public class ImporterItem extends ListenerPanel implements Hoverable {
        static public final String CHANGE = "change";
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/DataNodeBook.java b/src/be/nikiroo/fanfix_swing/gui/utils/DataNodeBook.java
new file mode 100644 (file)
index 0000000..1e26d15
--- /dev/null
@@ -0,0 +1,86 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import be.nikiroo.fanfix_swing.gui.book.BookInfo;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo.Type;
+
+public class DataNodeBook {
+       private Type type;
+       private String subname;
+       private String name;
+       private boolean children;
+       private boolean root;
+
+       private String display;
+
+       // root node
+       public DataNodeBook(Type type, boolean children) {
+               this(type, null, null, children);
+               this.root = true;
+       }
+
+       public DataNodeBook(Type type, String name, boolean root,
+                       boolean children) {
+               this(type, name, null, children);
+               this.root = root;
+       }
+
+       // no root, no children, empty path
+       public DataNodeBook(Type type, String name) {
+               this(type, name, null, false);
+               this.root = false;
+       }
+
+       // name empty = main value for the group
+       // not root
+       public DataNodeBook(Type type, String name, String subname,
+                       boolean children) {
+               this.type = type;
+               this.name = name == null ? "" : name;
+               this.subname = subname == null ? "" : subname;
+               this.children = children;
+               this.root = false;
+       }
+
+       public BookInfo.Type getType() {
+               return type;
+       }
+
+       public String getPath() {
+               String slash = "";
+               if (!name.isEmpty()) {
+                       if (children || !subname.isEmpty()) {
+                               slash = "/";
+                       }
+               }
+
+               return name + slash + subname;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public String getDisplay() {
+               if (display != null)
+                       return display;
+
+               if (!subname.isEmpty()) {
+                       return subname;
+               }
+
+               return name;
+       }
+
+       public void setDisplay(String display) {
+               this.display = display;
+       }
+
+       public boolean isRoot() {
+               return root;
+       }
+
+       @Override
+       public String toString() {
+               return getDisplay();
+       }
+}
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeAuthors.java b/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeAuthors.java
new file mode 100644 (file)
index 0000000..a46a49c
--- /dev/null
@@ -0,0 +1,33 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo.Type;
+import be.nikiroo.utils.ui.DataNode;
+import be.nikiroo.utils.ui.DataTree;
+
+public class DataTreeAuthors extends DataTree<DataNodeBook> {
+       @Override
+       protected boolean checkFilter(String filter, DataNodeBook userData) {
+               return userData.toString().toLowerCase().contains(filter.toLowerCase());
+       }
+
+       @Override
+       protected DataNode<DataNodeBook> extractData() throws IOException {
+               List<DataNode<DataNodeBook>> nodes = new ArrayList<DataNode<DataNodeBook>>();
+
+               // TODO: getResult() -> getTagList, getAuthorList... ?
+               List<String> authors = Instance.getInstance().getLibrary().getAuthors();
+               for (String author : authors) {
+                       nodes.add(new DataNode<DataNodeBook>(null,
+                                       new DataNodeBook(Type.AUTHOR, author)));
+               }
+
+               return new DataNode<DataNodeBook>(nodes,
+                               new DataNodeBook(Type.AUTHOR, !nodes.isEmpty()));
+       }
+}
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeSources.java b/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeSources.java
new file mode 100644 (file)
index 0000000..188765a
--- /dev/null
@@ -0,0 +1,55 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo.Type;
+import be.nikiroo.utils.ui.DataNode;
+import be.nikiroo.utils.ui.DataTree;
+
+public class DataTreeSources extends DataTree<DataNodeBook> {
+       @Override
+       protected boolean checkFilter(String filter, DataNodeBook userData) {
+               // TODO
+               return userData.toString().toLowerCase().contains(filter.toLowerCase());
+       }
+
+       @Override
+       protected DataNode<DataNodeBook> extractData() throws IOException {
+               List<DataNode<DataNodeBook>> nodes = new ArrayList<DataNode<DataNodeBook>>();
+
+               Map<String, List<String>> sourcesGrouped = Instance.getInstance()
+                               .getLibrary().getSourcesGrouped();
+
+               List<String> sources = new ArrayList<String>(sourcesGrouped.keySet());
+               sort(sources);
+               for (String source : sources) {
+                       List<String> children = sourcesGrouped.get(source);
+                       boolean hasChildren = (children.size() > 1) || (children.size() == 1
+                                       && !children.get(0).trim().isEmpty());
+
+                       List<DataNode<DataNodeBook>> subnodes = new ArrayList<DataNode<DataNodeBook>>();
+                       if (hasChildren) {
+                               sort(children);
+                               for (String subSource : children) {
+                                       boolean baseSubSource = subSource.isEmpty()
+                                                       && children.size() > 1;
+                                       DataNodeBook book = new DataNodeBook(Type.SOURCE, source,
+                                                       subSource, false);
+                                       if (baseSubSource)
+                                               book.setDisplay("*");
+                                       subnodes.add(new DataNode<DataNodeBook>(null, book));
+                               }
+                       }
+
+                       nodes.add(new DataNode<DataNodeBook>(subnodes,
+                                       new DataNodeBook(Type.SOURCE, source, "", hasChildren)));
+               }
+
+               return new DataNode<DataNodeBook>(nodes,
+                               new DataNodeBook(Type.SOURCE, !nodes.isEmpty()));
+       }
+}
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeTag.java b/src/be/nikiroo/fanfix_swing/gui/utils/DataTreeTag.java
new file mode 100644 (file)
index 0000000..445d2bf
--- /dev/null
@@ -0,0 +1,48 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.library.MetaResultList;
+import be.nikiroo.fanfix_swing.gui.book.BookInfo.Type;
+import be.nikiroo.utils.ui.DataNode;
+import be.nikiroo.utils.ui.DataTree;
+
+public class DataTreeTag extends DataTree<DataNodeBook> {
+       @Override
+       protected boolean checkFilter(String filter, DataNodeBook userData) {
+               return userData.toString().toLowerCase().contains(filter.toLowerCase());
+       }
+
+       @Override
+       protected DataNode<DataNodeBook> extractData() throws IOException {
+               List<DataNode<DataNodeBook>> nodes = new ArrayList<DataNode<DataNodeBook>>();
+
+               List<String> tagList = new ArrayList<String>();
+               MetaResultList metas = Instance.getInstance().getLibrary().getList();
+               // TODO: getTagList, getAuthorList... ?
+               for (MetaData meta : metas.getMetas()) {
+                       List<String> tags = meta.getTags();
+                       if (tags != null) {
+                               for (String tag : tags) {
+                                       if (!tagList.contains(tag)) {
+                                               tagList.add(tag);
+                                       }
+                               }
+                       }
+               }
+               sort(tagList);
+
+               for (String tag : tagList) {
+                       nodes.add(new DataNode<DataNodeBook>(null,
+                                       new DataNodeBook(Type.TAG, tag)));
+               }
+
+               return new DataNode<DataNodeBook>(nodes,
+                               new DataNodeBook(Type.TAG, !nodes.isEmpty()));
+       }
+}