View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (under your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.request;
25  
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.Iterator;
30  import java.util.LinkedList;
31  import java.util.List;
32  import org.modeshape.common.util.CheckArg;
33  import org.modeshape.common.util.HashCode;
34  import org.modeshape.graph.GraphI18n;
35  import org.modeshape.graph.Location;
36  import org.modeshape.graph.NodeConflictBehavior;
37  import org.modeshape.graph.property.Name;
38  import org.modeshape.graph.property.Path;
39  import org.modeshape.graph.property.Property;
40  
41  /**
42   * Instruction to create the node under the specified location. This command will create the node and set the initial properties.
43   */
44  public class CreateNodeRequest extends ChangeRequest implements Iterable<Property> {
45  
46      private static final long serialVersionUID = 1L;
47  
48      public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
49  
50      private final Location under;
51      private final String workspaceName;
52      private final Name childName;
53      private final List<Property> properties;
54      private final NodeConflictBehavior conflictBehavior;
55      private Location actualLocation;
56  
57      /**
58       * Create a request to create a node with the given properties under the supplied location.
59       * 
60       * @param parentLocation the location of the existing parent node, under which the new child should be created
61       * @param workspaceName the name of the workspace containing the parent
62       * @param childName the name of the new child to create under the existing parent
63       * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
64       *        properties} for the new node
65       * @throws IllegalArgumentException if the location, workspace name, or child name is null
66       */
67      public CreateNodeRequest( Location parentLocation,
68                                String workspaceName,
69                                Name childName,
70                                Property... properties ) {
71          this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
72      }
73  
74      /**
75       * Create a request to create a node with the given properties under the supplied location.
76       * 
77       * @param parentLocation the location of the existing parent node, under which the new child should be created
78       * @param workspaceName the name of the workspace containing the parent
79       * @param childName the name of the new child to create under the existing parent
80       * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
81       *        properties} for the new node
82       * @throws IllegalArgumentException if the location, workspace name, or child name is null
83       */
84      public CreateNodeRequest( Location parentLocation,
85                                String workspaceName,
86                                Name childName,
87                                Iterable<Property> properties ) {
88          this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
89      }
90  
91      /**
92       * Create a request to create a node with the given properties under the supplied location.
93       * 
94       * @param parentLocation the location of the existing parent node, under which the new child should be created
95       * @param workspaceName the name of the workspace containing the parent
96       * @param childName the name of the new child to create under the existing parent
97       * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
98       *        properties} for the new node
99       * @throws IllegalArgumentException if the location, workspace name, or child name is null
100      */
101     public CreateNodeRequest( Location parentLocation,
102                               String workspaceName,
103                               Name childName,
104                               Iterator<Property> properties ) {
105         this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
106     }
107 
108     /**
109      * Create a request to create a node with the given properties under the supplied location.
110      * 
111      * @param parentLocation the location of the existing parent node, under which the new child should be created
112      * @param workspaceName the name of the workspace containing the parent
113      * @param childName the name of the new child to create under the existing parent
114      * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
115      *        properties} for the new node
116      * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
117      *        location
118      * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
119      */
120     public CreateNodeRequest( Location parentLocation,
121                               String workspaceName,
122                               Name childName,
123                               NodeConflictBehavior conflictBehavior,
124                               Property... properties ) {
125         CheckArg.isNotNull(parentLocation, "parentLocation");
126         CheckArg.isNotNull(workspaceName, "workspaceName");
127         CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
128         CheckArg.isNotNull(childName, "childName");
129         this.under = parentLocation;
130         this.workspaceName = workspaceName;
131         this.childName = childName;
132         this.conflictBehavior = conflictBehavior;
133         int number = properties.length + (under.hasIdProperties() ? under.getIdProperties().size() : 0);
134         List<Property> props = new ArrayList<Property>(number);
135         for (Property property : properties) {
136             if (property != null) props.add(property);
137         }
138         this.properties = Collections.unmodifiableList(props);
139     }
140 
141     /**
142      * Create a request to create a node with the given properties under the supplied location.
143      * 
144      * @param parentLocation the location of the existing parent node, under which the new child should be created
145      * @param workspaceName the name of the workspace containing the parent
146      * @param childName the name of the new child to create under the existing parent
147      * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
148      *        properties} for the new node
149      * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
150      *        location
151      * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
152      */
153     public CreateNodeRequest( Location parentLocation,
154                               String workspaceName,
155                               Name childName,
156                               NodeConflictBehavior conflictBehavior,
157                               Iterable<Property> properties ) {
158         CheckArg.isNotNull(parentLocation, "parentLocation");
159         CheckArg.isNotNull(workspaceName, "workspaceName");
160         CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
161         CheckArg.isNotNull(childName, "childName");
162         this.under = parentLocation;
163         this.workspaceName = workspaceName;
164         this.childName = childName;
165         this.conflictBehavior = conflictBehavior;
166         List<Property> props = new LinkedList<Property>();
167         for (Property property : properties) {
168             if (property != null) props.add(property);
169         }
170         this.properties = Collections.unmodifiableList(props);
171     }
172 
173     /**
174      * Create a request to create a node with the given properties under the supplied location.
175      * 
176      * @param parentLocation the location of the existing parent node, under which the new child should be created
177      * @param workspaceName the name of the workspace containing the parent
178      * @param childName the name of the new child to create under the existing parent
179      * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
180      *        properties} for the new node
181      * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
182      *        location
183      * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
184      */
185     public CreateNodeRequest( Location parentLocation,
186                               String workspaceName,
187                               Name childName,
188                               NodeConflictBehavior conflictBehavior,
189                               Iterator<Property> properties ) {
190         CheckArg.isNotNull(parentLocation, "parentLocation");
191         CheckArg.isNotNull(workspaceName, "workspaceName");
192         CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
193         CheckArg.isNotNull(childName, "childName");
194         this.under = parentLocation;
195         this.workspaceName = workspaceName;
196         this.childName = childName;
197         this.conflictBehavior = conflictBehavior;
198         List<Property> props = new LinkedList<Property>();
199         while (properties.hasNext()) {
200             Property property = properties.next();
201             if (property != null) props.add(property);
202         }
203         this.properties = Collections.unmodifiableList(props);
204     }
205 
206     /**
207      * Get the location defining the parent of the new node that is to be created.
208      * 
209      * @return the location of the parent node; never null
210      */
211     public Location under() {
212         return under;
213     }
214 
215     /**
216      * Get the name of the workspace in which the node is to be createde
217      * 
218      * @return the name of the workspace; never null
219      */
220     public String inWorkspace() {
221         return workspaceName;
222     }
223 
224     /**
225      * Get the name for the new child.
226      * 
227      * @return the child's name; never null
228      */
229     public Name named() {
230         return childName;
231     }
232 
233     /**
234      * {@inheritDoc}
235      * 
236      * @see java.lang.Iterable#iterator()
237      */
238     public Iterator<Property> iterator() {
239         return this.properties.iterator();
240     }
241 
242     /**
243      * Get the properties for the node. If the node's {@link #under() location} has identification properties, the resulting
244      * properties will include the {@link Location#getIdProperties() identification properties}.
245      * 
246      * @return the collection of properties; never null
247      */
248     public Collection<Property> properties() {
249         return properties;
250     }
251 
252     /**
253      * Get the expected behavior when copying the branch and the {@link #under() destination} already has a node with the same
254      * name.
255      * 
256      * @return the behavior specification
257      */
258     public NodeConflictBehavior conflictBehavior() {
259         return conflictBehavior;
260     }
261 
262     /**
263      * {@inheritDoc}
264      * 
265      * @see org.modeshape.graph.request.Request#isReadOnly()
266      */
267     @Override
268     public boolean isReadOnly() {
269         return false;
270     }
271 
272     /**
273      * Sets the actual and complete location of the node being created. This method must be called when processing the request,
274      * and the actual location must have a {@link Location#getPath() path}.
275      * 
276      * @param actual the actual location of the node being created, or null if the {@link #under() current location} should be
277      *        used
278      * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the
279      *         {@link #under() current location}, or if the actual location does not have a path.
280      * @throws IllegalStateException if the request is frozen
281      */
282     public void setActualLocationOfNode( Location actual ) {
283         checkNotFrozen();
284         CheckArg.isNotNull(actual, "actual");
285         if (!under.equals(actual, false)) { // not same if actual is null
286         }
287         assert actual != null;
288         if (!actual.hasPath()) {
289             throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
290         }
291         assert actual.hasPath();
292         if (under.hasPath() && !under.getPath().equals(actual.getPath().getParent())) {
293             throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, under));
294         }
295         this.actualLocation = actual;
296     }
297 
298     /**
299      * Get the actual location of the node that was created.
300      * 
301      * @return the actual location, or null if the actual location was not set
302      */
303     public Location getActualLocationOfNode() {
304         return actualLocation;
305     }
306 
307     /**
308      * {@inheritDoc}
309      * 
310      * @see org.modeshape.graph.request.ChangeRequest#changes(java.lang.String, org.modeshape.graph.property.Path)
311      */
312     @Override
313     public boolean changes( String workspace,
314                             Path path ) {
315         return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
316     }
317 
318     /**
319      * {@inheritDoc}
320      * 
321      * @see org.modeshape.graph.request.ChangeRequest#changedLocation()
322      */
323     @Override
324     public Location changedLocation() {
325         return actualLocation != null ? actualLocation : under;
326     }
327 
328     /**
329      * {@inheritDoc}
330      * 
331      * @see org.modeshape.graph.request.ChangeRequest#changedWorkspace()
332      */
333     @Override
334     public String changedWorkspace() {
335         return workspaceName;
336     }
337 
338     /**
339      * {@inheritDoc}
340      * 
341      * @see org.modeshape.graph.request.Request#cancel()
342      */
343     @Override
344     public void cancel() {
345         super.cancel();
346         this.actualLocation = null;
347     }
348 
349     /**
350      * {@inheritDoc}
351      * 
352      * @see java.lang.Object#hashCode()
353      */
354     @Override
355     public int hashCode() {
356         return HashCode.compute(under, childName, workspaceName);
357     }
358 
359     /**
360      * {@inheritDoc}
361      * 
362      * @see java.lang.Object#equals(java.lang.Object)
363      */
364     @Override
365     public boolean equals( Object obj ) {
366         if (obj == this) return true;
367         if (this.getClass().isInstance(obj)) {
368             CreateNodeRequest that = (CreateNodeRequest)obj;
369             if (!this.under().isSame(that.under())) return false;
370             if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
371             if (!this.inWorkspace().equals(that.conflictBehavior())) return false;
372             if (!this.properties().equals(that.properties())) return false;
373             return true;
374         }
375         return false;
376     }
377 
378     /**
379      * {@inheritDoc}
380      * 
381      * @see java.lang.Object#toString()
382      */
383     @Override
384     public String toString() {
385         String parent = under() + "/";
386         if (under.hasPath() && under.getPath().isRoot()) parent = "/";
387         return "create in the \"" + workspaceName + "\" workspace the node \"" + parent + childName + "\" with properties "
388                + properties();
389     }
390 
391     /**
392      * {@inheritDoc}
393      * <p>
394      * This method does not clone the results.
395      * </p>
396      * 
397      * @see org.modeshape.graph.request.ChangeRequest#clone()
398      */
399     @Override
400     public CreateNodeRequest clone() {
401         CreateNodeRequest request = new CreateNodeRequest(under, workspaceName, childName, conflictBehavior, properties);
402         request.setActualLocationOfNode(actualLocation);
403         return request;
404     }
405 
406     @Override
407     public RequestType getType() {
408         return RequestType.CREATE_NODE;
409     }
410 }