View Javadoc
1   /**
2    * This file Copyright (c) 2009-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.jcr.nodebuilder;
35  
36  import info.magnolia.jcr.iterator.FilteringNodeIterator;
37  import info.magnolia.jcr.predicate.AbstractPredicate;
38  import info.magnolia.jcr.util.NodeUtil;
39  import info.magnolia.jcr.util.PropertyUtil;
40  
41  import java.util.ArrayList;
42  import java.util.List;
43  
44  import javax.jcr.ItemExistsException;
45  import javax.jcr.ItemNotFoundException;
46  import javax.jcr.Node;
47  import javax.jcr.RepositoryException;
48  import javax.jcr.Value;
49  
50  import org.apache.commons.collections.CollectionUtils;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  
54  /**
55   * Factory methods for most common NodeOperation implementations.
56   */
57  public abstract class Ops {
58      public static NodeOperation addNode(final String name) {
59          return new AbstractNodeOperation() {
60              @Override
61              protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
62                  // TODO dlipp: Caution - addNode does not create/update Metadata (in contrast to Content#createContent! To be checked!
63                  return context.addNode(name);
64              }
65          };
66      }
67  
68      public static NodeOperation addNode(final String name, final String type) {
69          return new AbstractNodeOperation() {
70              @Override
71              protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
72                  return context.addNode(name, type);
73              }
74          };
75      }
76  
77      public static NodeOperation getNode(final String name) {
78          return new AbstractNodeOperation() {
79              @Override
80              protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
81                  return context.getNode(name);
82              }
83          };
84      }
85  
86      /**
87       * Adds property. Throws an ItemExistsException if the property already exists.
88       */
89      public static NodeOperation addProperty(final String name, final String newValue) {
90          return new AbstractNodeOperation() {
91              @Override
92              protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
93                  if (context.hasProperty(name)) {
94                      throw new ItemExistsException("Property " + name + " already exists at " + context.getPath());
95                  }
96                  final Value value = PropertyUtil.createValue(newValue, context.getSession().getValueFactory());
97                  context.setProperty(name, value);
98                  return context;
99              }
100         };
101     }
102 
103     /**
104      * Sets the value of an existing property, ignoring its current value.
105      * @throws ItemNotFoundException if the property does not exist.
106      */
107     public static NodeOperation setProperty(final String name, final Object newValue) {
108         return new AbstractNodeOperation() {
109             @Override
110             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
111                 if (!context.hasProperty(name)) {
112                     throw new ItemNotFoundException(name);
113                 }
114                 final Value value = PropertyUtil.createValue(newValue, context.getSession().getValueFactory());
115                 context.setProperty(name, value);
116                 return context;
117             }
118         };
119     }
120 
121     /**
122      * Renames a node.
123      */
124     public static NodeOperation renameNode(final String currentName, final String newName) {
125         return new AbstractNodeOperation() {
126             @Override
127             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
128                 NodeUtil.renameNode(context.getNode(currentName), newName);
129                 return context;
130             }
131         };
132     }
133 
134     /**
135      * Renames a property by creating a new one and copying the value.
136      */
137     public static NodeOperation renameProperty(final String name, final String newName) {
138         return new AbstractNodeOperation() {
139             @Override
140             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
141                 if (!context.hasProperty(name)) {
142                     throw new ItemNotFoundException(name);
143                 }
144                 if (context.hasProperty(newName)) {
145                     //throw new ItemExistsException("Property " + newName + " already exists at " + context.getHandle());
146                     throw new ItemExistsException(newName);
147                 }
148                 final Value value = context.getProperty(name).getValue();
149                 context.setProperty(newName, value);
150                 context.getProperty(name).remove();
151                 return context;
152             }
153         };
154     }
155 
156     /**
157      * Moves a node, using session-scoped operation.
158      */
159     public static NodeOperation moveNode(final String nodeName, final String dest) {
160         return new AbstractNodeOperation() {
161             @Override
162             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
163                 NodeUtil.moveNode(context.getNode(nodeName), context.getNode(dest));
164                 return context;
165             }
166         };
167     }
168 
169     /**
170      * No operation; can be useful in ternary expression, for instance.
171      */
172     public static NodeOperation noop() {
173         return new NodeOperation() {
174             @Override
175             public NodeOperation then(NodeOperation... childrenOps) {
176                 return null;
177             }
178 
179             @Override
180             public void exec(Node context, ErrorHandler errorHandler) {
181             }
182         };
183     }
184 
185     /**
186      * Gets or adds node depending on whether it existed or not.
187      */
188     public static final NodeOperation getOrAddNode(final String name, final String nodeType) {
189         return new AbstractNodeOperation() {
190 
191             @Override
192             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
193                 return (context.hasNode(name)) ? context.getNode(name) : context.addNode(name, nodeType);
194             }
195         };
196     }
197 
198     /**
199      * Executes then() ops on all child nodes. Use with care as it might result in modification of more nodes then intended.
200      */
201     public static final NodeOperation onChildNodes() {
202         return onChildNodes(new AbstractPredicate<Node>() {
203             @Override
204             public boolean evaluateTyped(Node t) {
205                 return true;
206             }
207         });
208     }
209 
210     /**
211      * Executes then() ops on all child nodes that are evaluated true by the predicate.
212      */
213     public static final NodeOperation onChildNodes(final AbstractPredicate<Node> predicate) {
214         return new AbstractNodeOperation() {
215             // yes we do want to log into ANO logger
216             private final Logger log = LoggerFactory.getLogger(AbstractNodeOperation.class);
217 
218             private List<NodeOperation> childrenOps = new ArrayList<NodeOperation>();
219 
220             @Override
221             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
222                 return context;
223             }
224 
225             @Override
226             public void exec(Node context, ErrorHandler errorHandler) {
227                 try {
228                     FilteringNodeIterator iter = new FilteringNodeIterator(context.getNodes(), predicate);
229                     while (iter.hasNext()) {
230                         for (NodeOperation childrenOp : childrenOps) {
231                             childrenOp.exec(iter.nextNode(), errorHandler);
232                         }
233                     }
234                 } catch (RepositoryException e) {
235                     try {
236                         errorHandler.handle(e, context);
237                     } catch (RepositoryException e1) {
238                         log.warn("Could not handle original exception {} because of: ", e.getMessage(), e1);
239                     }
240                 }
241 
242             }
243 
244             @Override
245             public NodeOperation then(NodeOperation... childrenOps) {
246                 // add the operations to allow multiple calls on the method.
247                 CollectionUtils.addAll(this.childrenOps, childrenOps);
248                 return this;
249             }
250 
251         };
252     }
253 
254     /**
255      * Executes all then() defined ops only when passed in flag is evaluated true, behaves as noop otherwise.
256      * This is useful to switch off/on blocks of bigger operation chains without having to split them into too many small chunks.
257      */
258     public static final NodeOperation ifTrue(final boolean bool) {
259         return new AbstractNodeOperation() {
260 
261             private List<NodeOperation> childrenOps = new ArrayList<NodeOperation>();
262 
263             @Override
264             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
265                 return context;
266             }
267 
268             @Override
269             public void exec(Node context, ErrorHandler errorHandler) {
270                 if (bool) {
271                     for (NodeOperation childrenOp : childrenOps) {
272                         childrenOp.exec(context, errorHandler);
273                     }
274                 }
275             }
276 
277             @Override
278             public NodeOperation then(NodeOperation... childrenOps) {
279                 // add the operations to allow multiple calls on the method.
280                 CollectionUtils.addAll(this.childrenOps, childrenOps);
281                 return this;
282             }
283 
284         };
285     }
286 }