View Javadoc

1   /**
2    * This file Copyright (c) 2011-2012 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.jcr.wrapper;
35  
36  import info.magnolia.cms.core.MgnlNodeType;
37  import info.magnolia.jcr.RuntimeRepositoryException;
38  import info.magnolia.jcr.iterator.ChainedNodeIterator;
39  import info.magnolia.jcr.iterator.FilteringNodeIterator;
40  import info.magnolia.jcr.predicate.AbstractPredicate;
41  
42  import java.util.ArrayList;
43  import java.util.List;
44  
45  import javax.jcr.Node;
46  import javax.jcr.NodeIterator;
47  import javax.jcr.PathNotFoundException;
48  import javax.jcr.Property;
49  import javax.jcr.RepositoryException;
50  
51  import org.apache.commons.lang.StringUtils;
52  
53  
54  /**
55   * This wrapper inherits nodes from the parent hierarchy. The method {@link #isAnchor()} defines
56   * the anchor to which the inheritance is performed relative to. By default the anchor is of type
57   * (mgnl:content).
58   * <p>
59   * The inheritance is then performed as follows:
60   * <ul>
61   * <li>try to get the node directly</li>
62   * <li>find next anchor</li>
63   * <li>try to get the node from the anchor</li>
64   * <li>repeat until no anchor can be found anymore (root)</li>
65   * </ul>
66   * <p>
67   * The {@link #getNodes()} and {@link #getNodes(String)} methods merge the direct and inherited children by first adding the
68   * inherited children to the iterator and then the direct children.
69   *
70   * @deprecated since 4.5.9 use {@link InheritanceContentDecorator}
71   */
72  public class InheritanceNodeWrapper extends ChildWrappingNodeWrapper {
73  
74      private final Node start;
75      private final AbstractPredicate<Node> filter;
76  
77      public InheritanceNodeWrapper(Node node) {
78          this(node, node);
79      }
80  
81      public InheritanceNodeWrapper(Node node, AbstractPredicate<Node> filter) {
82          this(node, node, filter);
83      }
84  
85      public InheritanceNodeWrapper(Node node, Node start, AbstractPredicate<Node> filter) {
86          super(node);
87          this.start = start;
88          this.filter = filter;
89      }
90  
91      public InheritanceNodeWrapper(Node node, Node start) {
92          super(node);
93          this.start = start;
94          this.filter = new AbstractPredicate<Node>() {
95  
96              @Override
97              public boolean evaluateTyped(Node t) {
98                  return true;
99              }
100         };
101     }
102 
103     @Override
104     //InheritanceNodeWrapper has to allow multilevel inheritance
105     public void setWrappedNode(Node node) {
106         this.wrapped = node;
107     }
108 
109     /**
110      * Find the anchor for this node.
111      */
112     protected InheritanceNodeWrapper findAnchor() throws RepositoryException{
113         if(this.getDepth() == 0){
114             return null;
115         }
116         if(isAnchor()){
117             return this;
118         }
119         // until the current node is the anchor
120         return ((InheritanceNodeWrapper)wrapNode(this.getParent())).findAnchor();
121     }
122 
123     /**
124      * Find next anchor.
125      */
126     protected InheritanceNodeWrapper findNextAnchor() throws RepositoryException{
127         final InheritanceNodeWrapper currentAnchor = findAnchor();
128         if(currentAnchor != null && this.getDepth() >0){
129             return ((InheritanceNodeWrapper)wrapNode(currentAnchor.getParent())).findAnchor();
130         }
131         return null;
132     }
133 
134     /**
135      * True if this node is an anchor. By default true if this node is of type {@link MgnlNodeType#NT_CONTENT}.
136      */
137     protected boolean isAnchor() {
138         try {
139             return this.isNodeType(MgnlNodeType.NT_CONTENT);
140         } catch (RepositoryException e) {
141             throw new RuntimeRepositoryException(e);
142         }
143     }
144 
145     /**
146      * This method returns null if no node has been found.
147      */
148     protected Node getNodeSafely(String relPath) throws RepositoryException {
149         if(getWrappedNode().hasNode(relPath)) {
150             return wrapNode(getWrappedNode().getNode(relPath));
151         }
152 
153         String innerPath = resolveInnerPath() + "/" + relPath;
154         innerPath = StringUtils.removeStart(innerPath,"/");
155 
156         Node inherited = getNodeSafely(findNextAnchor(), innerPath);
157         return inherited;
158     }
159 
160     /**
161      * Returns the inner path of the this node up to the anchor.
162      */
163     protected String resolveInnerPath() throws RepositoryException {
164         final String path;
165         InheritanceNodeWrapper anchor = findAnchor();
166         // if no anchor left we are relative to the root
167         if(anchor == null){
168             path = this.getPath();
169         }
170         else{
171             path = StringUtils.substringAfter(this.getPath(), anchor.getPath());
172         }
173         return StringUtils.removeStart(path,"/");
174     }
175 
176     /**
177      * This method returns null if no node has been found.
178      */
179     protected Node getNodeSafely(InheritanceNodeWrapper anchor, String path) throws RepositoryException{
180         if(anchor == null){
181             return null;
182         }
183         if(StringUtils.isEmpty(path)){
184             return anchor;
185         }
186         return anchor.getNodeSafely(path);
187     }
188 
189 
190     @Override
191     public boolean hasNode(String relPath) throws RepositoryException {
192         return getNodeSafely(relPath) != null;
193     }
194 
195     @Override
196     public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
197         Node inherited = getNodeSafely(relPath);
198         if (inherited == null || !filter.evaluateTyped(inherited)) {
199             throw new PathNotFoundException("Can't inherit a node [" + relPath + "] on node [" + getWrappedNode().getPath() + "]");
200         }
201         return wrapNode(inherited);
202     }
203 
204     @Override
205     public NodeIterator getNodes() throws RepositoryException {
206         List<NodeIterator> nodes = new ArrayList<NodeIterator>();
207 
208         // add inherited children
209         try {
210             Node inherited = getNodeSafely(findNextAnchor(), resolveInnerPath());
211             if(inherited != null && !inherited.getPath().startsWith(this.getPath())){
212                 nodes.add(inherited.getNodes());
213             }
214         }
215         catch (RepositoryException e) {
216             throw new RuntimeException("Can't inherit children from " + getWrappedNode(), e);
217         }
218         // add direct children
219         nodes.add(getWrappedNode().getNodes());
220 
221         return wrapNodeIterator(new FilteringNodeIterator(new ChainedNodeIterator(nodes), filter));
222     }
223 
224     @Override
225     public NodeIterator getNodes(String namePattern) throws RepositoryException {
226         List<NodeIterator> nodes = new ArrayList<NodeIterator>();
227 
228         // add inherited children
229         try {
230             Node inherited = getNodeSafely(findNextAnchor(), resolveInnerPath());
231             if(inherited != null && !inherited.getPath().startsWith(this.getPath())){
232                 nodes.add(inherited.getNodes(namePattern));
233             }
234         }
235         catch (RepositoryException e) {
236             throw new RepositoryException("Can't inherit children from " + getWrappedNode(), e);
237         }
238         // add direct children
239         nodes.add(getWrappedNode().getNodes(namePattern));
240 
241         return wrapNodeIterator(new FilteringNodeIterator(new ChainedNodeIterator(nodes), filter));
242     }
243 
244     @Override
245     public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
246         try {
247             if (getWrappedNode().hasProperty(relPath)) {
248                 return getWrappedNode().getProperty(relPath);
249             }
250             Node inherited = getNodeSafely(findNextAnchor(), resolveInnerPath());
251             if(inherited != null){
252                 return inherited.getProperty(relPath);
253             } else {
254                 throw new PathNotFoundException("No property exists at " + relPath + " or current Session does not have read access to it.");
255             }
256         }
257         catch (RepositoryException e) {
258             throw new RepositoryException("Can't inherit property " + relPath + "  for " + getWrappedNode(), e);
259         }
260     }
261 
262     @Override
263     public boolean hasProperty(String name) throws RepositoryException {
264         try {
265             if (getWrappedNode().hasProperty(name)) {
266                 return true;
267             }
268             Node inherited = getNodeSafely(findNextAnchor(), resolveInnerPath());
269             if (inherited != null) {
270                 return inherited.hasProperty(name);
271             }
272         } catch (RepositoryException e) {
273             throw new RuntimeException("Can't inherit nodedata " + name + "  for " + getWrappedNode(), e);
274 
275         }
276         // creates a none existing node data in the standard manner
277         return super.hasProperty(name);
278     }
279 
280     /**
281      * True if this is not a sub node of the starting point.
282      */
283     public boolean isInherited() {
284         try {
285             return !this.getPath().startsWith(start.getPath());
286         } catch (RepositoryException e) {
287             throw new RuntimeRepositoryException(e);
288         }
289     }
290 
291     @Override
292     public Node wrapNode(Node node) {
293         if(node instanceof InheritanceNodeWrapper) {
294             return node;
295         }
296         return new InheritanceNodeWrapper(node, start, filter);
297     }
298 }