View Javadoc
1   /**
2    * This file Copyright (c) 2011-2015 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         if (item.isNode()) {
162             final Node node = (Node) item;
163             try {
164                 final NodeIterator it = node.getNodes();
165                 while (it.hasNext()) {
166                     if (isNodeVisible(it.nextNode())) {
167                         return true;
168                     }
169                 }
170             } catch (RepositoryException e) {
171                 log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
172             }
173 
174 
175             if (getConfiguration().isIncludeProperties()) {
176                 try {
177                     final PropertyIterator propertyIterator = node.getProperties();
178                     while (propertyIterator.hasNext()) {
179                         final Property property = propertyIterator.nextProperty();
180                         if (!isJcrOrMgnlProperty(property)) {
181                             return true;
182                         }
183                     }
184                 } catch (RepositoryException e) {
185                     log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
186                 }
187             }
188         }
189         return false;
190     }
191 
192     private boolean isJcrOrMgnlProperty(Property property) throws RepositoryException {
193         final String propertyName = property.getName();
194         return propertyName.startsWith(NodeTypes.JCR_PREFIX) || propertyName.startsWith(NodeTypes.MGNL_PREFIX);
195     }
196 
197     protected Collection<JcrItemId> createContainerIds(Collection <Item> children) {
198         ArrayList<JcrItemId> ids = new ArrayList<JcrItemId>();
199         for (Item child : children) {
200             try {
201                 JcrItemId itemId = JcrItemUtil.getItemId(child);
202                 ids.add(itemId);
203             } catch (RepositoryException e) {
204                 handleRepositoryException(log, "Cannot retrieve currentId", e);
205             }
206         }
207         return ids;
208     }
209 
210     @Override
211     public List<String> getSortableContainerPropertyIds() {
212         // at present tree view is not sortable
213         return Collections.emptyList();
214     }
215 
216     public Collection<Item> getChildren(Item item) {
217         if (!item.isNode()) {
218             return Collections.emptySet();
219         }
220 
221         Node node = (Node) item;
222 
223         ArrayList<Item> items = new ArrayList<Item>();
224 
225         try {
226             NodeIterator iterator = node.getNodes();
227             while (iterator.hasNext()) {
228                 Node next = iterator.nextNode();
229                 if (isNodeVisible(next)) {
230                     items.add(next);
231                 }
232             }
233 
234             if (getConfiguration().isIncludeProperties()) {
235                 ArrayList<Property> properties = new ArrayList<Property>();
236                 PropertyIterator propertyIterator = node.getProperties();
237                 while (propertyIterator.hasNext()) {
238                     final Property property = propertyIterator.nextProperty();
239                     if (!isJcrOrMgnlProperty(property)) {
240                         properties.add(property);
241                     }
242                 }
243                 ItemNameComparator itemNameComparator = new ItemNameComparator();
244                 Collections.sort(properties, itemNameComparator);
245                 items.addAll(properties);
246             }
247         } catch (RepositoryException e) {
248             handleRepositoryException(log, "Could not retrieve children", e);
249         }
250 
251         return Collections.unmodifiableCollection(items);
252     }
253 
254     protected boolean isNodeVisible(Node node) throws RepositoryException {
255 
256         if (!getConfiguration().isIncludeSystemNodes() && node.getName().startsWith("jcr:") || node.getName().startsWith("rep:")) {
257             return false;
258         }
259 
260         String primaryNodeTypeName = node.getPrimaryNodeType().getName();
261         for (NodeTypeDefinition nodeTypeDefinition : getConfiguration().getNodeTypes()) {
262             if (nodeTypeDefinition.isStrict()) {
263                 if (primaryNodeTypeName.equals(nodeTypeDefinition.getName())) {
264                     return true;
265                 }
266             } else if (NodeUtil.isNodeType(node, nodeTypeDefinition.getName())) {
267                 return true;
268             }
269         }
270         return false;
271     }
272 
273     public Collection<Item> getRootItemIds() throws RepositoryException {
274         return getChildren(getRootNode());
275     }
276 
277     /**
278      * Checks if an item is a root. Since root node is never shown, we consider its child nodes and properties as roots
279      * to remove unnecessary offset in trees.
280      */
281     public boolean isRoot(Item item) throws RepositoryException {
282         if (item != null) {
283             try {
284                 int rootDepth = getRootNode().getDepth();
285                 return item.getDepth() <= rootDepth + 1;
286             } catch (RepositoryException e) {
287                 handleRepositoryException(log, "Cannot determine depth of jcr item", e);
288             }
289         }
290         return true;
291     }
292 
293     /**
294      * Only used in tests.
295      */
296     String getPathInTree(Item item) throws RepositoryException {
297         String base = getConfiguration().getRootPath();
298         return "/".equals(base) ? item.getPath() : StringUtils.substringAfter(item.getPath(), base);
299     }
300 
301     private Session getSession() throws RepositoryException {
302         return MgnlContext.getJCRSession(getWorkspace());
303     }
304 
305     protected Node getRootNode() throws RepositoryException {
306         return getSession().getNode(getConfiguration().getRootPath());
307     }
308 }