View Javadoc

1   /**
2    * This file Copyright (c) 2009-2010 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.nodebuilder;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.core.ItemType;
38  import info.magnolia.cms.core.NodeData;
39  import info.magnolia.cms.util.ContentUtil;
40  import info.magnolia.cms.util.NodeDataUtil;
41  import info.magnolia.cms.util.NodeTypeFilter;
42  
43  import javax.jcr.ItemExistsException;
44  import javax.jcr.ItemNotFoundException;
45  import javax.jcr.RepositoryException;
46  import javax.jcr.Value;
47  
48  /**
49   * Factory methods for most common NodeOperation implementations.
50   *
51   * @author gjoseph
52   * @version $Revision: $ ($Author: $)
53   */
54  public abstract class Ops {
55      public static NodeOperation addNode(final String name) {
56          return new AbstractOp() {
57              Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
58                  return context.createContent(name);
59              }
60          };
61      }
62  
63      public static NodeOperation addNode(final String name, final String type) {
64          return new AbstractOp() {
65              Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
66                  return context.createContent(name, type);
67              }
68          };
69      }
70  
71      public static NodeOperation addNode(final String name, final ItemType type) {
72          return new AbstractOp() {
73              Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
74                  return context.createContent(name, type);
75              }
76          };
77      }
78  
79      public static NodeOperation getNode(final String name) {
80          return new AbstractOp() {
81              Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
82                  return context.getContent(name);
83              }
84          };
85      }
86  
87      /**
88       * Can remove either a node or property.
89       */
90      public static NodeOperation remove(final String name) {
91          return new AbstractOp() {
92              Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
93                  context.delete(name);
94                  return context;
95              }
96          };
97      }
98  
99      /**
100      * Adds a currently non-existing property. Throws an ItemExistsException if the property already exists.
101      */
102     public static NodeOperation addProperty(final String name, final Object value) {
103         return new AbstractOp() {
104             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
105                 if (context.hasNodeData(name)) {
106                     // throw new ItemExistsException("Property " + name + " already exists at " + context.getHandle());
107                     throw new ItemExistsException(name);
108                 }
109                 context.createNodeData(name, value);
110                 return context;
111             }
112         };
113     }
114 
115     /**
116      * Sets the value of an existing property, ignoring its current value.
117      * @throws ItemNotFoundException if the property does not exist.
118      */
119     public static NodeOperation setProperty(final String name, final Object newValue) {
120         return new AbstractOp() {
121             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
122                 if (!context.hasNodeData(name)) {
123                     throw new ItemNotFoundException(name);
124                 }
125                 final Value value = NodeDataUtil.createValue(newValue, context.getJCRNode().getSession().getValueFactory());
126                 context.setNodeData(name, value);
127                 return context;
128             }
129         };
130     }
131 
132     /**
133      * Sets the value of an existing property, only if the actual current value matches the given expected current value.
134      * @throws ItemNotFoundException if the property does not exist.
135      * @throws RepositoryException if the current value does not match the expected one.
136      */
137     public static NodeOperation setProperty(final String name, final Object expectedCurrentValue, final Object newValue) {
138         return new AbstractOp() {
139             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
140                 if (!context.hasNodeData(name)) {
141                     throw new ItemNotFoundException(name);
142                 }
143                 final NodeData current = context.getNodeData(name);
144                 if (!expectedCurrentValue.equals(NodeDataUtil.getValueObject(current))) {
145                     errorHandler.report("Expected " + expectedCurrentValue + " at " + current.getHandle() + " but found " + current.getString() + " instead; can't set value to " + newValue + ".");
146                     return context;
147                 }
148 
149                 final Value value = NodeDataUtil.createValue(newValue, context.getJCRNode().getSession().getValueFactory());
150                 current.setValue(value);
151                 return context;
152             }
153         };
154     }
155 
156     /**
157      * Renames the node defined by the name parameter.
158      */
159     public static NodeOperation renameNode(final String name, final String newName) {
160         return new AbstractOp() {
161             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
162                 ContentUtil.rename(context.getContent(name), newName);
163                 return context;
164             }
165         };
166     }
167 
168     /**
169      * Renames a property by creating a new one and copying the value.
170      */
171     public static NodeOperation renameProperty(final String name, final String newName) {
172         return new AbstractOp() {
173             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
174                 Object value = context.getNodeData(name);
175                 context.createNodeData(newName, value);
176                 context.deleteNodeData(name);
177                 return context;
178             }
179         };
180     }
181 
182     /**
183      * Moves the node defined by the name parameter in the session 
184      */
185     public static NodeOperation moveNode(final String name, final String dest) {
186         return new AbstractOp() {
187             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
188                 ContentUtil.moveInSession(context.getContent(name), dest);
189                 return context;
190             }
191         };
192     }
193 
194     /**
195      * Copies the node defined by the name parameter in the session. 
196      */
197     public static NodeOperation copyNode(final String name, final String dest) {
198         return new AbstractOp() {
199             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
200                 ContentUtil.copyInSession(context.getContent(name), dest);
201                 return context;
202             }
203         };
204     }
205 
206     /**
207      * Executes the operation for each child node excluding meta data and jcr base node.
208      */
209     public static NodeOperation onChildNodes(final NodeOperation... childrenOps) {
210         return onChildNodes(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER, childrenOps);
211     }
212 
213     /**
214      * Executes the operation for each child node of a certain type.
215      */
216     public static NodeOperation onChildNodes(final String type, final NodeOperation... childrenOps) {
217         return onChildNodes(new NodeTypeFilter(type), childrenOps);
218     }
219 
220     /**
221      * Executes the operation for each child node of a certain type.
222      */
223     public static NodeOperation onChildNodes(final ItemType type, final NodeOperation... childrenOps) {
224         return onChildNodes(new NodeTypeFilter(type), childrenOps);
225     }
226 
227     /**
228      * Executes the operation for each child node matching the filter.
229      */
230     public static NodeOperation onChildNodes(final Content.ContentFilter filter, final NodeOperation... childrenOps) {
231         return new AbstractOp() {
232             // TODO shouldn't this implement NodeOperation directly instead? it has no business doing with the then() method anyway
233             Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException {
234                 for (Content subNode : context.getChildren(filter)) {
235                     for (NodeOperation nodeOperation : childrenOps) {
236                         nodeOperation.exec(subNode, errorHandler);
237                     }
238                 }
239                 return context;
240             }
241         };
242     }
243 
244     /**
245      * No operation; can be useful in ternary expression, for instance.
246      */
247     public static NodeOperation noop() {
248         return new NodeOperation() {
249             public NodeOperation then(NodeOperation... childrenOps) {
250                 return null;
251             }
252 
253             public void exec(Content context, ErrorHandler errorHandler) {
254             }
255         };
256     }
257 
258     abstract static class AbstractOp implements NodeOperation {
259         private NodeOperation[] childrenOps = {};
260 
261         public void exec(Content context, ErrorHandler errorHandler) {
262             try {
263                 context = doExec(context, errorHandler);
264             } catch (RepositoryException e) {
265                 errorHandler.handle(e, context);
266             }
267 
268             for (NodeOperation childrenOp : childrenOps) {
269                 childrenOp.exec(context, errorHandler);
270             }
271 
272         }
273 
274         /**
275          * @return the node that should now be used as the context for subsequent operations
276          */
277         abstract Content doExec(Content context, ErrorHandler errorHandler) throws RepositoryException;
278 
279         public NodeOperation then(NodeOperation... childrenOps) {
280             this.childrenOps = childrenOps;
281             return this;
282         }
283     }
284 }