001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (under your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.graph.request;
025
026 import java.util.ArrayList;
027 import java.util.Collection;
028 import java.util.Collections;
029 import java.util.Iterator;
030 import java.util.LinkedList;
031 import java.util.List;
032 import org.jboss.dna.common.util.CheckArg;
033 import org.jboss.dna.common.util.HashCode;
034 import org.jboss.dna.graph.GraphI18n;
035 import org.jboss.dna.graph.Location;
036 import org.jboss.dna.graph.NodeConflictBehavior;
037 import org.jboss.dna.graph.property.Name;
038 import org.jboss.dna.graph.property.Path;
039 import org.jboss.dna.graph.property.Property;
040
041 /**
042 * Instruction to create the node under the specified location. This command will create the node and set the initial properties.
043 *
044 * @author Randall Hauch
045 */
046 public class CreateNodeRequest extends ChangeRequest implements Iterable<Property> {
047
048 private static final long serialVersionUID = 1L;
049
050 public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
051
052 private final Location under;
053 private final String workspaceName;
054 private final Name childName;
055 private final List<Property> properties;
056 private final NodeConflictBehavior conflictBehavior;
057 private Location actualLocation;
058
059 /**
060 * Create a request to create a node with the given properties under the supplied location.
061 *
062 * @param parentLocation the location of the existing parent node, under which the new child should be created
063 * @param workspaceName the name of the workspace containing the parent
064 * @param childName the name of the new child to create under the existing parent
065 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
066 * properties} for the new node
067 * @throws IllegalArgumentException if the location, workspace name, or child name is null
068 */
069 public CreateNodeRequest( Location parentLocation,
070 String workspaceName,
071 Name childName,
072 Property... properties ) {
073 this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
074 }
075
076 /**
077 * Create a request to create a node with the given properties under the supplied location.
078 *
079 * @param parentLocation the location of the existing parent node, under which the new child should be created
080 * @param workspaceName the name of the workspace containing the parent
081 * @param childName the name of the new child to create under the existing parent
082 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
083 * properties} for the new node
084 * @throws IllegalArgumentException if the location, workspace name, or child name is null
085 */
086 public CreateNodeRequest( Location parentLocation,
087 String workspaceName,
088 Name childName,
089 Iterable<Property> properties ) {
090 this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
091 }
092
093 /**
094 * Create a request to create a node with the given properties under the supplied location.
095 *
096 * @param parentLocation the location of the existing parent node, under which the new child should be created
097 * @param workspaceName the name of the workspace containing the parent
098 * @param childName the name of the new child to create under the existing parent
099 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
100 * properties} for the new node
101 * @throws IllegalArgumentException if the location, workspace name, or child name is null
102 */
103 public CreateNodeRequest( Location parentLocation,
104 String workspaceName,
105 Name childName,
106 Iterator<Property> properties ) {
107 this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
108 }
109
110 /**
111 * Create a request to create a node with the given properties under the supplied location.
112 *
113 * @param parentLocation the location of the existing parent node, under which the new child should be created
114 * @param workspaceName the name of the workspace containing the parent
115 * @param childName the name of the new child to create under the existing parent
116 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
117 * properties} for the new node
118 * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
119 * location
120 * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
121 */
122 public CreateNodeRequest( Location parentLocation,
123 String workspaceName,
124 Name childName,
125 NodeConflictBehavior conflictBehavior,
126 Property... properties ) {
127 CheckArg.isNotNull(parentLocation, "parentLocation");
128 CheckArg.isNotNull(workspaceName, "workspaceName");
129 CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
130 CheckArg.isNotNull(childName, "childName");
131 this.under = parentLocation;
132 this.workspaceName = workspaceName;
133 this.childName = childName;
134 this.conflictBehavior = conflictBehavior;
135 int number = properties.length + (under.hasIdProperties() ? under.getIdProperties().size() : 0);
136 List<Property> props = new ArrayList<Property>(number);
137 for (Property property : properties) {
138 if (property != null) props.add(property);
139 }
140 this.properties = Collections.unmodifiableList(props);
141 }
142
143 /**
144 * Create a request to create a node with the given properties under the supplied location.
145 *
146 * @param parentLocation the location of the existing parent node, under which the new child should be created
147 * @param workspaceName the name of the workspace containing the parent
148 * @param childName the name of the new child to create under the existing parent
149 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
150 * properties} for the new node
151 * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
152 * location
153 * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
154 */
155 public CreateNodeRequest( Location parentLocation,
156 String workspaceName,
157 Name childName,
158 NodeConflictBehavior conflictBehavior,
159 Iterable<Property> properties ) {
160 CheckArg.isNotNull(parentLocation, "parentLocation");
161 CheckArg.isNotNull(workspaceName, "workspaceName");
162 CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
163 CheckArg.isNotNull(childName, "childName");
164 this.under = parentLocation;
165 this.workspaceName = workspaceName;
166 this.childName = childName;
167 this.conflictBehavior = conflictBehavior;
168 List<Property> props = new LinkedList<Property>();
169 for (Property property : properties) {
170 if (property != null) props.add(property);
171 }
172 this.properties = Collections.unmodifiableList(props);
173 }
174
175 /**
176 * Create a request to create a node with the given properties under the supplied location.
177 *
178 * @param parentLocation the location of the existing parent node, under which the new child should be created
179 * @param workspaceName the name of the workspace containing the parent
180 * @param childName the name of the new child to create under the existing parent
181 * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
182 * properties} for the new node
183 * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
184 * location
185 * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
186 */
187 public CreateNodeRequest( Location parentLocation,
188 String workspaceName,
189 Name childName,
190 NodeConflictBehavior conflictBehavior,
191 Iterator<Property> properties ) {
192 CheckArg.isNotNull(parentLocation, "parentLocation");
193 CheckArg.isNotNull(workspaceName, "workspaceName");
194 CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
195 CheckArg.isNotNull(childName, "childName");
196 this.under = parentLocation;
197 this.workspaceName = workspaceName;
198 this.childName = childName;
199 this.conflictBehavior = conflictBehavior;
200 List<Property> props = new LinkedList<Property>();
201 while (properties.hasNext()) {
202 Property property = properties.next();
203 if (property != null) props.add(property);
204 }
205 this.properties = Collections.unmodifiableList(props);
206 }
207
208 /**
209 * Get the location defining the parent of the new node that is to be created.
210 *
211 * @return the location of the parent node; never null
212 */
213 public Location under() {
214 return under;
215 }
216
217 /**
218 * Get the name of the workspace in which the node is to be createde
219 *
220 * @return the name of the workspace; never null
221 */
222 public String inWorkspace() {
223 return workspaceName;
224 }
225
226 /**
227 * Get the name for the new child.
228 *
229 * @return the child's name; never null
230 */
231 public Name named() {
232 return childName;
233 }
234
235 /**
236 * {@inheritDoc}
237 *
238 * @see java.lang.Iterable#iterator()
239 */
240 public Iterator<Property> iterator() {
241 return this.properties.iterator();
242 }
243
244 /**
245 * Get the properties for the node. If the node's {@link #under() location} has identification properties, the resulting
246 * properties will include the {@link Location#getIdProperties() identification properties}.
247 *
248 * @return the collection of properties; never null
249 */
250 public Collection<Property> properties() {
251 return properties;
252 }
253
254 /**
255 * Get the expected behavior when copying the branch and the {@link #under() destination} already has a node with the same
256 * name.
257 *
258 * @return the behavior specification
259 */
260 public NodeConflictBehavior conflictBehavior() {
261 return conflictBehavior;
262 }
263
264 /**
265 * {@inheritDoc}
266 *
267 * @see org.jboss.dna.graph.request.Request#isReadOnly()
268 */
269 @Override
270 public boolean isReadOnly() {
271 return false;
272 }
273
274 /**
275 * Sets the actual and complete location of the node being created. This method must be called when processing the request,
276 * and the actual location must have a {@link Location#getPath() path}.
277 *
278 * @param actual the actual location of the node being created, or null if the {@link #under() current location} should be
279 * used
280 * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
281 * location} as the {@link #under() current location}, or if the actual location does not have a path.
282 * @throws IllegalStateException if the request is frozen
283 */
284 public void setActualLocationOfNode( Location actual ) {
285 checkNotFrozen();
286 CheckArg.isNotNull(actual, "actual");
287 if (!under.isSame(actual, false)) { // not same if actual is null
288 }
289 assert actual != null;
290 if (!actual.hasPath()) {
291 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
292 }
293 assert actual.hasPath();
294 if (under.hasPath() && !under.getPath().equals(actual.getPath().getParent())) {
295 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, under));
296 }
297 this.actualLocation = actual;
298 }
299
300 /**
301 * Get the actual location of the node that was created.
302 *
303 * @return the actual location, or null if the actual location was not set
304 */
305 public Location getActualLocationOfNode() {
306 return actualLocation;
307 }
308
309 /**
310 * {@inheritDoc}
311 *
312 * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
313 */
314 @Override
315 public boolean changes( String workspace,
316 Path path ) {
317 return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
318 }
319
320 /**
321 * {@inheritDoc}
322 *
323 * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
324 */
325 @Override
326 public Location changedLocation() {
327 return actualLocation;
328 }
329
330 /**
331 * {@inheritDoc}
332 *
333 * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace()
334 */
335 @Override
336 public String changedWorkspace() {
337 return workspaceName;
338 }
339
340 /**
341 * {@inheritDoc}
342 *
343 * @see org.jboss.dna.graph.request.Request#cancel()
344 */
345 @Override
346 public void cancel() {
347 super.cancel();
348 this.actualLocation = null;
349 }
350
351 /**
352 * {@inheritDoc}
353 *
354 * @see java.lang.Object#hashCode()
355 */
356 @Override
357 public int hashCode() {
358 return HashCode.compute(under, childName, workspaceName);
359 }
360
361 /**
362 * {@inheritDoc}
363 *
364 * @see java.lang.Object#equals(java.lang.Object)
365 */
366 @Override
367 public boolean equals( Object obj ) {
368 if (obj == this) return true;
369 if (this.getClass().isInstance(obj)) {
370 CreateNodeRequest that = (CreateNodeRequest)obj;
371 if (!this.under().equals(that.under())) return false;
372 if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
373 if (!this.inWorkspace().equals(that.conflictBehavior())) return false;
374 if (!this.properties().equals(that.properties())) return false;
375 return true;
376 }
377 return false;
378 }
379
380 /**
381 * {@inheritDoc}
382 *
383 * @see java.lang.Object#toString()
384 */
385 @Override
386 public String toString() {
387 String parent = under() + "/";
388 if (under.hasPath() && under.getPath().isRoot()) parent = "/";
389 return "create in the \"" + workspaceName + "\" workspace the node \"" + parent + childName + "\" with properties "
390 + properties();
391 }
392
393 }