View Javadoc

1   /**
2    * This file Copyright (c) 2011 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.admincentral.tree.container;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.jcr.RuntimeRepositoryException;
38  import info.magnolia.jcr.util.NodeTypes;
39  import info.magnolia.jcr.util.NodeUtil;
40  import info.magnolia.ui.model.workbench.definition.WorkbenchDefinition;
41  import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
42  import info.magnolia.ui.vaadin.integration.jcr.container.AbstractJcrContainer;
43  
44  import java.util.ArrayList;
45  import java.util.Collection;
46  import java.util.Collections;
47  import java.util.Comparator;
48  import java.util.List;
49  
50  import javax.jcr.Item;
51  import javax.jcr.Node;
52  import javax.jcr.NodeIterator;
53  import javax.jcr.Property;
54  import javax.jcr.PropertyIterator;
55  import javax.jcr.RepositoryException;
56  import javax.jcr.Session;
57  
58  import org.apache.commons.lang.StringUtils;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  
62  import com.vaadin.data.Container;
63  
64  /**
65   * Hierarchical implementation of {@link info.magnolia.ui.vaadin.integration.jcr.container.AbstractJcrContainer}.
66   */
67  public class HierarchicalJcrContainer extends AbstractJcrContainer implements Container.Hierarchical {
68  
69      private static final Logger log = LoggerFactory.getLogger(HierarchicalJcrContainer.class);
70  
71      private static class ItemNameComparator implements Comparator<Item> {
72          @Override
73          public int compare(Item lhs, Item rhs) {
74              try {
75                  return lhs.getName().compareTo(rhs.getName());
76              } catch (RepositoryException e) {
77                  throw new RuntimeRepositoryException(e);
78              }
79          }
80      }
81  
82      public HierarchicalJcrContainer(WorkbenchDefinition workbenchDefinition) {
83          super(workbenchDefinition);
84      }
85  
86      @Override
87      public Collection<String> getChildren(Object itemId) {
88          try {
89              long start = System.currentTimeMillis();
90              Collection<Item> children = getChildren(getItemByPath((String) itemId));
91              log.debug("Fetched {} children in {}ms", children.size(), System.currentTimeMillis() - start);
92              return createContainerIds(children);
93          } catch (RepositoryException e) {
94              throw new RuntimeRepositoryException(e);
95          }
96      }
97  
98      @Override
99      public String getParent(Object itemId) {
100         try {
101             Item item = getItemByPath((String) itemId);
102             if (item.isNode() && item.getDepth() == 0) {
103                 return null;
104             }
105             return item.getParent().getPath();
106         } catch (RepositoryException e) {
107             throw new RuntimeRepositoryException(e);
108         }
109     }
110 
111     @Override
112     public Collection<String> rootItemIds() {
113         try {
114             return createContainerIds(getRootItemIds());
115         } catch (RepositoryException e) {
116             throw new RuntimeRepositoryException(e);
117         }
118     }
119 
120     @Override
121     public boolean setParent(Object itemId, Object newParentId) throws UnsupportedOperationException {
122         fireItemSetChange();
123         return true;
124     }
125 
126     @Override
127     public boolean areChildrenAllowed(Object itemId) {
128         final JcrItemAdapter item = ((JcrItemAdapter) getItem(itemId));
129         return item.isNode() && hasChildren(itemId);
130     }
131 
132     @Override
133     public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) throws UnsupportedOperationException {
134         throw new UnsupportedOperationException();
135     }
136 
137     @Override
138     public boolean isRoot(Object itemId) {
139         try {
140             return isRoot(getItemByPath((String) itemId));
141         } catch (RepositoryException e) {
142             throw new RuntimeRepositoryException(e);
143         }
144     }
145 
146     @Override
147     public boolean hasChildren(Object itemId) {
148         try {
149             final Item item = getItemByPath((String) itemId);
150             return item.isNode() && !getChildren(item).isEmpty();
151         } catch (RepositoryException e) {
152             throw new RuntimeRepositoryException(e);
153         }
154     }
155 
156     protected Collection<String> createContainerIds(Collection<Item> children) throws RepositoryException {
157         ArrayList<String> ids = new ArrayList<String>();
158         for (Item child : children) {
159             ids.add(child.getPath());
160         }
161         return ids;
162     }
163 
164     @Override
165     public List<String> getSortableContainerPropertyIds() {
166         // at present tree view is not sortable
167         return Collections.emptyList();
168     }
169 
170     public Collection<Item> getChildren(Item item) throws RepositoryException {
171         if (!item.isNode()) {
172             return Collections.emptySet();
173         }
174 
175         Node node = (Node) item;
176 
177         ArrayList<Item> items = new ArrayList<Item>();
178 
179         ArrayList<Node> mainItemTypeNodes = new ArrayList<Node>();
180         ArrayList<Node> groupingItemTypeNodes = new ArrayList<Node>();
181         NodeIterator iterator = node.getNodes();
182         final String mainItemTypeName = getMainItemTypeAsString();
183         final String groupingItemTypeName = getWorkbenchDefinition().getGroupingItemType() == null ? null : getWorkbenchDefinition().getGroupingItemType().getItemType();
184         String currentNodeTypeName;
185         while (iterator.hasNext()) {
186             Node next = iterator.nextNode();
187             currentNodeTypeName = next.getPrimaryNodeType().getName();
188             if (mainItemTypeName.equals(currentNodeTypeName)) {
189                 mainItemTypeNodes.add(next);
190             }
191             if (groupingItemTypeName != null && groupingItemTypeName.equals(currentNodeTypeName)) {
192                 groupingItemTypeNodes.add(next);
193             }
194         }
195 
196         items.addAll(groupingItemTypeNodes);
197         items.addAll(mainItemTypeNodes);
198 
199         if (getWorkbenchDefinition().includeProperties()) {
200             ArrayList<Property> properties = new ArrayList<Property>();
201             PropertyIterator propertyIterator = node.getProperties();
202             while (propertyIterator.hasNext()) {
203                 final Property property = propertyIterator.nextProperty();
204                 final String propertyName = property.getName();
205                 if (!propertyName.startsWith(NodeTypes.JCR_PREFIX) && !propertyName.startsWith(NodeTypes.MGNL_PREFIX)) {
206                     properties.add(property);
207                 }
208             }
209             ItemNameComparator itemNameComparator = new ItemNameComparator();
210             Collections.sort(properties, itemNameComparator);
211             items.addAll(properties);
212         }
213 
214         return Collections.unmodifiableCollection(items);
215     }
216 
217     public Collection<Item> getRootItemIds() throws RepositoryException {
218         return getChildren(getRootNode());
219     }
220 
221     /**
222      * Checks if an item is a root. Since root node is never shown, we consider its child nodes and properties as roots
223      * to remove unnecessary offset in trees.
224      */
225     public boolean isRoot(Item item) throws RepositoryException {
226         int rootDepth = getRootNode().getDepth();
227         return item.getDepth() <= rootDepth + 1;
228     }
229 
230     public Item getItemByPath(String path) throws RepositoryException {
231         String absolutePath = getPathInWorkspace(path);
232         return getSession().getItem(absolutePath);
233     }
234 
235     // Move operations performed by drag-n-drop in JcrBrowser
236 
237     // TODO these move methods need to be commands instead
238 
239     public boolean moveItem(Item source, Item target) throws RepositoryException {
240         if (!basicMoveCheck(source, target)) {
241             return false;
242         }
243         NodeUtil.moveNode((Node) source, (Node) target);
244         source.getSession().save();
245 
246         return true;
247     }
248 
249     public boolean moveItemBefore(Item source, Item target) throws RepositoryException {
250         if (!basicMoveCheck(source, target)) {
251             return false;
252         }
253 
254         NodeUtil.moveNodeBefore((Node) source, (Node) target);
255         source.getSession().save();
256 
257         return true;
258     }
259 
260     public boolean moveItemAfter(Item source, Item target) throws RepositoryException {
261         if (!basicMoveCheck(source, target)) {
262             return false;
263         }
264 
265         NodeUtil.moveNodeAfter((Node) source, (Node) target);
266         source.getSession().save();
267 
268         return true;
269     }
270 
271     /**
272      * Perform basic check.
273      */
274     private boolean basicMoveCheck(Item source, Item target) throws RepositoryException {
275         // One or both are not node... do nothing
276         if (!target.isNode() && !source.isNode()) {
277             return false;
278         }
279         // Source and origin are the same... do nothing
280         if (target.getPath().equals(source.getPath())) {
281             return false;
282         }
283         // Source can not be a child of target.
284         return !target.getPath().startsWith(source.getPath());
285     }
286 
287     /**
288      * Only used in tests.
289      */
290     String getPathInTree(Item item) throws RepositoryException {
291         String base = getWorkbenchDefinition().getPath();
292         return "/".equals(base) ? item.getPath() : StringUtils.substringAfter(item.getPath(), base);
293     }
294 
295     private Session getSession() throws RepositoryException {
296         return MgnlContext.getJCRSession(getWorkspace());
297     }
298 
299     private Node getRootNode() throws RepositoryException {
300         return getSession().getNode(getWorkbenchDefinition().getPath());
301     }
302 
303     private String getPathInWorkspace(String pathInTree) {
304         // if path is absolute, just return it
305         if (pathInTree.startsWith("/")) {
306             return pathInTree;
307         }
308         String base = getWorkbenchDefinition().getPath();
309         return base + pathInTree;
310     }
311 
312 }