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