1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.workbench.tree;
35
36 import info.magnolia.cms.util.QueryUtil;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.jcr.RuntimeRepositoryException;
39 import info.magnolia.jcr.util.NodeTypes;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnectorDefinition;
42 import info.magnolia.ui.vaadin.integration.contentconnector.NodeTypeDefinition;
43 import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
44 import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
45 import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
46 import info.magnolia.ui.workbench.container.AbstractJcrContainer;
47 import info.magnolia.ui.workbench.container.OrderBy;
48
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.List;
54
55 import javax.jcr.Item;
56 import javax.jcr.Node;
57 import javax.jcr.NodeIterator;
58 import javax.jcr.Property;
59 import javax.jcr.PropertyIterator;
60 import javax.jcr.RepositoryException;
61 import javax.jcr.Session;
62 import javax.jcr.nodetype.NodeType;
63 import javax.jcr.query.Query;
64
65 import org.apache.commons.lang3.StringUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.vaadin.v7.data.Container;
70
71
72
73
74 public class HierarchicalJcrContainer extends AbstractJcrContainer implements Container.Hierarchical {
75
76 private static final Logger log = LoggerFactory.getLogger(HierarchicalJcrContainer.class);
77
78 private static final String WHERE_CLAUSE_FOR_PATH = " ISCHILDNODE('%s')";
79
80 private int rootDepth = -1;
81 private String nodePath;
82
83 private boolean sortable;
84
85 private boolean isIncludingSystemProperties = false;
86
87 public HierarchicalJcrContainer(JcrContentConnectorDefinition definition) {
88 super(definition);
89 }
90
91 @Override
92 public Collection<JcrItemId> getChildren(Object itemId) {
93 long start = System.currentTimeMillis();
94 Collection<Item> children = getChildren(getJcrItem(itemId));
95 log.debug("Fetched {} children in {}ms", children.size(), System.currentTimeMillis() - start);
96 return createContainerIds(children);
97 }
98
99 @Override
100 public JcrItemId getParent(Object itemId) {
101 try {
102 Item item = getJcrItem(itemId);
103 int itemDepth = item.getDepth();
104
105 if (itemDepth == 0) {
106 return null;
107 }
108
109 if (item.isNode() && itemDepth == getRootDepth() + 1) {
110 return null;
111 }
112 return JcrItemUtil.getItemId(item.getParent());
113 } catch (RepositoryException e) {
114 handleRepositoryException(log, "Cannot determine parent for itemId: " + itemId, e);
115 return null;
116 }
117 }
118
119 @Override
120 public Collection<JcrItemId> rootItemIds() {
121 try {
122 return createContainerIds(getRootItemIds());
123 } catch (RepositoryException e) {
124 handleRepositoryException(log, "Cannot retrieve root item id's", e);
125 return Collections.emptySet();
126 }
127 }
128
129 @Override
130 public void refresh() {
131 resetOffset();
132 clearItemIndexes();
133 fireItemSetChange();
134 }
135
136 @Override
137 public boolean setParent(Object itemId, Object newParentId) throws UnsupportedOperationException {
138 fireItemSetChange();
139 return true;
140 }
141
142 @Override
143 public boolean areChildrenAllowed(Object itemId) {
144 final JcrItemAdapter./../../info/magnolia/ui/vaadin/integration/jcr/JcrItemAdapter.html#JcrItemAdapter">JcrItemAdapter item = ((JcrItemAdapter) getItem(itemId));
145 if (item == null) {
146 return false;
147 }
148 return item.isNode() && hasChildren(itemId);
149 }
150
151 @Override
152 public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) throws UnsupportedOperationException {
153 throw new UnsupportedOperationException();
154 }
155
156 @Override
157 public boolean isRoot(Object itemId) {
158 try {
159 return isRoot(getJcrItem(itemId));
160 } catch (RepositoryException e) {
161 handleRepositoryException(log, "Cannot determine whether item is root - itemId: " + itemId, e);
162 return true;
163 }
164 }
165
166 @Override
167 public boolean hasChildren(Object itemId) {
168 final Item item = getJcrItem(itemId);
169 if (item.isNode()) {
170 final Node node = (Node) item;
171 try {
172 final NodeIterator it = node.getNodes();
173 while (it.hasNext()) {
174 if (isNodeVisible(it.nextNode())) {
175 return true;
176 }
177 }
178 } catch (RepositoryException e) {
179 log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
180 }
181
182
183 if (getConfiguration().isIncludeProperties()) {
184 try {
185 final PropertyIterator propertyIterator = node.getProperties();
186 while (propertyIterator.hasNext()) {
187 final Property property = propertyIterator.nextProperty();
188 if (isIncludingSystemProperties || !isJcrOrMgnlProperty(property))
189 return true;
190 }
191 } catch (RepositoryException e) {
192 log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
193 }
194 }
195 }
196 return false;
197 }
198
199 @Override
200 public void sort(final Object[] propertyId, final boolean[] ascending) {
201 sorters.clear();
202 for (int i = 0; i < propertyId.length; i++) {
203 if (getSortableContainerPropertyIds().contains(String.valueOf(propertyId[i]))) {
204 OrderBycontainer/OrderBy.html#OrderBy">OrderBy orderBy = new OrderBy((String) propertyId[i], ascending[i]);
205 sorters.add(orderBy);
206 }
207 }
208 if (sorters.isEmpty()) {
209 fireItemSetChange();
210 } else {
211 fireItemSetChange(new ItemsSortedEvent(this));
212 }
213 }
214
215 public void setIncludeSystemProperties(boolean includingSystemProperties) {
216 boolean currentlyIncludingSystemProperties = this.isIncludingSystemProperties;
217 this.isIncludingSystemProperties = includingSystemProperties;
218
219 if (currentlyIncludingSystemProperties != includingSystemProperties) {
220 refresh();
221 }
222 }
223
224 public boolean isIncludingSystemProperties() {
225 return isIncludingSystemProperties;
226 }
227
228 private boolean isJcrOrMgnlProperty(Property property) throws RepositoryException {
229 final String propertyName = property.getName();
230 return propertyName.startsWith(NodeTypes.JCR_PREFIX) || propertyName.startsWith(NodeTypes.MGNL_PREFIX);
231 }
232
233 protected Collection<JcrItemId> createContainerIds(Collection<Item> children) {
234 ArrayList<JcrItemId> ids = new ArrayList<>();
235 for (Item child : children) {
236 try {
237 JcrItemId itemId = JcrItemUtil.getItemId(child);
238 ids.add(itemId);
239 } catch (RepositoryException e) {
240 handleRepositoryException(log, "Cannot retrieve currentId", e);
241 }
242 }
243 return ids;
244 }
245
246 public Collection<Item> getChildren(Item item) {
247 if (!item.isNode()) {
248 return Collections.emptySet();
249 }
250
251 Node node = (Node) item;
252 ArrayList<Item> items = new ArrayList<Item>();
253 try {
254 NodeIterator iterator;
255
256
257 if (isSortable() && (!sorters.isEmpty() || StringUtils.isNotBlank(getConfiguration().getDefaultOrder()))) {
258 nodePath = node.getPath();
259 String query = constructJCRQuery(true);
260 iterator = QueryUtil.search(getWorkspace(), query, Query.JCR_SQL2);
261 } else {
262 iterator = node.getNodes();
263 }
264
265 while (iterator.hasNext()) {
266 Node next = iterator.nextNode();
267 if (isNodeVisible(next)) {
268 items.add(next);
269 }
270 }
271
272 if (getConfiguration().isIncludeProperties()) {
273 ArrayList<Property> properties = new ArrayList<Property>();
274 PropertyIterator propertyIterator = node.getProperties();
275 while (propertyIterator.hasNext()) {
276 final Property property = propertyIterator.nextProperty();
277 if (isIncludingSystemProperties || !isJcrOrMgnlProperty(property)) {
278 properties.add(property);
279 }
280 }
281 ItemNameComparator itemNameComparator = new ItemNameComparator();
282 Collections.sort(properties, itemNameComparator);
283 items.addAll(properties);
284 }
285 } catch (RepositoryException e) {
286 handleRepositoryException(log, "Could not retrieve children", e);
287 }
288
289 return Collections.unmodifiableCollection(items);
290 }
291
292 protected boolean isNodeVisible(Node node) throws RepositoryException {
293
294 if (!getConfiguration().isIncludeSystemNodes() && node.getName().startsWith("jcr:") || node.getName().startsWith("rep:")) {
295 return false;
296 }
297
298 String primaryNodeTypeName = node.getPrimaryNodeType().getName();
299 for (NodeTypeDefinition nodeTypeDefinition : getConfiguration().getNodeTypes()) {
300 if (nodeTypeDefinition.isStrict()) {
301 if (primaryNodeTypeName.equals(nodeTypeDefinition.getName())) {
302 return true;
303 }
304 } else if (NodeUtil.isNodeType(node, nodeTypeDefinition.getName())) {
305 return true;
306 }
307 }
308 return false;
309 }
310
311 public Collection<Item> getRootItemIds() throws RepositoryException {
312 return getChildren(getRootNode());
313 }
314
315
316
317
318
319 public boolean isRoot(Item item) throws RepositoryException {
320 if (item != null) {
321 try {
322 return item.getDepth() == getRootDepth() + 1;
323 } catch (RepositoryException e) {
324 handleRepositoryException(log, "Cannot determine depth of jcr item", e);
325 }
326 }
327 return true;
328 }
329
330 private int getRootDepth() {
331 if (rootDepth == -1) {
332 try {
333 rootDepth = getRootNode().getDepth();
334 } catch (RepositoryException e) {
335 throw new RuntimeRepositoryException(e);
336 }
337 }
338 return rootDepth;
339 }
340
341 @Override
342 protected String getQueryWhereClause() {
343 String whereClause;
344 String clauseWorkspacePath = getQueryWhereClauseForNodePath(nodePath);
345 String clauseNodeTypes = getQueryWhereClauseNodeTypes();
346 if (StringUtils.isNotBlank(clauseNodeTypes)) {
347 whereClause = " where ((" + clauseNodeTypes + ") ";
348 if (StringUtils.isNotBlank(clauseWorkspacePath)) {
349 whereClause += "and " + clauseWorkspacePath;
350 }
351 whereClause += ") ";
352 } else {
353 whereClause = " where ";
354 }
355
356 log.debug("JCR query WHERE clause is {}", whereClause);
357 return whereClause;
358 }
359
360 private String getQueryWhereClauseForNodePath(final String nodePath) {
361 return String.format(WHERE_CLAUSE_FOR_PATH, nodePath);
362 }
363
364 @Override
365 protected String getQueryWhereClauseNodeTypes() {
366 List<String> defs = new ArrayList<String>();
367 for (NodeType nt : getSearchableNodeTypes()) {
368 if (nt.isMixin()) {
369
370 defs.add("[jcr:mixinTypes] = '" + nt.getName() + "'");
371 } else {
372 defs.add("[jcr:primaryType] = '" + nt.getName() + "'");
373 }
374 }
375 return StringUtils.join(defs, " or ");
376 }
377
378
379
380
381 String getPathInTree(Item item) throws RepositoryException {
382 String base = getConfiguration().getRootPath();
383 return "/".equals(base) ? item.getPath() : StringUtils.substringAfter(item.getPath(), base);
384 }
385
386 private Session getSession() throws RepositoryException {
387 return MgnlContext.getJCRSession(getWorkspace());
388 }
389
390 protected Node getRootNode() throws RepositoryException {
391 return getSession().getNode(getConfiguration().getRootPath());
392 }
393
394 public boolean isSortable() {
395 return sortable;
396 }
397
398 public void setSortable(boolean sortable) {
399 this.sortable = sortable;
400 }
401
402 private static class ItemNameComparator implements Comparator<Item> {
403
404 @Override
405 public int compare(Item lhs, Item rhs) {
406 try {
407 return lhs.getName().compareTo(rhs.getName());
408 } catch (RepositoryException e) {
409 log.warn("Cannot compare item names: " + e);
410 return 0;
411 }
412 }
413 }
414
415
416
417
418 protected class ItemsSortedEvent implements Container.ItemSetChangeEvent {
419
420 Container container;
421
422 public ItemsSortedEvent(Container container) {
423 super();
424 this.container = container;
425 }
426
427 @Override
428 public Container getContainer() {
429 return container;
430 }
431 }
432 }