diff gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java @ 9220:e3c2ae1887e8

Allow to filter contents of datacage on client side. Allow to override column label of datacage Some code cleanup
author gernotbelger
date Wed, 04 Jul 2018 12:00:51 +0200
parents 93da474506e7
children 84397da33d17
line wrap: on
line diff
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java	Wed Jul 04 10:46:37 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java	Wed Jul 04 12:00:51 2018 +0200
@@ -8,468 +8,409 @@
 
 package org.dive4elements.river.client.client.ui;
 
-import com.google.gwt.core.client.GWT;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-import com.smartgwt.client.types.SelectionStyle;
-import com.smartgwt.client.types.TreeModelType;
-
-import com.smartgwt.client.util.SC;
-
-import com.smartgwt.client.widgets.Button;
-
-import com.smartgwt.client.widgets.events.ClickEvent;
-import com.smartgwt.client.widgets.events.ClickHandler;
-
-import com.smartgwt.client.widgets.grid.HoverCustomizer;
-import com.smartgwt.client.widgets.grid.ListGridRecord;
-
-import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
-import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
-
-import com.smartgwt.client.widgets.layout.VLayout;
-
-import com.smartgwt.client.widgets.tree.Tree;
-import com.smartgwt.client.widgets.tree.TreeGrid;
-import com.smartgwt.client.widgets.tree.TreeNode;
-
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.MissingResourceException;
 import java.util.Stack;
 
 import org.dive4elements.river.client.client.Config;
 import org.dive4elements.river.client.client.FLYSConstants;
-
 import org.dive4elements.river.client.client.event.DatacageDoubleClickHandler;
 import org.dive4elements.river.client.client.event.DatacageHandler;
-
 import org.dive4elements.river.client.client.services.MetaDataService;
 import org.dive4elements.river.client.client.services.MetaDataServiceAsync;
-
-import org.dive4elements.river.client.shared.model.Artifact;
 import org.dive4elements.river.client.shared.model.AttrList;
 import org.dive4elements.river.client.shared.model.DataCageNode;
 import org.dive4elements.river.client.shared.model.DataCageTree;
 import org.dive4elements.river.client.shared.model.ToLoad;
 import org.dive4elements.river.client.shared.model.User;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.types.TreeModelType;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Button;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.grid.HoverCustomizer;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tree.Tree;
+import com.smartgwt.client.widgets.tree.TreeGrid;
+import com.smartgwt.client.widgets.tree.TreeNode;
+
 // TODO: refactor, extract ~DataCageGrid
 /**
  * Display tree of, for example, previous calculations and allows
  * selection in order to access/clone these.
  */
-public class DatacageWidget
-extends      VLayout
-{
+public class DatacageWidget extends VLayout {
     public static final int MAX_OPEN = 30;
 
-    protected MetaDataServiceAsync metaDataService =
-        GWT.create(MetaDataService.class);
-
-    protected FLYSConstants messages =
-        GWT.create(FLYSConstants.class);
-
-    protected Artifact artifact;
-    protected User     user;
-    protected String   outs;
-    protected String   parameters;
-
-    protected TreeGrid treeGrid;
-    protected Tree     tree;
-
-    protected ToLoad   toLoad;
-
-    protected List<DatacageHandler>            handlers;
-    protected List<DatacageDoubleClickHandler> doubleHandlers;
-
-    /** Layout to show spinning wheel of joy. */
-    protected VLayout lockScreen;
-
-
-    public DatacageWidget() {
-        handlers       = new ArrayList<DatacageHandler>();
-        doubleHandlers = new ArrayList<DatacageDoubleClickHandler>();
+    public interface DatacageFilter {
+        boolean accept(DataCageNode node);
     }
 
-
-    public DatacageWidget(Artifact artifact, User user) {
-        this(artifact, user, null);
-    }
-
-    public DatacageWidget(Artifact artifact, User user, String outs) {
-        this(artifact, user, outs, true);
-    }
+    public static final DatacageFilter ACCEPT_ALL_FILTER = new DatacageFilter() {
+        @Override
+        public boolean accept(final DataCageNode node) {
+            return true;
+        }
+    };
 
-    public DatacageWidget(
-        Artifact   artifact,
-        User       user,
-        String     outs,
-        boolean    showButton
-    ) {
-        this(artifact, user, outs, null, showButton);
-    }
-
+    private final MetaDataServiceAsync metaDataService = GWT.create(MetaDataService.class);
 
-    public DatacageWidget(
-        Artifact   artifact,
-        User       user,
-        String     outs,
-        String     parameters,
-        boolean    showButton
-    ) {
-        this();
+    private final FLYSConstants messages = GWT.create(FLYSConstants.class);
 
-        this.artifact   = artifact;
-        this.user       = user;
-        this.outs       = outs;
-        this.parameters = parameters;
+    private final List<DatacageHandler> handlers = new ArrayList<DatacageHandler>();
 
-        toLoad = new ToLoad();
+    private final List<DatacageDoubleClickHandler> doubleHandlers = new ArrayList<DatacageDoubleClickHandler>();
+
+    private final TreeGrid treeGrid;
+
+    private Tree tree;
+
+    private ToLoad toLoad;
+
+    /** Layout to show spinning wheel of joy. */
+    private VLayout lockScreen;
+
+    private DataCageTree dcTree;
+
+    private DatacageFilter filter = ACCEPT_ALL_FILTER;
+
+    private DatacageWidgetData data;
+
+    public DatacageWidget(final DatacageWidgetData data) {
+
+        this.data = data;
+
+        this.toLoad = new ToLoad();
 
         setWidth100();
 
-        tree = new Tree();
-        tree.setModelType(TreeModelType.CHILDREN);
-        tree.setNameProperty("name");
-        tree.setIdField("id");
-        tree.setChildrenProperty("children-nodes");
-        tree.setShowRoot(false);
+        this.treeGrid = new TreeGrid();
 
-        treeGrid = new TreeGrid();
-        treeGrid.setLoadDataOnDemand(false);
-        treeGrid.setWidth100();
-        treeGrid.setHeight100();
-        treeGrid.setShowRoot(false);
-        treeGrid.setNodeIcon("[SKIN]/../blank.gif");
-        treeGrid.setShowConnectors(true);
-        treeGrid.setLoadingMessage(messages.databasket_loading());
-        treeGrid.setEmptyMessage(messages.databasket_loading());
-        treeGrid.setLoadingDataMessage(messages.databasket_loading());
+        final String columnLabel = this.data.getColumnLabel();
+        if (columnLabel != null)
+            this.treeGrid.setTreeFieldTitle(columnLabel);
 
-        treeGrid.setHoverMoveWithMouse(true);
-        treeGrid.setCanHover(true);
-        treeGrid.setShowHover(true);
-        treeGrid.setHoverOpacity(75);
-        treeGrid.setHoverWidth(120);
+        this.treeGrid.setLoadDataOnDemand(false);
+        this.treeGrid.setWidth100();
+        this.treeGrid.setHeight100();
+        this.treeGrid.setShowRoot(false);
+        this.treeGrid.setNodeIcon("[SKIN]/../blank.gif");
+        this.treeGrid.setShowConnectors(true);
+        this.treeGrid.setLoadingMessage(this.messages.databasket_loading());
+        this.treeGrid.setLoadingDataMessage(this.messages.databasket_loading());
 
-        treeGrid.setHoverCustomizer(new HoverCustomizer() {
+        // FIXME: other message
+        this.treeGrid.setEmptyMessage(this.messages.databasket_loading());
+
+        this.treeGrid.setHoverMoveWithMouse(true);
+        this.treeGrid.setCanHover(true);
+        this.treeGrid.setShowHover(true);
+        this.treeGrid.setHoverOpacity(75);
+        this.treeGrid.setHoverWidth(120);
+
+        this.treeGrid.setHoverCustomizer(new HoverCustomizer() {
             @Override
-            public String hoverHTML(Object value,
-                ListGridRecord record,
-                int rowNum,
-                int colNum
-                ) {
-                if(record instanceof TreeNode) {
-                    TreeNode hoveredTreeNode = (TreeNode)record;
+            public String hoverHTML(final Object value, final ListGridRecord record, final int rowNum, final int colNum) {
+                if (record instanceof TreeNode) {
+                    final TreeNode hoveredTreeNode = (TreeNode) record;
                     String info = hoveredTreeNode.getAttribute("info");
                     if (info == null) {
                         info = hoveredTreeNode.getName();
                     }
                     return info;
                 }
-                else {
-                    return "";// should not happen
-                }
+
+                return "";// should not happen
             }
         });
 
-        treeGrid.addRecordDoubleClickHandler(new RecordDoubleClickHandler() {
+        this.treeGrid.addRecordDoubleClickHandler(new RecordDoubleClickHandler() {
             @Override
-            public void onRecordDoubleClick(RecordDoubleClickEvent event) {
+            public void onRecordDoubleClick(final RecordDoubleClickEvent event) {
                 doubleClickedOnTree(event);
             }
         });
 
-        addMember(treeGrid);
+        addMember(this.treeGrid);
 
-        if (showButton) {
+        if (data.isShowButton())
             addMember(createPlusButton());
-        }
 
         triggerTreeBuilding();
     }
 
+    public void setFilter(final DatacageFilter filter) {
+        assert (filter != null);
+
+        this.filter = filter;
+
+        if (this.dcTree != null)
+            updateTree(this.dcTree);
+    }
+
     /** Disable input, show spinning wheel of joy. */
-    public void lockUI() {
-        lockScreen = ScreenLock.lockUI(this, lockScreen);
+    private void lockUI() {
+        this.lockScreen = ScreenLock.lockUI(this, this.lockScreen);
     }
 
     /** Enable input, remove grey, remove spinning wheel of joy. */
-    public void unlockUI() {
-        ScreenLock.unlockUI(this, lockScreen);
+    protected final void unlockUI() {
+        ScreenLock.unlockUI(this, this.lockScreen);
     }
 
     /**
-     * @param handler Handler to be added (notified on add-action).
+     * Sets whether more than one item can be selected.
+     *
+     * @param multi
+     *            if true, allow mutliple selections.
      */
-    public DatacageWidget(Artifact artifact, User user, String outs,
-        DatacageHandler handler) {
-        this(artifact, user, outs);
-        this.addDatacageHandler(handler);
-    }
-
-
-    public DatacageWidget(
-        Artifact        artifact,
-        User            user,
-        String          outs,
-        DatacageHandler handler,
-        String          parameters
-    ) {
-        this(artifact, user, outs, handler);
-        this.parameters = parameters;
-    }
-
-
-    /**
-     * Sets whether more than one item can be selected.
-     * @param multi if true, allow mutliple selections.
-     */
-    public void setIsMutliSelectable(boolean multi) {
+    public void setIsMutliSelectable(final boolean multi) {
         if (multi) {
-            treeGrid.setSelectionType(SelectionStyle.MULTIPLE);
-        }
-        else {
-            treeGrid.setSelectionType(SelectionStyle.SINGLE);
+            this.treeGrid.setSelectionType(SelectionStyle.MULTIPLE);
+        } else {
+            this.treeGrid.setSelectionType(SelectionStyle.SINGLE);
         }
     }
 
-
     /**
-     * @param handler Handler to be added (notified on add-action).
+     * @param handler
+     *            Handler to be added (notified on add-action).
      */
-    public void addDatacageHandler(DatacageHandler handler) {
-        if (!handlers.contains(handler)) {
-            handlers.add(handler);
-        }
-    }
-
-
-    /**
-     * @param h Handler to be added (notified on Double click on node).
-     */
-    public void addDatacageDoubleClickHandler(DatacageDoubleClickHandler h) {
-        if (!doubleHandlers.contains(h)) {
-            doubleHandlers.add(h);
+    public void addDatacageHandler(final DatacageHandler handler) {
+        if (!this.handlers.contains(handler)) {
+            this.handlers.add(handler);
         }
     }
 
+    /**
+     * @param h
+     *            Handler to be added (notified on Double click on node).
+     */
+    public void addDatacageDoubleClickHandler(final DatacageDoubleClickHandler h) {
+        if (!this.doubleHandlers.contains(h)) {
+            this.doubleHandlers.add(h);
+        }
+    }
 
     /**
-     * @param handler Handler to remove from list.
+     * @param handler
+     *            Handler to remove from list.
      */
-    public void removeDatacageHandler(DatacageHandler handler) {
-        handlers.remove(handler);
+    public void removeDatacageHandler(final DatacageHandler handler) {
+        this.handlers.remove(handler);
     }
 
-
-    public ToLoad getToLoad() {
-        return toLoad;
-    }
-
-
     public ToLoad getSelection() {
         // Reset content of toLoads.
-        toLoad = new ToLoad();
+        this.toLoad = new ToLoad();
 
-        if (treeGrid == null) {
-            return toLoad;
+        if (this.treeGrid == null) {
+            return this.toLoad;
         }
 
-        ListGridRecord [] selection = treeGrid.getSelectedRecords();
+        final ListGridRecord[] selection = this.treeGrid.getSelectedRecords();
 
         if (selection != null) {
-            for (ListGridRecord record: selection) {
+            for (final ListGridRecord record : selection) {
                 if (record instanceof TreeNode) {
-                    collectToLoads((TreeNode)record);
+                    collectToLoads((TreeNode) record);
                 }
             }
         }
 
-        return toLoad;
+        return this.toLoad;
     }
 
-
     public List<TreeNode> getPlainSelection() {
-        ListGridRecord [] selection = treeGrid.getSelectedRecords();
-        List<TreeNode> nodes = new ArrayList<TreeNode>();
+        final ListGridRecord[] selection = this.treeGrid.getSelectedRecords();
+        final List<TreeNode> nodes = new ArrayList<TreeNode>();
         if (selection != null) {
-            for (ListGridRecord record: selection) {
+            for (final ListGridRecord record : selection) {
                 if (record instanceof TreeNode) {
-                    nodes.add((TreeNode)record);
+                    nodes.add((TreeNode) record);
                 }
             }
         }
         return nodes;
     }
 
-
     /**
      * Returns the titles of selected items (if any).
      */
     public String[] getSelectionTitles() {
-        if (treeGrid == null) {
+        if (this.treeGrid == null) {
             return new String[] {};
         }
 
-        ListGridRecord [] selection = treeGrid.getSelectedRecords();
+        final ListGridRecord[] selection = this.treeGrid.getSelectedRecords();
 
         if (selection == null) {
             return new String[] {};
         }
 
-        List<String> titleList = new ArrayList<String>();
-        for (ListGridRecord record: selection) {
+        final List<String> titleList = new ArrayList<String>();
+        for (final ListGridRecord record : selection) {
             if (record instanceof TreeNode) {
-                titleList.add(((TreeNode)record).getAttribute("name"));
+                titleList.add(((TreeNode) record).getAttribute("name"));
             }
         }
 
         return titleList.toArray(new String[titleList.size()]);
     }
 
-
     /**
      * Callback for add-button.
      * Fires to load for every selected element and handler.
      */
-    public void plusClicked() {
+    protected final void plusClicked() {
         if (!getSelection().isEmpty()) {
             fireToLoad();
         }
     }
 
-
-    protected Button createPlusButton() {
-        Button plusBtn = new Button(messages.datacageAdd());
+    private Button createPlusButton() {
+        final Button plusBtn = new Button(this.messages.datacageAdd());
         plusBtn.addClickHandler(new ClickHandler() {
             @Override
-            public void onClick(ClickEvent event) {
+            public void onClick(final ClickEvent event) {
                 plusClicked();
             }
         });
         return plusBtn;
     }
 
-
-    protected void fireToLoad() {
-        for (DatacageHandler handler: handlers) {
-            handler.toLoad(toLoad);
+    private void fireToLoad() {
+        for (final DatacageHandler handler : this.handlers) {
+            handler.toLoad(this.toLoad);
         }
     }
 
-
     /** Notify DatacageDoubleClickHandlers that a doubleclick happened. */
-    protected void fireOnDoubleClick() {
-        for (DatacageDoubleClickHandler handler: doubleHandlers) {
-            handler.onDoubleClick(toLoad);
+    private void fireOnDoubleClick() {
+        for (final DatacageDoubleClickHandler handler : this.doubleHandlers) {
+            handler.onDoubleClick(this.toLoad);
         }
     }
 
-
-    protected void doubleClickedOnTree(RecordDoubleClickEvent event) {
-        TreeNode node = (TreeNode)event.getRecord();
+    protected final void doubleClickedOnTree(final RecordDoubleClickEvent event) {
+        final TreeNode node = (TreeNode) event.getRecord();
         collectToLoads(node);
         fireOnDoubleClick();
     }
 
-
     /**
      * Adds to toLoad, from info in node.
      * Afterwards, add all children of node to stack to parse (next time
      * collectToLoads is called).
      */
-    protected void collectToLoads(TreeNode node) {
-        Stack<TreeNode> stack = new Stack<TreeNode>();
+    private void collectToLoads(TreeNode node) {
+        final Stack<TreeNode> stack = new Stack<TreeNode>();
 
         stack.push(node);
 
         while (!stack.isEmpty()) {
             node = stack.pop();
-            String factory = node.getAttribute("factory");
+            final String factory = node.getAttribute("factory");
             if (factory != null) { // we need at least a factory
-                String artifact = node.getAttribute("artifact-id");
-                String out      = node.getAttribute("out");
-                String name     = node.getAttribute("facet");
-                String ids      = node.getAttribute("ids");
-                String displayname = node.getAttribute("name");
-                String targetOut = node.getAttribute("target_out");
+                final String artifact = node.getAttribute("artifact-id");
+                final String out = node.getAttribute("out");
+                final String name = node.getAttribute("facet");
+                final String ids = node.getAttribute("ids");
+                final String displayname = node.getAttribute("name");
+                final String targetOut = node.getAttribute("target_out");
                 String debugAttributeValues = "";
-                for (String attr: node.getAttributes()) {
-                    debugAttributeValues += ("[" + attr +": "
-                        + node.getAttributeAsString(attr) + "] ");
+                for (final String attr : node.getAttributes()) {
+                    debugAttributeValues += ("[" + attr + ": " + node.getAttributeAsString(attr) + "] ");
                 }
-                GWT.log("DatacageWidget.collectToLoad, attributes are "
-                    + debugAttributeValues);
+                GWT.log("DatacageWidget.collectToLoad, attributes are " + debugAttributeValues);
 
-                toLoad.add(artifact,
-                     factory,
-                     out,
-                     name,
-                     ids,
-                     displayname,
-                     targetOut);
+                this.toLoad.add(artifact, factory, out, name, ids, displayname, targetOut);
             }
-            TreeNode [] children = tree.getChildren(node);
+            final TreeNode[] children = this.tree.getChildren(node);
             if (children != null) {
-                for (TreeNode child: children) {
+                for (final TreeNode child : children) {
                     stack.push(child);
                 }
             }
         }
     }
 
-
     /** Get meta-data and populate tree with it. */
-    protected void triggerTreeBuilding() {
-        Config config = Config.getInstance();
-        String locale = config.getLocale();
-
-        String artifactId = artifact.getUuid();
-        String userId     = (user != null) ? user.identifier() : null;
-
+    private void triggerTreeBuilding() {
         lockUI();
 
-        metaDataService.getMetaData(
-            locale,
-            artifactId,
-            userId,
-            outs,
-            parameters,
-            new AsyncCallback<DataCageTree>() {
-                @Override
-                public void onFailure(Throwable caught) {
-                    GWT.log("Could not load meta data.");
-                    SC.warn(caught.getMessage());
-                    unlockUI();
-                }
+        final String locale = Config.getInstance().getLocale();
 
-                @Override
-                public void onSuccess(DataCageTree dcTree) {
-                    GWT.log("Successfully loaded meta data.");
-                    IdGenerator idGenerator = new IdGenerator();
-                    DataCageNode dcRoot = dcTree.getRoot();
-                    TreeNode root = buildRecursiveChildren(
-                        dcRoot, idGenerator);
-                    tree.setRoot(root);
+        final String artifactId = this.data.getArtifact().getUuid();
+        final User user = this.data.getUser();
+        final String userId = user != null ? user.identifier() : null;
+        final String outs = this.data.getOuts();
+        final String parameters = this.data.getParameters();
 
-                    TreeNode[] nodes = tree.getChildren(root);
-                    for (TreeNode node: nodes) {
-                        if (node.getAttribute("factory") == null &&
-                                !tree.hasChildren(node)) {
-                            node.setIsFolder(true);
-                        }
-                    }
+        this.metaDataService.getMetaData(locale, artifactId, userId, outs, parameters, new AsyncCallback<DataCageTree>() {
+            @Override
+            public void onFailure(final Throwable caught) {
+                GWT.log("Could not load meta data.");
+                SC.warn(caught.getMessage());
+                unlockUI();
+            }
 
-                    if (idGenerator.current() < MAX_OPEN) {
-                        tree.openAll();
-                    }
-                    treeGrid.setData(tree);
-                    unlockUI();
-                }
-            });
+            @Override
+            public void onSuccess(final DataCageTree dcData) {
+                GWT.log("Successfully loaded meta data.");
+
+                setDatacageData(dcData);
+                unlockUI();
+            }
+        });
+    }
+
+    protected final void setDatacageData(final DataCageTree dcTree) {
+
+        this.dcTree = dcTree;
+
+        if (this.tree != null)
+            this.tree.destroy();
+
+        updateTree(this.dcTree);
+    }
+
+    private void updateTree(final DataCageTree data) {
+
+        this.tree = new Tree();
+        this.tree.setModelType(TreeModelType.CHILDREN);
+        this.tree.setNameProperty("name");
+        this.tree.setIdField("id");
+        this.tree.setChildrenProperty("children-nodes");
+        this.tree.setShowRoot(false);
+
+        final IdGenerator idGenerator = new IdGenerator();
+        final DataCageNode dcRoot = data.getRoot();
+        final TreeNode root = buildRecursiveChildren(dcRoot, idGenerator);
+        if (root != null) {
+            this.tree.setRoot(root);
+
+            // FIXME: why is this necessary? an it only happens for the first level...
+            final TreeNode[] nodes = this.tree.getChildren(root);
+            for (final TreeNode node : nodes) {
+                if (node.getAttribute("factory") == null && !this.tree.hasChildren(node))
+                    node.setIsFolder(true);
+            }
+        }
+
+        if (idGenerator.current() < MAX_OPEN)
+            this.tree.openAll();
+
+        this.treeGrid.setData(this.tree);
     }
 
     private static final class IdGenerator {
@@ -479,11 +420,11 @@
         }
 
         public int next() {
-            return current++;
+            return this.current++;
         }
 
         public int current() {
-            return current;
+            return this.current;
         }
     } // class IdGenerator
 
@@ -492,48 +433,58 @@
             return s;
         }
 
-        s = s.substring(2, s.length()-1);
+        s = s.substring(2, s.length() - 1);
 
         try {
-            return messages.getString(s);
+            return this.messages.getString(s);
         }
-        catch (MissingResourceException mre) {
+        catch (final MissingResourceException mre) {
             GWT.log("cannot find i18n for + '" + s + "'");
             return s;
         }
     }
 
-    protected TreeNode buildRecursiveChildren(
-        DataCageNode   node,
-        IdGenerator    idGenerator
-    ) {
-        TreeNode tn = new TreeNode();
+    private TreeNode buildRecursiveChildren(final DataCageNode node, final IdGenerator idGenerator) {
+
+        if (!this.filter.accept(node))
+            return null;
+
+        final List<DataCageNode> children = node.getChildren();
+
+        final Collection<TreeNode> tns = new ArrayList<TreeNode>();
+
+        if (children != null && !children.isEmpty()) {
+
+            for (final DataCageNode child : children) {
+
+                final TreeNode childNode = buildRecursiveChildren(child, idGenerator);
+                if (childNode != null)
+                    tns.add(childNode);
+            }
+
+            /* if we should have children, but all got filtered, hide this node as well */
+            if (tns.isEmpty())
+                return null;
+        }
+
+        final TreeNode tn = new TreeNode();
         tn.setAttribute("id", idGenerator.next());
 
-        List<DataCageNode> children = node.getChildren();
-
-        if (children != null) {
-            TreeNode [] tns = new TreeNode[children.size()];
-            for (int i = 0; i < tns.length; ++i) {
-                DataCageNode child = children.get(i);
-                tns[i] = buildRecursiveChildren(child, idGenerator);
-            }
-            tn.setAttribute("children-nodes", tns);
-        }
+        if (!tns.isEmpty())
+            tn.setAttribute("children-nodes", tns.toArray(new TreeNode[tns.size()]));
 
         tn.setAttribute("name", i18n(node.getDescription()));
         tn.setAttribute("facet", node.getName());
 
-        AttrList attrs = node.getAttributes();
+        final AttrList attrs = node.getAttributes();
         if (attrs != null) {
             for (int i = 0, N = attrs.size(); i < N; ++i) {
-                String key   = attrs.getKey(i);
-                String value = attrs.getValue(i);
+                final String key = attrs.getKey(i);
+                final String value = attrs.getValue(i);
                 tn.setAttribute(key, value);
             }
         }
 
         return tn;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org