View Javadoc

1   /**
2    * This file Copyright (c) 2010-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.cms.core;
35  
36  import info.magnolia.cms.i18n.I18nContentSupportFactory;
37  import info.magnolia.cms.security.AccessDeniedException;
38  import info.magnolia.cms.security.PermissionUtil;
39  import info.magnolia.cms.util.NodeDataUtil;
40  import info.magnolia.cms.util.NodeTypeFilter;
41  import info.magnolia.context.MgnlContext;
42  
43  import java.io.InputStream;
44  import java.util.ArrayList;
45  import java.util.Calendar;
46  import java.util.Collection;
47  import java.util.Comparator;
48  
49  import javax.jcr.PathNotFoundException;
50  import javax.jcr.PropertyType;
51  import javax.jcr.RepositoryException;
52  import javax.jcr.Value;
53  import javax.jcr.Workspace;
54  
55  import org.apache.commons.lang.StringUtils;
56  
57  /**
58   * A base class by implementing some default behavior.
59   * A subclass must carefully implement {@link #newNodeDataInstance(String, int, boolean)},
60   * {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)} and
61   * {@link #getNodeDataCollection(String)}.
62   *
63   * @author pbaerfuss
64   * @version $Id$
65   */
66  public abstract class AbstractContent extends ContentHandler implements Content {
67      private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractContent.class);
68  
69      @Override
70      public Content createContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
71          return createContent(name, ItemType.CONTENT);
72      }
73  
74      @Override
75      public Content createContent(String name, ItemType contentType) throws PathNotFoundException, RepositoryException, AccessDeniedException {
76          return createContent(name, contentType.getSystemName());
77      }
78  
79      @Override
80      public NodeData createNodeData(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
81          return setNodeData(name, "");
82      }
83  
84      @Override
85      public NodeData createNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
86          return setNodeData(name, value);
87      }
88  
89      /**
90       * @deprecated
91       */
92      @Override
93      @Deprecated
94      public NodeData createNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
95          return setNodeData(name, value);
96      }
97  
98      /**
99       * @deprecated
100      */
101     @Override
102     @Deprecated
103     public NodeData createNodeData(String name, int type) throws PathNotFoundException, RepositoryException, AccessDeniedException {
104         // set some default values to create the property
105         switch (type) {
106         case PropertyType.STRING:
107             return setNodeData(name, StringUtils.EMPTY);
108         case PropertyType.BOOLEAN:
109             return setNodeData(name, Boolean.FALSE);
110         case PropertyType.DATE:
111             return setNodeData(name, Calendar.getInstance());
112         case PropertyType.LONG:
113             return setNodeData(name, Long.valueOf(0));
114         case PropertyType.DOUBLE:
115             return setNodeData(name, Double.valueOf(0.0));
116         default:
117             return newNodeDataInstance(name, type, true);
118         }
119     }
120 
121     /**
122      * @deprecated
123      */
124     @Override
125     @Deprecated
126     public NodeData createNodeData(String name, Object valueObj) throws RepositoryException {
127         return setNodeData(name, valueObj);
128     }
129 
130     /**
131      * {@inheritDoc}
132      * Delegates to {@link #newNodeDataInstance(String, int, boolean)} by setting the type to PropertyType.UNDEFINED. A subclass has to handle this by trying to determine the type if the node data exists. The reason for this is that implementations want to instantiate different node data classes per type
133      */
134     @Override
135     public NodeData getNodeData(String name) {
136         try {
137             // will try to determine the type if the node data exists, otherwise an non-mutable node data will be returned
138             return newNodeDataInstance(name, PropertyType.UNDEFINED, false);
139         }
140         catch(RepositoryException e){
141             throw new IllegalStateException("Can't instantiate node data " + name + " on node " + toString(), e);
142         }
143     }
144 
145     /**
146      * As defined in {@link Content#getNodeData(String)} this method always returns a node data object. If the type is {@link PropertyType#UNDEFINED} the implementation should check if the node data exists and determine the type to use.
147      *
148      * @param createIfNotExisting if false an empty non-mutable node data will be returned if the node data doesn't exist otherwise a mutable nodedata object is returned (depending on the type)
149      */
150     abstract public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException;
151 
152     /**
153      * Delegates to {@link NodeData#isExist()}.
154      */
155     @Override
156     public boolean hasNodeData(String name) throws RepositoryException {
157         return getNodeData(name).isExist();
158     }
159 
160     @Override
161     public NodeData setNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
162         NodeData nodeData = newNodeDataInstance(name, value.getType(), true);
163         nodeData.setValue(value);
164         return nodeData;
165     }
166 
167     @Override
168     public NodeData setNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
169         if(value.length == 0){
170             throw new IllegalArgumentException("Value array can't be empty");
171         }
172         NodeData nodeData = newNodeDataInstance(name, value[0].getType(), true);
173         nodeData.setValue(value);
174         return nodeData;
175     }
176 
177     @Override
178     public NodeData setNodeData(String name, boolean value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
179         NodeData nodeData = newNodeDataInstance(name, PropertyType.BOOLEAN, true);
180         nodeData.setValue(value);
181         return nodeData;
182     }
183 
184     @Override
185     public NodeData setNodeData(String name, long value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
186         NodeData nodeData = newNodeDataInstance(name, PropertyType.LONG, true);
187         nodeData.setValue(value);
188         return nodeData;
189     }
190 
191     @Override
192     public NodeData setNodeData(String name, double value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
193         NodeData nodeData = newNodeDataInstance(name, PropertyType.DOUBLE, true);
194         nodeData.setValue(value);
195         return nodeData;
196     }
197 
198     @Override
199     public NodeData setNodeData(String name, String value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
200         NodeData nodeData = newNodeDataInstance(name, PropertyType.STRING, true);
201         nodeData.setValue(value);
202         return nodeData;
203     }
204 
205     @Override
206     public NodeData setNodeData(String name, InputStream value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
207         NodeData nodeData = newNodeDataInstance(name, PropertyType.BINARY, true);
208         nodeData.setValue(value);
209         return nodeData;
210     }
211 
212     @Override
213     public NodeData setNodeData(String name, Calendar value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
214         NodeData nodeData = newNodeDataInstance(name, PropertyType.DATE, true);
215         nodeData.setValue(value);
216         return nodeData;
217     }
218 
219     @Override
220     public NodeData setNodeData(String name, Content value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
221         NodeData nodeData = newNodeDataInstance(name, PropertyType.REFERENCE, true);
222         nodeData.setValue(value);
223         return nodeData;
224     }
225 
226     /**
227      * Uses the {@link NodeDataUtil} to create and set the node data based on the object type.
228      */
229     @Override
230     public NodeData setNodeData(String name, Object value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
231         NodeData nodeData = newNodeDataInstance(name, NodeDataUtil.getJCRPropertyType(value), true);
232         NodeDataUtil.setValue(nodeData, value);
233         return nodeData;
234     }
235 
236     @Override
237     public void deleteNodeData(String name) throws PathNotFoundException, RepositoryException {
238         getNodeData(name).delete();
239     }
240 
241     /**
242      * {@inheritDoc}
243      * Delegates to {@link #getChildren(ItemType)} passing the current node's type.
244      */
245     @Override
246     public Collection<Content> getChildren() {
247         String type = null;
248 
249         try {
250             type = this.getNodeTypeName();
251         }
252         catch (RepositoryException re) {
253             throw new RuntimeException("Can't read type of node [" + toString() + "]", re);
254 
255         }
256         // fix all getChildren calls from the root node
257         if ("rep:root".equalsIgnoreCase(type)) {
258             type = ItemType.CONTENT.getSystemName();
259         }
260         // --------------------------------------------------
261         return this.getChildren(type);
262     }
263 
264     /**
265      * {@inheritDoc}
266      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
267      */
268     @Override
269     public Collection<Content> getChildren(ContentFilter filter) {
270         return getChildren(filter, null);
271     }
272 
273     /**
274      * {@inheritDoc}
275      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
276      */
277     @Override
278     public Collection<Content> getChildren(ItemType itemType) {
279         return getChildren(new NodeTypeFilter(itemType), null);
280     }
281 
282     /**
283      * {@inheritDoc}
284      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
285      */
286     @Override
287     public Collection<Content> getChildren(String contentType) {
288         return getChildren(new NodeTypeFilter(contentType), null);
289     }
290 
291     /**
292      * {@inheritDoc}
293      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
294      */
295     @Override
296     public Collection<Content> getChildren(final String contentType, final String namePattern) {
297         return getChildren(new NodeTypeFilter(contentType), namePattern, null);
298     }
299 
300     /**
301      * {@inheritDoc}
302      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
303      */
304     @Override
305     public Collection<Content> getChildren(ContentFilter filter, Comparator<Content> orderCriteria) {
306         return getChildren(filter, null, orderCriteria);
307     }
308 
309     /**
310      * @param namePattern ignored if null.
311      */
312     abstract public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria);
313 
314     /**
315      * @deprecated
316      */
317     @Override
318     @Deprecated
319     public Content getChildByName(String namePattern) {
320         Collection<Content> children = getChildren("nt:base", namePattern);
321         if (!children.isEmpty()) {
322             return children.iterator().next();
323         }
324         return null;
325     }
326 
327     @Override
328     public Collection<NodeData> getNodeDataCollection() {
329         return getNodeDataCollection(null);
330     }
331 
332     protected Collection<NodeData> getBinaryNodeDatas(String namePattern) throws RepositoryException {
333         Collection<NodeData> nodeDatas = new ArrayList<NodeData>();
334         Collection<Content> binaryNodes = getChildren(ItemType.NT_RESOURCE, namePattern);
335         for (Content binaryNode : binaryNodes) {
336             nodeDatas.add(newNodeDataInstance(binaryNode.getName(), PropertyType.BINARY, false));
337         }
338         return nodeDatas;
339     }
340 
341 
342     @Override
343     public boolean hasChildren() {
344         return (this.getChildren().size() > 0);
345     }
346 
347     @Override
348     public boolean hasChildren(String contentType) {
349         return (this.getChildren(contentType).size() > 0);
350     }
351 
352     @Override
353     public void delete(String path) throws RepositoryException {
354         if(isNodeData(path)){
355             deleteNodeData(path);
356         }
357         else{
358             getContent(path).delete();
359         }
360     }
361 
362     @Override
363     public boolean isNodeData(String path) throws AccessDeniedException, RepositoryException {
364         return hasNodeData(path);
365     }
366 
367     @Override
368     public String getTemplate() {
369         return this.getMetaData().getTemplate();
370     }
371 
372     @Override
373     public String getTitle() {
374         return I18nContentSupportFactory.getI18nSupport().getNodeData(this, "title").getString();
375     }
376 
377     @Override
378     public void updateMetaData() throws RepositoryException, AccessDeniedException {
379         MetaData md = this.getMetaData();
380         md.setModificationDate();
381         md.setAuthorId(MgnlContext.getUser().getName());
382     }
383 
384     @Override
385     public boolean isGranted(long permissions) {
386         // In case getHandle() was manipulated by the subclass (e.g. ContentVersion we need to make sure permissions are checked on the said path)
387         final String handle = getHandle();
388         try {
389             return PermissionUtil.isGranted(this.getJCRNode().getSession(), handle, permissions);
390         } catch (RepositoryException e) {
391             log.error("An error occurred while trying to access path " + handle + " with permissions " + permissions, e);
392             return false;
393         }
394     }
395 
396     @Override
397     public Workspace getWorkspace() throws RepositoryException {
398         return getJCRNode().getSession().getWorkspace();
399     }
400 
401     @Override
402     public String toString() {
403         String type = "";
404         String workspaceName = "";
405 
406         try {
407             workspaceName = getWorkspace() == null ? "null" : getWorkspace().getName();
408             type = getItemType().getSystemName();
409         }
410         catch (RepositoryException e) {
411             // ignore
412         }
413         StringBuilder builder = new StringBuilder();
414         builder.append(workspaceName);
415         builder.append(':').append(getHandle());
416         builder.append('[');
417         builder.append(type);
418         builder.append(']');
419 
420         return builder.toString();
421     }
422 
423 
424 }