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 (at 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.Collection;
027    import java.util.HashMap;
028    import java.util.Iterator;
029    import java.util.LinkedList;
030    import java.util.List;
031    import java.util.Map;
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.connector.RepositoryConnection;
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 read the properties and children of the node at the specifed location.
043     * 
044     * @author Randall Hauch
045     */
046    public class ReadNodeRequest extends CacheableRequest implements Iterable<Location> {
047    
048        private static final long serialVersionUID = 1L;
049    
050        private final Location at;
051        private final String workspaceName;
052        private final Map<Name, Property> properties = new HashMap<Name, Property>();
053        private final List<Location> children = new LinkedList<Location>();
054        private Location actualLocation;
055    
056        /**
057         * Create a request to read the properties and number of children of a node at the supplied location.
058         * 
059         * @param at the location of the node to be read
060         * @param workspaceName the name of the workspace containing the node
061         * @throws IllegalArgumentException if the location or workspace name is null
062         */
063        public ReadNodeRequest( Location at,
064                                String workspaceName ) {
065            CheckArg.isNotNull(at, "at");
066            CheckArg.isNotNull(workspaceName, "workspaceName");
067            this.workspaceName = workspaceName;
068            this.at = at;
069        }
070    
071        /**
072         * {@inheritDoc}
073         * 
074         * @see org.jboss.dna.graph.request.Request#isReadOnly()
075         */
076        @Override
077        public boolean isReadOnly() {
078            return true;
079        }
080    
081        /**
082         * Get the location defining the node that is to be read.
083         * 
084         * @return the location of the node; never null
085         */
086        public Location at() {
087            return at;
088        }
089    
090        /**
091         * Get the name of the workspace in which the node exists.
092         * 
093         * @return the name of the workspace; never null
094         */
095        public String inWorkspace() {
096            return workspaceName;
097        }
098    
099        /**
100         * Get the properties that were read from the {@link RepositoryConnection}.
101         * 
102         * @return the properties, as a map of property name to property; never null
103         */
104        public Map<Name, Property> getPropertiesByName() {
105            return properties;
106        }
107    
108        /**
109         * Get the properties that were read from the {@link RepositoryConnection}.
110         * 
111         * @return the collection of properties; never null
112         */
113        public Collection<Property> getProperties() {
114            return properties.values();
115        }
116    
117        /**
118         * Add a property that was read from the {@link RepositoryConnection}
119         * 
120         * @param property the property that was read
121         * @return the previous property that had the same name, or null if there was no previously-recorded property with the same
122         *         name
123         * @throws IllegalArgumentException if the property is null
124         * @throws IllegalStateException if the request is frozen
125         */
126        public Property addProperty( Property property ) {
127            checkNotFrozen();
128            return this.properties.put(property.getName(), property);
129        }
130    
131        /**
132         * Add a property that was read from the {@link RepositoryConnection}
133         * 
134         * @param properties the properties that were read
135         * @throws IllegalArgumentException if the properties array is null
136         * @throws IllegalStateException if the request is frozen
137         */
138        public void addProperties( Property... properties ) {
139            checkNotFrozen();
140            CheckArg.isNotNull(properties, "properties");
141            for (Property property : properties) {
142                this.properties.put(property.getName(), property);
143            }
144        }
145    
146        /**
147         * Add a property that was read from the {@link RepositoryConnection}
148         * 
149         * @param properties the properties that were read
150         * @throws IllegalArgumentException if the iterable reference is null
151         * @throws IllegalStateException if the request is frozen
152         */
153        public void addProperties( Iterable<Property> properties ) {
154            checkNotFrozen();
155            CheckArg.isNotNull(properties, "properties");
156            for (Property property : properties) {
157                this.properties.put(property.getName(), property);
158            }
159        }
160    
161        /**
162         * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
163         * represented by a location.
164         * 
165         * @return the children that were read; never null
166         */
167        public List<Location> getChildren() {
168            return children;
169        }
170    
171        /**
172         * {@inheritDoc}
173         * 
174         * @see java.lang.Iterable#iterator()
175         */
176        public Iterator<Location> iterator() {
177            return children.iterator();
178        }
179    
180        /**
181         * Add to the list of children that has been read the supplied children with the given path and identification properties. The
182         * children are added in order.
183         * 
184         * @param children the locations of the children that were read
185         * @throws IllegalArgumentException if the parameter is null
186         * @throws IllegalStateException if the request is frozen
187         * @see #addChild(Location)
188         * @see #addChild(Path, Property)
189         * @see #addChild(Path, Property, Property...)
190         */
191        public void addChildren( Iterable<Location> children ) {
192            checkNotFrozen();
193            CheckArg.isNotNull(children, "children");
194            for (Location child : children) {
195                if (child != null) this.children.add(child);
196            }
197        }
198    
199        /**
200         * Add to the list of children that has been read the child with the given path and identification properties. The children
201         * should be added in order.
202         * 
203         * @param child the location of the child that was read
204         * @throws IllegalArgumentException if the location is null
205         * @throws IllegalStateException if the request is frozen
206         * @see #addChild(Path, Property)
207         * @see #addChild(Path, Property, Property...)
208         */
209        public void addChild( Location child ) {
210            checkNotFrozen();
211            CheckArg.isNotNull(child, "child");
212            this.children.add(child);
213        }
214    
215        /**
216         * Add to the list of children that has been read the child with the given path and identification properties. The children
217         * should be added in order.
218         * 
219         * @param pathToChild the path of the child that was just read
220         * @param firstIdProperty the first identification property of the child that was just read
221         * @param remainingIdProperties the remaining identification properties of the child that was just read
222         * @throws IllegalArgumentException if the path or identification properties are null
223         * @throws IllegalStateException if the request is frozen
224         * @see #addChild(Location)
225         * @see #addChild(Path, Property)
226         */
227        public void addChild( Path pathToChild,
228                              Property firstIdProperty,
229                              Property... remainingIdProperties ) {
230            checkNotFrozen();
231            Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
232            this.children.add(child);
233        }
234    
235        /**
236         * Add to the list of children that has been read the child with the given path and identification property. The children
237         * should be added in order.
238         * 
239         * @param pathToChild the path of the child that was just read
240         * @param idProperty the identification property of the child that was just read
241         * @throws IllegalArgumentException if the path or identification properties are null
242         * @throws IllegalStateException if the request is frozen
243         * @see #addChild(Location)
244         * @see #addChild(Path, Property, Property...)
245         */
246        public void addChild( Path pathToChild,
247                              Property idProperty ) {
248            checkNotFrozen();
249            Location child = Location.create(pathToChild, idProperty);
250            this.children.add(child);
251        }
252    
253        /**
254         * Sets the actual and complete location of the node whose children and properties have been read. This method must be called
255         * when processing the request, and the actual location must have a {@link Location#getPath() path}.
256         * 
257         * @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
258         * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
259         *         location} as the {@link #at() current location}, or if the actual location does not have a path.
260         * @throws IllegalStateException if the request is frozen
261         */
262        public void setActualLocationOfNode( Location actual ) {
263            checkNotFrozen();
264            if (!at.isSame(actual)) { // not same if actual is null
265                throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
266            }
267            assert actual != null;
268            if (!actual.hasPath()) {
269                throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
270            }
271            this.actualLocation = actual;
272        }
273    
274        /**
275         * Get the actual location of the node whose children and properties were read.
276         * 
277         * @return the actual location, or null if the actual location was not set
278         */
279        public Location getActualLocationOfNode() {
280            return actualLocation;
281        }
282    
283        /**
284         * {@inheritDoc}
285         * 
286         * @see org.jboss.dna.graph.request.Request#cancel()
287         */
288        @Override
289        public void cancel() {
290            super.cancel();
291            this.actualLocation = null;
292            this.children.clear();
293            this.properties.clear();
294        }
295    
296        /**
297         * {@inheritDoc}
298         * 
299         * @see java.lang.Object#hashCode()
300         */
301        @Override
302        public int hashCode() {
303            return HashCode.compute(at, workspaceName);
304        }
305    
306        /**
307         * {@inheritDoc}
308         * 
309         * @see java.lang.Object#equals(java.lang.Object)
310         */
311        @Override
312        public boolean equals( Object obj ) {
313            if (obj == this) return true;
314            if (this.getClass().isInstance(obj)) {
315                ReadNodeRequest that = (ReadNodeRequest)obj;
316                if (!this.at().equals(that.at())) return false;
317                if (!this.inWorkspace().equals(that.inWorkspace())) return false;
318                return true;
319            }
320            return false;
321        }
322    
323        /**
324         * {@inheritDoc}
325         * 
326         * @see java.lang.Object#toString()
327         */
328        @Override
329        public String toString() {
330            return "read node at " + at() + " in the \"" + workspaceName + "\" workspace";
331        }
332    
333    }