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.graph.GraphI18n;
034    import org.jboss.dna.graph.Location;
035    import org.jboss.dna.graph.NodeConflictBehavior;
036    import org.jboss.dna.graph.property.Name;
037    import org.jboss.dna.graph.property.Path;
038    import org.jboss.dna.graph.property.Property;
039    
040    /**
041     * Instruction to create the node under the specified location. This command will create the node and set the initial properties.
042     * 
043     * @author Randall Hauch
044     */
045    public class CreateNodeRequest extends Request implements Iterable<Property>, ChangeRequest {
046    
047        private static final long serialVersionUID = 1L;
048    
049        public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
050    
051        private final Location under;
052        private final String workspaceName;
053        private final Name childName;
054        private final List<Property> properties;
055        private final NodeConflictBehavior conflictBehavior;
056        private Location actualLocation;
057    
058        /**
059         * Create a request to create a node with the given properties under the supplied location.
060         * 
061         * @param parentLocation the location of the existing parent node, under which the new child should be created
062         * @param workspaceName the name of the workspace containing the parent
063         * @param childName the name of the new child to create under the existing parent
064         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
065         *        properties} for the new node
066         * @throws IllegalArgumentException if the location, workspace name, or child name is null
067         */
068        public CreateNodeRequest( Location parentLocation,
069                                  String workspaceName,
070                                  Name childName,
071                                  Property... properties ) {
072            this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
073        }
074    
075        /**
076         * Create a request to create a node with the given properties under the supplied location.
077         * 
078         * @param parentLocation the location of the existing parent node, under which the new child should be created
079         * @param workspaceName the name of the workspace containing the parent
080         * @param childName the name of the new child to create under the existing parent
081         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
082         *        properties} for the new node
083         * @throws IllegalArgumentException if the location, workspace name, or child name is null
084         */
085        public CreateNodeRequest( Location parentLocation,
086                                  String workspaceName,
087                                  Name childName,
088                                  Iterable<Property> properties ) {
089            this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
090        }
091    
092        /**
093         * Create a request to create a node with the given properties under the supplied location.
094         * 
095         * @param parentLocation the location of the existing parent node, under which the new child should be created
096         * @param workspaceName the name of the workspace containing the parent
097         * @param childName the name of the new child to create under the existing parent
098         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
099         *        properties} for the new node
100         * @throws IllegalArgumentException if the location, workspace name, or child name is null
101         */
102        public CreateNodeRequest( Location parentLocation,
103                                  String workspaceName,
104                                  Name childName,
105                                  Iterator<Property> properties ) {
106            this(parentLocation, workspaceName, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
107        }
108    
109        /**
110         * Create a request to create a node with the given properties under the supplied location.
111         * 
112         * @param parentLocation the location of the existing parent node, under which the new child should be created
113         * @param workspaceName the name of the workspace containing the parent
114         * @param childName the name of the new child to create under the existing parent
115         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
116         *        properties} for the new node
117         * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
118         *        location
119         * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
120         */
121        public CreateNodeRequest( Location parentLocation,
122                                  String workspaceName,
123                                  Name childName,
124                                  NodeConflictBehavior conflictBehavior,
125                                  Property... properties ) {
126            CheckArg.isNotNull(parentLocation, "parentLocation");
127            CheckArg.isNotNull(workspaceName, "workspaceName");
128            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
129            CheckArg.isNotNull(childName, "childName");
130            this.under = parentLocation;
131            this.workspaceName = workspaceName;
132            this.childName = childName;
133            this.conflictBehavior = conflictBehavior;
134            int number = properties.length + (under.hasIdProperties() ? under.getIdProperties().size() : 0);
135            List<Property> props = new ArrayList<Property>(number);
136            for (Property property : properties) {
137                if (property != null) props.add(property);
138            }
139            this.properties = Collections.unmodifiableList(props);
140        }
141    
142        /**
143         * Create a request to create a node with the given properties under the supplied location.
144         * 
145         * @param parentLocation the location of the existing parent node, under which the new child should be created
146         * @param workspaceName the name of the workspace containing the parent
147         * @param childName the name of the new child to create under the existing parent
148         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
149         *        properties} for the new node
150         * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
151         *        location
152         * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
153         */
154        public CreateNodeRequest( Location parentLocation,
155                                  String workspaceName,
156                                  Name childName,
157                                  NodeConflictBehavior conflictBehavior,
158                                  Iterable<Property> properties ) {
159            CheckArg.isNotNull(parentLocation, "parentLocation");
160            CheckArg.isNotNull(workspaceName, "workspaceName");
161            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
162            CheckArg.isNotNull(childName, "childName");
163            this.under = parentLocation;
164            this.workspaceName = workspaceName;
165            this.childName = childName;
166            this.conflictBehavior = conflictBehavior;
167            List<Property> props = new LinkedList<Property>();
168            for (Property property : properties) {
169                if (property != null) props.add(property);
170            }
171            this.properties = Collections.unmodifiableList(props);
172        }
173    
174        /**
175         * Create a request to create a node with the given properties under the supplied location.
176         * 
177         * @param parentLocation the location of the existing parent node, under which the new child should be created
178         * @param workspaceName the name of the workspace containing the parent
179         * @param childName the name of the new child to create under the existing parent
180         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
181         *        properties} for the new node
182         * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
183         *        location
184         * @throws IllegalArgumentException if the location, workspace name, child name, or the conflict behavior is null
185         */
186        public CreateNodeRequest( Location parentLocation,
187                                  String workspaceName,
188                                  Name childName,
189                                  NodeConflictBehavior conflictBehavior,
190                                  Iterator<Property> properties ) {
191            CheckArg.isNotNull(parentLocation, "parentLocation");
192            CheckArg.isNotNull(workspaceName, "workspaceName");
193            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
194            CheckArg.isNotNull(childName, "childName");
195            this.under = parentLocation;
196            this.workspaceName = workspaceName;
197            this.childName = childName;
198            this.conflictBehavior = conflictBehavior;
199            List<Property> props = new LinkedList<Property>();
200            while (properties.hasNext()) {
201                Property property = properties.next();
202                if (property != null) props.add(property);
203            }
204            this.properties = Collections.unmodifiableList(props);
205        }
206    
207        /**
208         * Get the location defining the parent of the new node that is to be created.
209         * 
210         * @return the location of the parent node; never null
211         */
212        public Location under() {
213            return under;
214        }
215    
216        /**
217         * Get the name of the workspace in which the node is to be createde
218         * 
219         * @return the name of the workspace; never null
220         */
221        public String inWorkspace() {
222            return workspaceName;
223        }
224    
225        /**
226         * Get the name for the new child.
227         * 
228         * @return the child's name; never null
229         */
230        public Name named() {
231            return childName;
232        }
233    
234        /**
235         * {@inheritDoc}
236         * 
237         * @see java.lang.Iterable#iterator()
238         */
239        public Iterator<Property> iterator() {
240            return this.properties.iterator();
241        }
242    
243        /**
244         * Get the properties for the node. If the node's {@link #under() location} has identification properties, the resulting
245         * properties will include the {@link Location#getIdProperties() identification properties}.
246         * 
247         * @return the collection of properties; never null
248         */
249        public Collection<Property> properties() {
250            return properties;
251        }
252    
253        /**
254         * Get the expected behavior when copying the branch and the {@link #under() destination} already has a node with the same
255         * name.
256         * 
257         * @return the behavior specification
258         */
259        public NodeConflictBehavior conflictBehavior() {
260            return conflictBehavior;
261        }
262    
263        /**
264         * {@inheritDoc}
265         * 
266         * @see org.jboss.dna.graph.request.Request#isReadOnly()
267         */
268        @Override
269        public boolean isReadOnly() {
270            return false;
271        }
272    
273        /**
274         * Sets the actual and complete location of the node being created. This method must be called when processing the request,
275         * and the actual location must have a {@link Location#getPath() path}.
276         * 
277         * @param actual the actual location of the node being created, or null if the {@link #under() current location} should be
278         *        used
279         * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
280         *         location} as the {@link #under() current location}, or if the actual location does not have a path.
281         */
282        public void setActualLocationOfNode( Location actual ) {
283            CheckArg.isNotNull(actual, "actual");
284            if (!under.isSame(actual, false)) { // not same if actual is null
285            }
286            assert actual != null;
287            if (!actual.hasPath()) {
288                throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
289            }
290            assert actual.hasPath();
291            if (under.hasPath() && !under.getPath().equals(actual.getPath().getParent())) {
292                throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, under));
293            }
294            this.actualLocation = actual;
295        }
296    
297        /**
298         * Get the actual location of the node that was created.
299         * 
300         * @return the actual location, or null if the actual location was not set
301         */
302        public Location getActualLocationOfNode() {
303            return actualLocation;
304        }
305    
306        /**
307         * {@inheritDoc}
308         * 
309         * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
310         */
311        public boolean changes( String workspace,
312                                Path path ) {
313            return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
314        }
315    
316        /**
317         * {@inheritDoc}
318         * 
319         * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
320         */
321        public Location changedLocation() {
322            return under;
323        }
324    
325        /**
326         * {@inheritDoc}
327         * 
328         * @see java.lang.Object#equals(java.lang.Object)
329         */
330        @Override
331        public boolean equals( Object obj ) {
332            if (obj == this) return true;
333            if (this.getClass().isInstance(obj)) {
334                CreateNodeRequest that = (CreateNodeRequest)obj;
335                if (!this.under().equals(that.under())) return false;
336                if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
337                if (!this.inWorkspace().equals(that.conflictBehavior())) return false;
338                if (!this.properties().equals(that.properties())) return false;
339                return true;
340            }
341            return false;
342        }
343    
344        /**
345         * {@inheritDoc}
346         * 
347         * @see java.lang.Object#toString()
348         */
349        @Override
350        public String toString() {
351            String parent = under() + "/";
352            if (under.hasPath() && under.getPath().isRoot()) parent = "/";
353            return "create in the \"" + workspaceName + "\" workspace the node \"" + parent + childName + "\" with properties "
354                   + properties();
355        }
356    
357    }