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 the actual location is null or does not have a path
279      * @throws IllegalStateException if the request is frozen
280      */
281     public void setActualLocationOfNode( Location actual ) {
282         checkNotFrozen();
283         CheckArg.isNotNull(actual, "actual");
284         if (!actual.hasPath()) {
285             throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
286         }
287         assert actual.hasPath();
288         this.actualLocation = actual;
289     }
290 
291     /**
292      * Get the actual location of the node that was created.
293      * 
294      * @return the actual location, or null if the actual location was not set
295      */
296     public Location getActualLocationOfNode() {
297         return actualLocation;
298     }
299 
300     /**
301      * {@inheritDoc}
302      * 
303      * @see org.modeshape.graph.request.ChangeRequest#changes(java.lang.String, org.modeshape.graph.property.Path)
304      */
305     @Override
306     public boolean changes( String workspace,
307                             Path path ) {
308         return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
309     }
310 
311     /**
312      * {@inheritDoc}
313      * 
314      * @see org.modeshape.graph.request.ChangeRequest#changedLocation()
315      */
316     @Override
317     public Location changedLocation() {
318         return actualLocation != null ? actualLocation : under;
319     }
320 
321     /**
322      * {@inheritDoc}
323      * 
324      * @see org.modeshape.graph.request.ChangeRequest#changedWorkspace()
325      */
326     @Override
327     public String changedWorkspace() {
328         return workspaceName;
329     }
330 
331     /**
332      * {@inheritDoc}
333      * 
334      * @see org.modeshape.graph.request.Request#cancel()
335      */
336     @Override
337     public void cancel() {
338         super.cancel();
339         this.actualLocation = null;
340     }
341 
342     /**
343      * {@inheritDoc}
344      * 
345      * @see java.lang.Object#hashCode()
346      */
347     @Override
348     public int hashCode() {
349         return HashCode.compute(under, childName, workspaceName);
350     }
351 
352     /**
353      * {@inheritDoc}
354      * 
355      * @see java.lang.Object#equals(java.lang.Object)
356      */
357     @Override
358     public boolean equals( Object obj ) {
359         if (obj == this) return true;
360         if (this.getClass().isInstance(obj)) {
361             CreateNodeRequest that = (CreateNodeRequest)obj;
362             if (!this.under().isSame(that.under())) return false;
363             if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
364             if (!this.inWorkspace().equals(that.conflictBehavior())) return false;
365             if (!this.properties().equals(that.properties())) return false;
366             return true;
367         }
368         return false;
369     }
370 
371     /**
372      * {@inheritDoc}
373      * 
374      * @see java.lang.Object#toString()
375      */
376     @Override
377     public String toString() {
378         String parent = under() + "/";
379         if (under.hasPath() && under.getPath().isRoot()) parent = "/";
380         return "create in the \"" + workspaceName + "\" workspace the node \"" + parent + childName + "\" with properties "
381                + properties();
382     }
383 
384     /**
385      * {@inheritDoc}
386      * <p>
387      * This method does not clone the results.
388      * </p>
389      * 
390      * @see org.modeshape.graph.request.ChangeRequest#clone()
391      */
392     @Override
393     public CreateNodeRequest clone() {
394         CreateNodeRequest request = new CreateNodeRequest(under, workspaceName, childName, conflictBehavior, properties);
395         request.setActualLocationOfNode(actualLocation);
396         return request;
397     }
398 
399     @Override
400     public RequestType getType() {
401         return RequestType.CREATE_NODE;
402     }
403 }