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.collections4.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      *
106      * @throws ItemNotFoundException if the property does not exist.
107      */
108     public static NodeOperation setProperty(final String name, final Object newValue) {
109         return new AbstractNodeOperation() {
110             @Override
111             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
112                 if (!context.hasProperty(name)) {
113                     throw new ItemNotFoundException(name);
114                 }
115                 final Value value = PropertyUtil.createValue(newValue, context.getSession().getValueFactory());
116                 context.setProperty(name, value);
117                 return context;
118             }
119         };
120     }
121 
122     /**
123      * Renames a node.
124      */
125     public static NodeOperation renameNode(final String currentName, final String newName) {
126         return new AbstractNodeOperation() {
127             @Override
128             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
129                 NodeUtil.renameNode(context.getNode(currentName), newName);
130                 return context;
131             }
132         };
133     }
134 
135     /**
136      * Renames a property by creating a new one and copying the value.
137      */
138     public static NodeOperation renameProperty(final String name, final String newName) {
139         return new AbstractNodeOperation() {
140             @Override
141             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
142                 if (!context.hasProperty(name)) {
143                     throw new ItemNotFoundException(name);
144                 }
145                 if (context.hasProperty(newName)) {
146                     //throw new ItemExistsException("Property " + newName + " already exists at " + context.getHandle());
147                     throw new ItemExistsException(newName);
148                 }
149                 final Value value = context.getProperty(name).getValue();
150                 context.setProperty(newName, value);
151                 context.getProperty(name).remove();
152                 return context;
153             }
154         };
155     }
156 
157     /**
158      * Moves a node, using session-scoped operation.
159      */
160     public static NodeOperation moveNode(final String nodeName, final String dest) {
161         return new AbstractNodeOperation() {
162             @Override
163             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
164                 NodeUtil.moveNode(context.getNode(nodeName), context.getNode(dest));
165                 return context;
166             }
167         };
168     }
169 
170     /**
171      * No operation; can be useful in ternary expression, for instance.
172      */
173     public static NodeOperation noop() {
174         return new NodeOperation() {
175             @Override
176             public NodeOperation then(NodeOperation... childrenOps) {
177                 return null;
178             }
179 
180             @Override
181             public void exec(Node context, ErrorHandler errorHandler) {
182             }
183         };
184     }
185 
186     /**
187      * Gets or adds node depending on whether it existed or not.
188      */
189     public static final NodeOperation getOrAddNode(final String name, final String nodeType) {
190         return new AbstractNodeOperation() {
191 
192             @Override
193             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
194                 return (context.hasNode(name)) ? context.getNode(name) : context.addNode(name, nodeType);
195             }
196         };
197     }
198 
199     /**
200      * Executes then() ops on all child nodes. Use with care as it might result in modification of more nodes then intended.
201      */
202     public static final NodeOperation onChildNodes() {
203         return onChildNodes(new AbstractPredicate<Node>() {
204             @Override
205             public boolean evaluateTyped(Node t) {
206                 return true;
207             }
208         });
209     }
210 
211     /**
212      * Executes then() ops on all child nodes that are evaluated true by the predicate.
213      */
214     public static final NodeOperation onChildNodes(final AbstractPredicate<Node> predicate) {
215         return new AbstractNodeOperation() {
216             // yes we do want to log into ANO logger
217             private final Logger log = LoggerFactory.getLogger(AbstractNodeOperation.class);
218 
219             private List<NodeOperation> childrenOps = new ArrayList<NodeOperation>();
220 
221             @Override
222             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
223                 return context;
224             }
225 
226             @Override
227             public void exec(Node context, ErrorHandler errorHandler) {
228                 try {
229                     FilteringNodeIterator iter = new FilteringNodeIterator(context.getNodes(), predicate);
230                     while (iter.hasNext()) {
231                         for (NodeOperation childrenOp : childrenOps) {
232                             childrenOp.exec(iter.nextNode(), errorHandler);
233                         }
234                     }
235                 } catch (RepositoryException e) {
236                     try {
237                         errorHandler.handle(e, context);
238                     } catch (RepositoryException e1) {
239                         log.warn("Could not handle original exception {} because of: ", e.getMessage(), e1);
240                     }
241                 }
242 
243             }
244 
245             @Override
246             public NodeOperation then(NodeOperation... childrenOps) {
247                 // add the operations to allow multiple calls on the method.
248                 CollectionUtils.addAll(this.childrenOps, childrenOps);
249                 return this;
250             }
251 
252         };
253     }
254 
255     /**
256      * Executes all then() defined ops only when passed in flag is evaluated true, behaves as noop otherwise.
257      * This is useful to switch off/on blocks of bigger operation chains without having to split them into too many small chunks.
258      */
259     public static final NodeOperation ifTrue(final boolean bool) {
260         return new AbstractNodeOperation() {
261 
262             private List<NodeOperation> childrenOps = new ArrayList<NodeOperation>();
263 
264             @Override
265             protected Node doExec(Node context, ErrorHandler errorHandler) throws RepositoryException {
266                 return context;
267             }
268 
269             @Override
270             public void exec(Node context, ErrorHandler errorHandler) {
271                 if (bool) {
272                     for (NodeOperation childrenOp : childrenOps) {
273                         childrenOp.exec(context, errorHandler);
274                     }
275                 }
276             }
277 
278             @Override
279             public NodeOperation then(NodeOperation... childrenOps) {
280                 // add the operations to allow multiple calls on the method.
281                 CollectionUtils.addAll(this.childrenOps, childrenOps);
282                 return this;
283             }
284 
285         };
286     }
287 }