001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.graph.requests;
023    
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.Iterator;
028    import java.util.LinkedList;
029    import java.util.List;
030    import org.jboss.dna.common.util.CheckArg;
031    import org.jboss.dna.graph.GraphI18n;
032    import org.jboss.dna.graph.Location;
033    import org.jboss.dna.graph.NodeConflictBehavior;
034    import org.jboss.dna.graph.properties.Property;
035    
036    /**
037     * Instruction to create the node at the specified location. This command will create the node and set the initial properties.
038     * 
039     * @author Randall Hauch
040     */
041    public class CreateNodeRequest extends Request implements Iterable<Property> {
042    
043        private static final long serialVersionUID = 1L;
044    
045        public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
046    
047        private final Location at;
048        private final List<Property> properties;
049        private final NodeConflictBehavior conflictBehavior;
050        private Location actualLocation;
051    
052        /**
053         * Create a request to create a node with the given properties at the supplied location.
054         * 
055         * @param at the location of the node to be read
056         * @param properties the properties of the new node, which should not include the location's
057         *        {@link Location#getIdProperties() identification properties}
058         * @throws IllegalArgumentException if the location is null
059         */
060        public CreateNodeRequest( Location at,
061                                  Property... properties ) {
062            this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
063        }
064    
065        /**
066         * Create a request to create a node with the given properties at the supplied location.
067         * 
068         * @param at the location of the node to be read
069         * @param properties the properties of the new node, which should not include the location's
070         *        {@link Location#getIdProperties() identification properties}
071         * @throws IllegalArgumentException if the location is null
072         */
073        public CreateNodeRequest( Location at,
074                                  Iterable<Property> properties ) {
075            this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
076        }
077    
078        /**
079         * Create a request to create a node with the given properties at the supplied location.
080         * 
081         * @param at the location of the node to be read
082         * @param properties the properties of the new node, which should not include the location's
083         *        {@link Location#getIdProperties() identification properties}
084         * @throws IllegalArgumentException if the location is null
085         */
086        public CreateNodeRequest( Location at,
087                                  Iterator<Property> properties ) {
088            this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
089        }
090    
091        /**
092         * Create a request to create a node with the given properties at the supplied location.
093         * 
094         * @param at the location of the node to be read
095         * @param properties the properties of the new node, which should not include the location's
096         *        {@link Location#getIdProperties() identification properties}
097         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
098         *        location
099         * @throws IllegalArgumentException if the location or the conflict behavior is null
100         */
101        public CreateNodeRequest( Location at,
102                                  NodeConflictBehavior conflictBehavior,
103                                  Property... properties ) {
104            CheckArg.isNotNull(at, "at");
105            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
106            this.at = at;
107            this.conflictBehavior = conflictBehavior;
108            int number = properties.length + (at.hasIdProperties() ? at.getIdProperties().size() : 0);
109            List<Property> props = new ArrayList<Property>(number);
110            for (Property property : properties) {
111                if (property != null) props.add(property);
112            }
113            // Add in the location properties ...
114            if (at.hasIdProperties()) {
115                for (Property property : at.getIdProperties()) {
116                    if (property != null) props.add(property);
117                }
118            }
119            this.properties = Collections.unmodifiableList(props);
120        }
121    
122        /**
123         * Create a request to create a node with the given properties at the supplied location.
124         * 
125         * @param at the location of the node to be read
126         * @param properties the properties of the new node, which should not include the location's
127         *        {@link Location#getIdProperties() identification properties}
128         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
129         *        location
130         * @throws IllegalArgumentException if the location or the conflict behavior is null
131         */
132        public CreateNodeRequest( Location at,
133                                  NodeConflictBehavior conflictBehavior,
134                                  Iterable<Property> properties ) {
135            CheckArg.isNotNull(at, "at");
136            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
137            this.at = at;
138            this.conflictBehavior = conflictBehavior;
139            List<Property> props = new LinkedList<Property>();
140            for (Property property : properties) {
141                if (property != null) props.add(property);
142            }
143            // Add in the location properties ...
144            if (at.hasIdProperties()) {
145                for (Property property : at.getIdProperties()) {
146                    if (property != null) props.add(property);
147                }
148            }
149            this.properties = Collections.unmodifiableList(props);
150        }
151    
152        /**
153         * Create a request to create a node with the given properties at the supplied location.
154         * 
155         * @param at the location of the node to be read
156         * @param properties the properties of the new node, which should not include the location's
157         *        {@link Location#getIdProperties() identification properties}
158         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
159         *        location
160         * @throws IllegalArgumentException if the location or the conflict behavior is null
161         */
162        public CreateNodeRequest( Location at,
163                                  NodeConflictBehavior conflictBehavior,
164                                  Iterator<Property> properties ) {
165            CheckArg.isNotNull(at, "at");
166            CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
167            this.at = at;
168            this.conflictBehavior = conflictBehavior;
169            List<Property> props = new LinkedList<Property>();
170            while (properties.hasNext()) {
171                Property property = properties.next();
172                if (property != null) props.add(property);
173            }
174            // Add in the location properties ...
175            if (at.hasIdProperties()) {
176                for (Property property : at.getIdProperties()) {
177                    if (property != null) props.add(property);
178                }
179            }
180            this.properties = Collections.unmodifiableList(props);
181        }
182    
183        /**
184         * Get the location defining the node that is to be created.
185         * 
186         * @return the location of the node; never null
187         */
188        public Location at() {
189            return at;
190        }
191    
192        /**
193         * {@inheritDoc}
194         * 
195         * @see java.lang.Iterable#iterator()
196         */
197        public Iterator<Property> iterator() {
198            return this.properties.iterator();
199        }
200    
201        /**
202         * Get the properties for the node. If the node's {@link #at() location} has identification properties, the resulting
203         * properties will include the {@link Location#getIdProperties() identification properties}.
204         * 
205         * @return the collection of properties; never null
206         */
207        public Collection<Property> properties() {
208            return properties;
209        }
210    
211        /**
212         * Get the expected behavior when copying the branch and the {@link #at() destination} already has a node with the same name.
213         * 
214         * @return the behavior specification
215         */
216        public NodeConflictBehavior conflictBehavior() {
217            return conflictBehavior;
218        }
219    
220        /**
221         * {@inheritDoc}
222         * 
223         * @see org.jboss.dna.graph.requests.Request#isReadOnly()
224         */
225        @Override
226        public boolean isReadOnly() {
227            return false;
228        }
229    
230        /**
231         * Sets the actual and complete location of the node being created. This method must be called when processing the request,
232         * and the actual location must have a {@link Location#getPath() path}.
233         * 
234         * @param actual the actual location of the node being created, or null if the {@link #at() current location} should be used
235         * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
236         *         location} as the {@link #at() current location}, or if the actual location does not have a path.
237         */
238        public void setActualLocationOfNode( Location actual ) {
239            if (!at.isSame(actual, false)) { // not same if actual is null
240                throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
241            }
242            assert actual != null;
243            if (!actual.hasPath()) {
244                throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
245            }
246            this.actualLocation = actual;
247        }
248    
249        /**
250         * Get the actual location of the node that was created.
251         * 
252         * @return the actual location, or null if the actual location was not set
253         */
254        public Location getActualLocationOfNode() {
255            return actualLocation;
256        }
257    
258        /**
259         * {@inheritDoc}
260         * 
261         * @see java.lang.Object#equals(java.lang.Object)
262         */
263        @Override
264        public boolean equals( Object obj ) {
265            if (this.getClass().isInstance(obj)) {
266                CreateNodeRequest that = (CreateNodeRequest)obj;
267                if (!this.at().equals(that.at())) return false;
268                if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
269                if (!this.properties().equals(that.properties())) return false;
270                return true;
271            }
272            return false;
273        }
274    
275        /**
276         * {@inheritDoc}
277         * 
278         * @see java.lang.Object#toString()
279         */
280        @Override
281        public String toString() {
282            return "create node at " + at() + " with properties " + properties();
283        }
284    
285    }