View Javadoc
1   /**
2    * This file Copyright (c) 2011-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.workbench.tree;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.jcr.util.NodeTypes;
38  import info.magnolia.jcr.util.NodeUtil;
39  import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnectorDefinition;
40  import info.magnolia.ui.vaadin.integration.contentconnector.NodeTypeDefinition;
41  import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
42  import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
43  import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
44  import info.magnolia.ui.workbench.container.AbstractJcrContainer;
45  
46  import java.util.ArrayList;
47  import java.util.Collection;
48  import java.util.Collections;
49  import java.util.Comparator;
50  import java.util.List;
51  
52  import javax.jcr.Item;
53  import javax.jcr.Node;
54  import javax.jcr.NodeIterator;
55  import javax.jcr.Property;
56  import javax.jcr.PropertyIterator;
57  import javax.jcr.RepositoryException;
58  import javax.jcr.Session;
59  
60  import org.apache.commons.lang3.StringUtils;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  import com.vaadin.data.Container;
65  
66  /**
67   * Hierarchical implementation of {@link info.magnolia.ui.workbench.container.AbstractJcrContainer}.
68   */
69  public class HierarchicalJcrContainer extends AbstractJcrContainer implements Container.Hierarchical {
70  
71      private static final Logger log = LoggerFactory.getLogger(HierarchicalJcrContainer.class);
72  
73      private static class ItemNameComparator implements Comparator<Item> {
74          @Override
75          public int compare(Item lhs, Item rhs) {
76              try {
77                  return lhs.getName().compareTo(rhs.getName());
78              } catch (RepositoryException e) {
79                  log.warn("Cannot compare item names: " + e);
80                  return 0;
81              }
82          }
83      }
84  
85      public HierarchicalJcrContainer(JcrContentConnectorDefinition definition) {
86          super(definition);
87      }
88  
89      @Override
90      public Collection<JcrItemId> getChildren(Object itemId) {
91          long start = System.currentTimeMillis();
92          Collection<Item> children = getChildren(getJcrItem(itemId));
93          log.debug("Fetched {} children in {}ms", children.size(), System.currentTimeMillis() - start);
94          return createContainerIds(children);
95      }
96  
97      @Override
98      public JcrItemId getParent(Object itemId) {
99          try {
100             Item item = getJcrItem(itemId);
101             if (item.isNode() && item.getDepth() == 0) {
102                 return null;
103             }
104             return JcrItemUtil.getItemId(item.getParent());
105         } catch (RepositoryException e) {
106             handleRepositoryException(log, "Cannot determine parent for itemId: " + itemId, e);
107             return null;
108         }
109     }
110 
111     @Override
112     public Collection<JcrItemId> rootItemIds() {
113         try {
114             return createContainerIds(getRootItemIds());
115         } catch (RepositoryException e) {
116             handleRepositoryException(log, "Cannot retrieve root item id's", e);
117             return Collections.emptySet();
118         }
119     }
120 
121     @Override
122     public void refresh() {
123         resetOffset();
124         clearItemIndexes();
125         fireItemSetChange();
126     }
127 
128     @Override
129     public boolean setParent(Object itemId, Object newParentId) throws UnsupportedOperationException {
130         fireItemSetChange();
131         return true;
132     }
133 
134     @Override
135     public boolean areChildrenAllowed(Object itemId) {
136         final JcrItemAdapter item = ((JcrItemAdapter) getItem(itemId));
137         if (item == null) {
138             return false;
139         }
140         return item.isNode() && hasChildren(itemId);
141     }
142 
143     @Override
144     public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) throws UnsupportedOperationException {
145         throw new UnsupportedOperationException();
146     }
147 
148     @Override
149     public boolean isRoot(Object itemId) {
150         try {
151             return isRoot(getJcrItem(itemId));
152         } catch (RepositoryException e) {
153             handleRepositoryException(log, "Cannot determine whether item is root - itemId: " + itemId, e);
154             return true;
155         }
156     }
157 
158     @Override
159     public boolean hasChildren(Object itemId) {
160         final Item item = getJcrItem(itemId);
161         return item.isNode() && !getChildren(item).isEmpty();
162     }
163 
164     protected Collection<JcrItemId> createContainerIds(Collection<Item> children) {
165         ArrayList<JcrItemId> ids = new ArrayList<JcrItemId>();
166         for (Item child : children) {
167             try {
168                 JcrItemId itemId = JcrItemUtil.getItemId(child);
169                 ids.add(itemId);
170             } catch (RepositoryException e) {
171                 handleRepositoryException(log, "Cannot retrieve currentId", e);
172             }
173         }
174         return ids;
175     }
176 
177     @Override
178     public List<String> getSortableContainerPropertyIds() {
179         // at present tree view is not sortable
180         return Collections.emptyList();
181     }
182 
183     public Collection<Item> getChildren(Item item) {
184         if (!item.isNode()) {
185             return Collections.emptySet();
186         }
187 
188         Node node = (Node) item;
189 
190         ArrayList<Item> items = new ArrayList<Item>();
191 
192         try {
193             NodeIterator iterator = node.getNodes();
194             while (iterator.hasNext()) {
195                 Node next = iterator.nextNode();
196                 if (isNodeVisible(next)) {
197                     items.add(next);
198                 }
199             }
200 
201             if (getConfiguration().isIncludeProperties()) {
202                 ArrayList<Property> properties = new ArrayList<Property>();
203                 PropertyIterator propertyIterator = node.getProperties();
204                 while (propertyIterator.hasNext()) {
205                     final Property property = propertyIterator.nextProperty();
206                     final String propertyName = property.getName();
207                     if (!propertyName.startsWith(NodeTypes.JCR_PREFIX) && !propertyName.startsWith(NodeTypes.MGNL_PREFIX)) {
208                         properties.add(property);
209                     }
210                 }
211                 ItemNameComparator itemNameComparator = new ItemNameComparator();
212                 Collections.sort(properties, itemNameComparator);
213                 items.addAll(properties);
214             }
215         } catch (RepositoryException e) {
216             handleRepositoryException(log, "Could not retrieve children", e);
217         }
218 
219         return Collections.unmodifiableCollection(items);
220     }
221 
222     protected boolean isNodeVisible(Node node) throws RepositoryException {
223 
224         if (!getConfiguration().isIncludeSystemNodes() && node.getName().startsWith("jcr:") || node.getName().startsWith("rep:")) {
225             return false;
226         }
227 
228         String primaryNodeTypeName = node.getPrimaryNodeType().getName();
229         for (NodeTypeDefinition nodeTypeDefinition : getConfiguration().getNodeTypes()) {
230             if (nodeTypeDefinition.isStrict()) {
231                 if (primaryNodeTypeName.equals(nodeTypeDefinition.getName())) {
232                     return true;
233                 }
234             } else if (NodeUtil.isNodeType(node, nodeTypeDefinition.getName())) {
235                 return true;
236             }
237         }
238         return false;
239     }
240 
241     public Collection<Item> getRootItemIds() throws RepositoryException {
242         return getChildren(getRootNode());
243     }
244 
245     /**
246      * Checks if an item is a root. Since root node is never shown, we consider its child nodes and properties as roots
247      * to remove unnecessary offset in trees.
248      */
249     public boolean isRoot(Item item) throws RepositoryException {
250         if (item != null) {
251             try {
252                 int rootDepth = getRootNode().getDepth();
253                 return item.getDepth() <= rootDepth + 1;
254             } catch (RepositoryException e) {
255                 handleRepositoryException(log, "Cannot determine depth of jcr item", e);
256             }
257         }
258         return true;
259     }
260 
261     /**
262      * Only used in tests.
263      */
264     String getPathInTree(Item item) throws RepositoryException {
265         String base = getConfiguration().getRootPath();
266         return "/".equals(base) ? item.getPath() : StringUtils.substringAfter(item.getPath(), base);
267     }
268 
269     private Session getSession() throws RepositoryException {
270         return MgnlContext.getJCRSession(getWorkspace());
271     }
272 
273     private Node getRootNode() throws RepositoryException {
274         return getSession().getNode(getConfiguration().getRootPath());
275     }
276 }