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;
025    
026    import java.io.File;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.io.Reader;
030    import java.math.BigDecimal;
031    import java.net.URI;
032    import java.util.ArrayList;
033    import java.util.Calendar;
034    import java.util.Collection;
035    import java.util.Collections;
036    import java.util.Date;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.LinkedList;
040    import java.util.List;
041    import java.util.Map;
042    import java.util.Set;
043    import java.util.UUID;
044    import net.jcip.annotations.Immutable;
045    import net.jcip.annotations.NotThreadSafe;
046    import org.jboss.dna.common.collection.EmptyIterator;
047    import org.jboss.dna.common.util.CheckArg;
048    import org.jboss.dna.graph.cache.CachePolicy;
049    import org.jboss.dna.graph.connector.RepositoryConnection;
050    import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
051    import org.jboss.dna.graph.connector.RepositorySource;
052    import org.jboss.dna.graph.connector.RepositorySourceException;
053    import org.jboss.dna.graph.io.GraphImporter;
054    import org.jboss.dna.graph.property.Binary;
055    import org.jboss.dna.graph.property.DateTime;
056    import org.jboss.dna.graph.property.Name;
057    import org.jboss.dna.graph.property.NameFactory;
058    import org.jboss.dna.graph.property.Path;
059    import org.jboss.dna.graph.property.PathNotFoundException;
060    import org.jboss.dna.graph.property.Property;
061    import org.jboss.dna.graph.property.PropertyFactory;
062    import org.jboss.dna.graph.property.Reference;
063    import org.jboss.dna.graph.property.ValueFormatException;
064    import org.jboss.dna.graph.property.Path.Segment;
065    import org.jboss.dna.graph.request.BatchRequestBuilder;
066    import org.jboss.dna.graph.request.CloneWorkspaceRequest;
067    import org.jboss.dna.graph.request.CompositeRequest;
068    import org.jboss.dna.graph.request.CreateNodeRequest;
069    import org.jboss.dna.graph.request.CreateWorkspaceRequest;
070    import org.jboss.dna.graph.request.InvalidRequestException;
071    import org.jboss.dna.graph.request.InvalidWorkspaceException;
072    import org.jboss.dna.graph.request.ReadAllChildrenRequest;
073    import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
074    import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
075    import org.jboss.dna.graph.request.ReadBranchRequest;
076    import org.jboss.dna.graph.request.ReadNodeRequest;
077    import org.jboss.dna.graph.request.ReadPropertyRequest;
078    import org.jboss.dna.graph.request.Request;
079    import org.jboss.dna.graph.request.RequestBuilder;
080    import org.jboss.dna.graph.request.UnsupportedRequestException;
081    import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
082    import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
083    import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
084    import org.xml.sax.SAXException;
085    
086    /**
087     * A graph representation of the content within a {@link RepositorySource}, including mechanisms to interact and manipulate that
088     * content. The graph is designed to be an <i><a href="http://en.wikipedia.org/wiki/Domain_Specific_Language">embedded domain
089     * specific language</a></i>, meaning calls to it are designed to read like sentences even though they are really just Java
090     * methods. And to be more readable, methods can be chained together.
091     * 
092     * @author Randall Hauch
093     */
094    @NotThreadSafe
095    public class Graph {
096    
097        protected static final Iterator<Property> EMPTY_PROPERTIES = new EmptyIterator<Property>();
098        protected static final Iterable<Property> NO_PROPERTIES = new Iterable<Property>() {
099            public final Iterator<Property> iterator() {
100                return EMPTY_PROPERTIES;
101            }
102        };
103    
104        /**
105         * Create a graph instance that uses the supplied repository and {@link ExecutionContext context}.
106         * 
107         * @param sourceName the name of the source that should be used
108         * @param connectionFactory the factory of repository connections
109         * @param context the context in which all executions should be performed
110         * @return the new graph
111         * @throws IllegalArgumentException if the source or context parameters are null
112         */
113        public static Graph create( String sourceName,
114                                    RepositoryConnectionFactory connectionFactory,
115                                    ExecutionContext context ) {
116            return new Graph(sourceName, connectionFactory, context);
117        }
118    
119        /**
120         * Create a graph instance that uses the supplied {@link RepositoryConnection} and {@link ExecutionContext context}.
121         * 
122         * @param connection the connection that should be used
123         * @param context the context in which all executions should be performed
124         * @return the new graph
125         * @throws IllegalArgumentException if the connection or context parameters are null
126         */
127        public static Graph create( final RepositoryConnection connection,
128                                    ExecutionContext context ) {
129            CheckArg.isNotNull(connection, "connection");
130            final String connectorSourceName = connection.getSourceName();
131            RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
132                public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
133                    if (connectorSourceName.equals(sourceName)) return connection;
134                    return null;
135                }
136            };
137            return new Graph(connectorSourceName, connectionFactory, context);
138        }
139    
140        /**
141         * Create a graph instance that uses the supplied {@link RepositoryConnection} and {@link ExecutionContext context}.
142         * 
143         * @param source the source that should be used
144         * @param context the context in which all executions should be performed
145         * @return the new graph
146         * @throws IllegalArgumentException if the connection or context parameters are null
147         */
148        public static Graph create( final RepositorySource source,
149                                    ExecutionContext context ) {
150            CheckArg.isNotNull(source, "source");
151            final String connectorSourceName = source.getName();
152            RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
153                public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
154                    if (connectorSourceName.equals(sourceName)) return source.getConnection();
155                    return null;
156                }
157            };
158            return new Graph(connectorSourceName, connectionFactory, context);
159        }
160    
161        private final String sourceName;
162        private final RepositoryConnectionFactory connectionFactory;
163        private final ExecutionContext context;
164        protected final RequestBuilder requests;
165        protected final Conjunction<Graph> nextGraph;
166        private Workspace currentWorkspace;
167    
168        protected Graph( String sourceName,
169                         RepositoryConnectionFactory connectionFactory,
170                         ExecutionContext context ) {
171            CheckArg.isNotNull(sourceName, "sourceName");
172            CheckArg.isNotNull(connectionFactory, "connectionFactory");
173            CheckArg.isNotNull(context, "context");
174            this.sourceName = sourceName;
175            this.connectionFactory = connectionFactory;
176            this.context = context;
177            this.nextGraph = new Conjunction<Graph>() {
178                public Graph and() {
179                    return Graph.this;
180                }
181            };
182            this.requests = new RequestBuilder() {
183                @Override
184                protected <T extends Request> T process( T request ) {
185                    Graph.this.execute(request);
186                    return request;
187                }
188            };
189        }
190    
191        /**
192         * Get the RepositoryConnectionFactory that this graph uses to create {@link RepositoryConnection repository connections}.
193         * 
194         * @return the factory repository connections used by this graph; never null
195         */
196        public RepositoryConnectionFactory getConnectionFactory() {
197            return connectionFactory;
198        }
199    
200        /**
201         * The name of the repository that will be used by this graph. This name is passed to the {@link #getConnectionFactory()
202         * connection factory} when this graph needs to {@link RepositoryConnectionFactory#createConnection(String) obtain} a
203         * {@link RepositoryConnection repository connection}.
204         * 
205         * @return the name of the source
206         */
207        public String getSourceName() {
208            return sourceName;
209        }
210    
211        /**
212         * Get the context of execution within which operations on this graph are performed.
213         * 
214         * @return the execution context; never null
215         */
216        public ExecutionContext getContext() {
217            return context;
218        }
219    
220        /**
221         * Obtain a connection to the source, execute the supplied request, and check the request for {@link Request#getError()
222         * errors}. If an error is found, then it is thrown (or wrapped by a {@link RepositorySourceException} if the error is not a
223         * {@link RuntimeException}.
224         * <p>
225         * This method is called automatically when the {@link #requests request builder} creates each request.
226         * </p>
227         * 
228         * @param request the request to be executed (may be a {@link CompositeRequest}.
229         * @throws PathNotFoundException if the request used a node that did not exist
230         * @throws InvalidRequestException if the request was not valid
231         * @throws InvalidWorkspaceException if the workspace used in the request was not valid
232         * @throws UnsupportedRequestException if the request was not supported by the source
233         * @throws RepositorySourceException if an error occurs during execution
234         * @throws RuntimeException if a runtime error occurs during execution
235         */
236        protected void execute( Request request ) {
237            RepositoryConnection connection = Graph.this.getConnectionFactory().createConnection(getSourceName());
238            if (connection == null) {
239                throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
240            }
241            try {
242                connection.execute(Graph.this.getContext(), request);
243            } finally {
244                connection.close();
245            }
246            if (request.hasError()) {
247                Throwable error = request.getError();
248                if (error instanceof RuntimeException) throw (RuntimeException)error;
249                throw new RepositorySourceException(getSourceName(), error);
250            }
251        }
252    
253        /**
254         * Get the default cache policy for this graph. May be null if such a policy has not been defined for thie
255         * {@link #getSourceName() source}.
256         * 
257         * @return the default cache policy, or null if no such policy has been defined for the source
258         * @throws RepositorySourceException if no repository source with the {@link #getSourceName() name} could be found
259         */
260        public CachePolicy getDefaultCachePolicy() {
261            RepositoryConnection connection = this.connectionFactory.createConnection(getSourceName());
262            if (connection == null) {
263                throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
264            }
265            try {
266                return connection.getDefaultCachePolicy();
267            } finally {
268                connection.close();
269            }
270        }
271    
272        /**
273         * Utility method to set the workspace that will be used by this graph.
274         * 
275         * @param workspaceName the name of the workspace; may not be null
276         * @param actualRootLocation the actual location of the root node in the workspace; may not be null
277         * @return the workspace; never null
278         */
279        protected Workspace setWorkspace( String workspaceName,
280                                          Location actualRootLocation ) {
281            assert workspaceName != null;
282            assert actualRootLocation != null;
283            this.currentWorkspace = new GraphWorkspace(workspaceName, actualRootLocation);
284            return this.currentWorkspace;
285        }
286    
287        /**
288         * Get the name of the current workspace being used by this graph. If the graph has not yet been instructed to
289         * {@link #useWorkspace(String) use} or {@link #createWorkspace() create} a workspace, this method will assume that the
290         * source's default workspace is to be used and will obtain from the source the name of that default workspace.
291         * 
292         * @return the name of the current workspace; never null
293         * @see #getCurrentWorkspace()
294         */
295        public String getCurrentWorkspaceName() {
296            return getCurrentWorkspace().getName();
297        }
298    
299        /**
300         * Get the name of the current workspace being used by this graph. If the graph has not yet been instructed to
301         * {@link #useWorkspace(String) use} or {@link #createWorkspace() create} a workspace, this method will assume that the
302         * source's default workspace is to be used and will obtain from the source the name of that default workspace. If the source
303         * does not have a default workspace, this method will fail with an {@link InvalidWorkspaceException}.
304         * 
305         * @return the name of the current workspace; never null
306         * @see #getCurrentWorkspaceName()
307         * @throws InvalidWorkspaceException if there is no current workspace
308         */
309        public Workspace getCurrentWorkspace() {
310            if (this.currentWorkspace == null) {
311                useWorkspace(null);
312            }
313            assert this.currentWorkspace != null;
314            return this.currentWorkspace;
315        }
316    
317        /**
318         * Get the set of workspace names that are known to this source and accessible by this {@link #getContext() context}.
319         * 
320         * @return the set of workspace names; never null
321         */
322        public Set<String> getWorkspaces() {
323            return requests.getWorkspaces().getAvailableWorkspaceNames();
324        }
325    
326        /**
327         * Switch this graph to use another existing workspace in the same source.
328         * 
329         * @param workspaceName the name of the existing workspace that this graph should begin using, or null if the graph should use
330         *        the "default" workspace in the source (if there is one)
331         * @return the workspace; never null
332         * @throws InvalidWorkspaceException if the workspace with the supplied name does not exist, or if null is supplied as the
333         *         workspace name but the source does not have a default workspace
334         */
335        public Workspace useWorkspace( String workspaceName ) {
336            VerifyWorkspaceRequest request = requests.verifyWorkspace(workspaceName);
337            return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
338        }
339    
340        /**
341         * Create a new workspace in the source used by this graph. This graph's workspace will be set as soon as the new workspace is
342         * created, and all subsequent operations will use the new workspace (until it is changed again by
343         * {@link #useWorkspace(String) using another workspace} or {@link #createWorkspace() creating another}.
344         * 
345         * @return the interface used to complete the request to create a new workspace; never null
346         */
347        public CreateWorkspace createWorkspace() {
348            return new CreateWorkspace() {
349                /**
350                 * {@inheritDoc}
351                 * 
352                 * @see org.jboss.dna.graph.Graph.NameWorkspace#named(java.lang.String)
353                 */
354                public Workspace named( String workspaceName ) {
355                    CreateWorkspaceRequest request = requests.createWorkspace(workspaceName, CreateConflictBehavior.DO_NOT_CREATE);
356                    return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
357                }
358    
359                /**
360                 * {@inheritDoc}
361                 * 
362                 * @see org.jboss.dna.graph.Graph.CreateWorkspace#namedSomethingLike(java.lang.String)
363                 */
364                public Workspace namedSomethingLike( String workspaceName ) {
365                    CreateWorkspaceRequest request = requests.createWorkspace(workspaceName,
366                                                                              CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME);
367                    return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
368                }
369    
370                /**
371                 * {@inheritDoc}
372                 * 
373                 * @see org.jboss.dna.graph.Graph.CreateWorkspace#clonedFrom(java.lang.String)
374                 */
375                public NameWorkspace clonedFrom( final String nameOfWorkspaceToClone ) {
376                    return new NameWorkspace() {
377                        /**
378                         * {@inheritDoc}
379                         * 
380                         * @see org.jboss.dna.graph.Graph.NameWorkspace#named(java.lang.String)
381                         */
382                        public Workspace named( String nameOfWorkspaceToCreate ) {
383                            CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
384                                                                                    nameOfWorkspaceToCreate,
385                                                                                    CreateConflictBehavior.DO_NOT_CREATE,
386                                                                                    CloneConflictBehavior.DO_NOT_CLONE);
387                            return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
388                        }
389    
390                        /**
391                         * {@inheritDoc}
392                         * 
393                         * @see org.jboss.dna.graph.Graph.NameWorkspace#namedSomethingLike(java.lang.String)
394                         */
395                        public Workspace namedSomethingLike( String nameOfWorkspaceToCreate ) {
396                            CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
397                                                                                    nameOfWorkspaceToCreate,
398                                                                                    CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME,
399                                                                                    CloneConflictBehavior.DO_NOT_CLONE);
400                            return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
401                        }
402                    };
403                }
404            };
405        }
406    
407        /**
408         * Begin the request to move the specified node into a parent node at a different location, which is specified via the
409         * <code>into(...)</code> method on the returned {@link Move} object.
410         * <p>
411         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
412         * method is called.
413         * </p>
414         * 
415         * @param from the node that is to be moved.
416         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
417         *         be moved
418         */
419        public Move<Conjunction<Graph>> move( Node from ) {
420            return move(from.getLocation());
421        }
422    
423        /**
424         * Begin the request to move a node at the specified location into a parent node at a different location, which is specified
425         * via the <code>into(...)</code> method on the returned {@link Move} object.
426         * <p>
427         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
428         * method is called.
429         * </p>
430         * 
431         * @param from the location of the node that is to be moved.
432         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
433         *         be moved
434         */
435        public Move<Conjunction<Graph>> move( Location from ) {
436            return new MoveAction<Conjunction<Graph>>(this.nextGraph, from) {
437                @Override
438                protected Conjunction<Graph> submit( Locations from,
439                                                     Location into,
440                                                     Name newName ) {
441                    String workspaceName = getCurrentWorkspaceName();
442                    do {
443                        requests.moveBranch(from.getLocation(), into, workspaceName, newName);
444                    } while ((from = from.next()) != null);
445                    return and();
446                }
447            };
448        }
449    
450        /**
451         * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
452         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
453         * <p>
454         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
455         * method is called.
456         * </p>
457         * 
458         * @param fromPath the path to the node that is to be moved.
459         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
460         *         be moved
461         */
462        public Move<Conjunction<Graph>> move( String fromPath ) {
463            return move(Location.create(createPath(fromPath)));
464        }
465    
466        /**
467         * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
468         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
469         * <p>
470         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
471         * method is called.
472         * </p>
473         * 
474         * @param from the path to the node that is to be moved.
475         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
476         *         be moved
477         */
478        public Move<Conjunction<Graph>> move( Path from ) {
479            return move(Location.create(from));
480        }
481    
482        /**
483         * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which is
484         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
485         * <p>
486         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
487         * method is called.
488         * </p>
489         * 
490         * @param from the UUID of the node that is to be moved.
491         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
492         *         be moved
493         */
494        public Move<Conjunction<Graph>> move( UUID from ) {
495            return move(Location.create(from));
496        }
497    
498        /**
499         * Begin the request to move a node with the specified unique identification property into a parent node at a different
500         * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification
501         * property should uniquely identify a single node.
502         * <p>
503         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
504         * method is called.
505         * </p>
506         * 
507         * @param idProperty the unique identification property of the node that is to be moved.
508         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
509         *         be moved
510         */
511        public Move<Conjunction<Graph>> move( Property idProperty ) {
512            return move(Location.create(idProperty));
513        }
514    
515        /**
516         * Begin the request to move a node with the specified identification properties into a parent node at a different location,
517         * which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification properties
518         * should uniquely identify a single node.
519         * <p>
520         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
521         * method is called.
522         * </p>
523         * 
524         * @param firstIdProperty the first identification property of the node that is to be moved
525         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be moved
526         * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is to
527         *         be moved
528         */
529        public Move<Conjunction<Graph>> move( Property firstIdProperty,
530                                              Property... additionalIdProperties ) {
531            return move(Location.create(firstIdProperty, additionalIdProperties));
532        }
533    
534        /**
535         * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
536         * <code>into(...)</code> method on the returned {@link Copy} object.
537         * <p>
538         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
539         * method is called.
540         * </p>
541         * 
542         * @param from the node that is to be copied.
543         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
544         *         be copied
545         */
546        public Copy<Graph> copy( Node from ) {
547            return copy(from.getLocation());
548        }
549    
550        /**
551         * Begin the request to copy a node at the specified location into a parent node at a different location, which is specified
552         * via the <code>into(...)</code> method on the returned {@link Copy} object.
553         * <p>
554         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
555         * method is called.
556         * </p>
557         * 
558         * @param from the location of the node that is to be copied.
559         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
560         *         be copied
561         */
562        public Copy<Graph> copy( Location from ) {
563            return new CopyAction<Graph>(this, from) {
564                @Override
565                protected Graph submit( Locations from,
566                                        Location into,
567                                        Name childName ) {
568                    String workspaceName = getCurrentWorkspaceName();
569                    do {
570                        requests.copyBranch(from.getLocation(),
571                                            workspaceName,
572                                            into,
573                                            workspaceName,
574                                            childName,
575                                            NodeConflictBehavior.APPEND);
576                    } while ((from = from.next()) != null);
577                    return and();
578                }
579            };
580        }
581    
582        /**
583         * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
584         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
585         * <p>
586         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
587         * method is called.
588         * </p>
589         * 
590         * @param fromPath the path to the node that is to be copied.
591         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
592         *         be copied
593         */
594        public Copy<Graph> copy( String fromPath ) {
595            return copy(Location.create(createPath(fromPath)));
596        }
597    
598        /**
599         * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
600         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
601         * <p>
602         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
603         * method is called.
604         * </p>
605         * 
606         * @param from the path to the node that is to be copied.
607         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
608         *         be copied
609         */
610        public Copy<Graph> copy( Path from ) {
611            return copy(Location.create(from));
612        }
613    
614        /**
615         * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which is
616         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
617         * <p>
618         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
619         * method is called.
620         * </p>
621         * 
622         * @param from the UUID of the node that is to be copied.
623         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
624         *         be copied
625         */
626        public Copy<Graph> copy( UUID from ) {
627            return copy(Location.create(from));
628        }
629    
630        /**
631         * Begin the request to copy a node with the specified unique identification property into a parent node at a different
632         * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification
633         * property should uniquely identify a single node.
634         * <p>
635         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
636         * method is called.
637         * </p>
638         * 
639         * @param idProperty the unique identification property of the node that is to be copied.
640         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
641         *         be copied
642         */
643        public Copy<Graph> copy( Property idProperty ) {
644            return copy(Location.create(idProperty));
645        }
646    
647        /**
648         * Begin the request to copy a node with the specified identification properties into a parent node at a different location,
649         * which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification properties
650         * should uniquely identify a single node.
651         * <p>
652         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
653         * method is called.
654         * </p>
655         * 
656         * @param firstIdProperty the first identification property of the node that is to be copied
657         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
658         * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node is to
659         *         be copied
660         */
661        public Copy<Graph> copy( Property firstIdProperty,
662                                 Property... additionalIdProperties ) {
663            return copy(Location.create(firstIdProperty, additionalIdProperties));
664        }
665    
666        /**
667         * Request to delete the specified node. This request is submitted to the repository immediately.
668         * 
669         * @param at the node that is to be deleted
670         * @return an object that may be used to start another request
671         */
672        public Conjunction<Graph> delete( Node at ) {
673            requests.deleteBranch(at.getLocation(), getCurrentWorkspaceName());
674            return nextGraph;
675        }
676    
677        /**
678         * Request to delete the node at the given location. This request is submitted to the repository immediately.
679         * 
680         * @param at the location of the node that is to be deleted
681         * @return an object that may be used to start another request
682         */
683        public Conjunction<Graph> delete( Location at ) {
684            requests.deleteBranch(at, getCurrentWorkspaceName());
685            return nextGraph;
686        }
687    
688        /**
689         * Request to delete the node at the given path. This request is submitted to the repository immediately.
690         * 
691         * @param atPath the path of the node that is to be deleted
692         * @return an object that may be used to start another request
693         */
694        public Conjunction<Graph> delete( String atPath ) {
695            return delete(Location.create(createPath(atPath)));
696        }
697    
698        /**
699         * Request to delete the node at the given path. This request is submitted to the repository immediately.
700         * 
701         * @param at the path of the node that is to be deleted
702         * @return an object that may be used to start another request
703         */
704        public Conjunction<Graph> delete( Path at ) {
705            return delete(Location.create(at));
706        }
707    
708        /**
709         * Request to delete the node with the given UUID. This request is submitted to the repository immediately.
710         * 
711         * @param at the UUID of the node that is to be deleted
712         * @return an object that may be used to start another request
713         */
714        public Conjunction<Graph> delete( UUID at ) {
715            return delete(Location.create(at));
716        }
717    
718        /**
719         * Request to delete the node with the given unique identification property. This request is submitted to the repository
720         * immediately.
721         * 
722         * @param idProperty the unique identifying property of the node that is to be deleted
723         * @return an object that may be used to start another request
724         */
725        public Conjunction<Graph> delete( Property idProperty ) {
726            return delete(Location.create(idProperty));
727        }
728    
729        /**
730         * Request to delete the node with the given identification properties. The identification properties should uniquely identify
731         * a single node. This request is submitted to the repository immediately.
732         * 
733         * @param firstIdProperty the first identification property of the node that is to be copied
734         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
735         * @return an object that may be used to start another request
736         */
737        public Conjunction<Graph> delete( Property firstIdProperty,
738                                          Property... additionalIdProperties ) {
739            return delete(Location.create(firstIdProperty, additionalIdProperties));
740        }
741    
742        /**
743         * Begin the request to create a node located at the supplied path, and return an interface used to either add properties for
744         * the new node, or complete/submit the request and return the location, node, or graph.
745         * <p>
746         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
747         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
748         * parent or new node.
749         * </p>
750         * 
751         * @param atPath the path to the node that is to be created.
752         * @return an object that may be used to start another request
753         */
754        public CreateAt<Graph> createAt( String atPath ) {
755            return createAt(createPath(atPath));
756        }
757    
758        /**
759         * Begin the request to create a node located at the supplied path, and return an interface used to either add properties for
760         * the new node, or complete/submit the request and return the location, node, or graph.
761         * <p>
762         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
763         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
764         * parent or new node.
765         * </p>
766         * 
767         * @param at the path to the node that is to be created.
768         * @return an object that may be used to start another request
769         */
770        public CreateAt<Graph> createAt( final Path at ) {
771            CheckArg.isNotNull(at, "at");
772            final Path parent = at.getParent();
773            final Name childName = at.getLastSegment().getName();
774            final String workspaceName = getCurrentWorkspaceName();
775            return new CreateAt<Graph>() {
776                private final List<Property> properties = new LinkedList<Property>();
777    
778                public CreateAt<Graph> and( UUID uuid ) {
779                    PropertyFactory factory = getContext().getPropertyFactory();
780                    properties.add(factory.create(DnaLexicon.UUID, uuid));
781                    return this;
782                }
783    
784                public CreateAt<Graph> and( Property property ) {
785                    properties.add(property);
786                    return this;
787                }
788    
789                public CreateAt<Graph> and( Iterable<Property> properties ) {
790                    for (Property property : properties) {
791                        this.properties.add(property);
792                    }
793                    return this;
794                }
795    
796                public CreateAt<Graph> and( String name,
797                                            Object... values ) {
798                    ExecutionContext context = getContext();
799                    PropertyFactory factory = context.getPropertyFactory();
800                    NameFactory nameFactory = context.getValueFactories().getNameFactory();
801                    properties.add(factory.create(nameFactory.create(name), values));
802                    return this;
803                }
804    
805                public CreateAt<Graph> and( Name name,
806                                            Object... values ) {
807                    ExecutionContext context = getContext();
808                    PropertyFactory factory = context.getPropertyFactory();
809                    properties.add(factory.create(name, values));
810                    return this;
811                }
812    
813                public CreateAt<Graph> and( Property property,
814                                            Property... additionalProperties ) {
815                    properties.add(property);
816                    for (Property additionalProperty : additionalProperties) {
817                        properties.add(additionalProperty);
818                    }
819                    return this;
820                }
821    
822                public CreateAt<Graph> with( UUID uuid ) {
823                    return and(uuid);
824                }
825    
826                public CreateAt<Graph> with( Property property ) {
827                    return and(property);
828                }
829    
830                public CreateAt<Graph> with( Iterable<Property> properties ) {
831                    return and(properties);
832                }
833    
834                public CreateAt<Graph> with( Property property,
835                                             Property... additionalProperties ) {
836                    return and(property, additionalProperties);
837                }
838    
839                public CreateAt<Graph> with( String name,
840                                             Object... values ) {
841                    return and(name, values);
842                }
843    
844                public CreateAt<Graph> with( Name name,
845                                             Object... values ) {
846                    return and(name, values);
847                }
848    
849                public Location getLocation() {
850                    Location parentLoc = Location.create(parent);
851                    CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
852                    return request.getActualLocationOfNode();
853                }
854    
855                public Node getNode() {
856                    Location parentLoc = Location.create(parent);
857                    CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
858                    return getNodeAt(request.getActualLocationOfNode());
859                }
860    
861                public Graph and() {
862                    requests.createNode(Location.create(parent), workspaceName, childName, this.properties.iterator());
863                    return Graph.this;
864                }
865            };
866        }
867    
868        /**
869         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
870         * <p>
871         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
872         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
873         * parent or new node.
874         * </p>
875         * 
876         * @param atPath the path to the node that is to be created.
877         * @return an object that may be used to start another request
878         */
879        public Conjunction<Graph> create( String atPath ) {
880            Path at = createPath(atPath);
881            Path parent = at.getParent();
882            Name child = at.getLastSegment().getName();
883            requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, EMPTY_PROPERTIES);
884            return nextGraph;
885        }
886    
887        /**
888         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
889         * <p>
890         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
891         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
892         * parent or new node.
893         * </p>
894         * 
895         * @param at the path to the node that is to be created.
896         * @return an object that may be used to start another request
897         */
898        public Conjunction<Graph> create( final Path at ) {
899            Path parent = at.getParent();
900            Name child = at.getLastSegment().getName();
901            requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, EMPTY_PROPERTIES);
902            return nextGraph;
903        }
904    
905        /**
906         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
907         * <p>
908         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
909         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
910         * parent or new node.
911         * </p>
912         * 
913         * @param atPath the path to the node that is to be created.
914         * @param properties the properties for the new node
915         * @return an object that may be used to start another request
916         */
917        public Conjunction<Graph> create( String atPath,
918                                          Property... properties ) {
919            Path at = createPath(atPath);
920            Path parent = at.getParent();
921            Name child = at.getLastSegment().getName();
922            requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties);
923            return nextGraph;
924        }
925    
926        /**
927         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
928         * <p>
929         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
930         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
931         * parent or new node.
932         * </p>
933         * 
934         * @param at the path to the node that is to be created.
935         * @param properties the properties for the new node
936         * @return an object that may be used to start another request
937         */
938        public Conjunction<Graph> create( Path at,
939                                          Property... properties ) {
940            CheckArg.isNotNull(at, "at");
941            Path parent = at.getParent();
942            Name child = at.getLastSegment().getName();
943            requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties);
944            return nextGraph;
945        }
946    
947        /**
948         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
949         * the repository immediately.
950         * <p>
951         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
952         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
953         * parent or new node.
954         * </p>
955         * 
956         * @param at the path to the node that is to be created.
957         * @param properties the properties for the new node
958         * @return an object that may be used to start another request
959         */
960        public Conjunction<Graph> create( Path at,
961                                          Iterable<Property> properties ) {
962            CheckArg.isNotNull(at, "at");
963            Path parent = at.getParent();
964            Name child = at.getLastSegment().getName();
965            requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties.iterator());
966            return nextGraph;
967        }
968    
969        /**
970         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
971         * the repository immediately.
972         * <p>
973         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
974         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
975         * parent or new node.
976         * </p>
977         * 
978         * @param atPath the path to the node that is to be created.
979         * @return an object that may be used to start another request
980         */
981        public GetNodeConjunction<Graph> createIfMissing( String atPath ) {
982            Path at = createPath(atPath);
983            Path parent = at.getParent();
984            Name child = at.getLastSegment().getName();
985            Location location = requests.createNode(Location.create(parent),
986                                                    getCurrentWorkspaceName(),
987                                                    child,
988                                                    EMPTY_PROPERTIES,
989                                                    NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
990            return new GetNodeOrReturnGraph(location);
991        }
992    
993        /**
994         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
995         * the repository immediately.
996         * <p>
997         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
998         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
999         * parent or new node.
1000         * </p>
1001         * 
1002         * @param at the path to the node that is to be created.
1003         * @return an object that may be used to start another request
1004         */
1005        public GetNodeConjunction<Graph> createIfMissing( final Path at ) {
1006            Path parent = at.getParent();
1007            Name child = at.getLastSegment().getName();
1008            Location location = requests.createNode(Location.create(parent),
1009                                                    getCurrentWorkspaceName(),
1010                                                    child,
1011                                                    EMPTY_PROPERTIES,
1012                                                    NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
1013            return new GetNodeOrReturnGraph(location);
1014        }
1015    
1016        /**
1017         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
1018         * the repository immediately.
1019         * <p>
1020         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
1021         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
1022         * parent or new node.
1023         * </p>
1024         * 
1025         * @param atPath the path to the node that is to be created.
1026         * @param properties the properties for the new node
1027         * @return an object that may be used to start another request
1028         */
1029        public GetNodeConjunction<Graph> createIfMissing( String atPath,
1030                                                          Property... properties ) {
1031            Path at = createPath(atPath);
1032            Path parent = at.getParent();
1033            Name child = at.getLastSegment().getName();
1034            Location location = requests.createNode(Location.create(parent),
1035                                                    getCurrentWorkspaceName(),
1036                                                    child,
1037                                                    properties,
1038                                                    NodeConflictBehavior.UPDATE).getActualLocationOfNode();
1039            return new GetNodeOrReturnGraph(location);
1040        }
1041    
1042        /**
1043         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
1044         * the repository immediately.
1045         * <p>
1046         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
1047         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
1048         * parent or new node.
1049         * </p>
1050         * 
1051         * @param at the path to the node that is to be created.
1052         * @param properties the properties for the new node
1053         * @return an object that may be used to start another request
1054         */
1055        public GetNodeConjunction<Graph> createIfMissing( Path at,
1056                                                          Property... properties ) {
1057            CheckArg.isNotNull(at, "at");
1058            Path parent = at.getParent();
1059            Name child = at.getLastSegment().getName();
1060            Location location = requests.createNode(Location.create(parent),
1061                                                    getCurrentWorkspaceName(),
1062                                                    child,
1063                                                    properties,
1064                                                    NodeConflictBehavior.UPDATE).getActualLocationOfNode();
1065            return new GetNodeOrReturnGraph(location);
1066        }
1067    
1068        /**
1069         * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
1070         * the repository immediately.
1071         * <p>
1072         * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
1073         * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
1074         * parent or new node.
1075         * </p>
1076         * 
1077         * @param at the path to the node that is to be created.
1078         * @param properties the properties for the new node
1079         * @return an object that may be used to start another request
1080         */
1081        public GetNodeConjunction<Graph> createIfMissing( Path at,
1082                                                          Iterable<Property> properties ) {
1083            CheckArg.isNotNull(at, "at");
1084            Path parent = at.getParent();
1085            Name child = at.getLastSegment().getName();
1086            Location location = requests.createNode(Location.create(parent),
1087                                                    getCurrentWorkspaceName(),
1088                                                    child,
1089                                                    properties.iterator(),
1090                                                    NodeConflictBehavior.UPDATE).getActualLocationOfNode();
1091            return new GetNodeOrReturnGraph(location);
1092        }
1093    
1094        /**
1095         * Begin the request to create a node under the existing parent node at the supplied location. Use this method if you are
1096         * creating a node when you have the {@link Location} of a parent from a previous request.
1097         * <p>
1098         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>node(...)</code>
1099         * method is called on the returned object
1100         * </p>
1101         * 
1102         * @param parent the location of the parent
1103         * @return the object used to start creating a node
1104         */
1105        public CreateNode<Conjunction<Graph>> createUnder( final Location parent ) {
1106            final NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
1107            CheckArg.isNotNull(parent, "parent");
1108            return new CreateNode<Conjunction<Graph>>() {
1109                public Conjunction<Graph> node( String name,
1110                                                Property... properties ) {
1111                    Name child = nameFactory.create(name);
1112                    requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
1113                    return nextGraph;
1114                }
1115    
1116                public Conjunction<Graph> node( String name,
1117                                                Iterator<Property> properties ) {
1118                    Name child = nameFactory.create(name);
1119                    requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
1120                    return nextGraph;
1121                }
1122    
1123                public Conjunction<Graph> node( String name,
1124                                                Iterable<Property> properties ) {
1125                    Name child = nameFactory.create(name);
1126                    requests.createNode(parent, getCurrentWorkspaceName(), child, properties.iterator());
1127                    return nextGraph;
1128                }
1129            };
1130        }
1131    
1132        /**
1133         * Set the properties on a node.
1134         * 
1135         * @param properties the properties to set
1136         * @return the remove request object that should be used to specify the node on which the properties are to be set.
1137         */
1138        public On<Conjunction<Graph>> set( final Property... properties ) {
1139            return new On<Conjunction<Graph>>() {
1140                public Conjunction<Graph> on( Location location ) {
1141                    requests.setProperties(location, getCurrentWorkspaceName(), properties);
1142                    return nextGraph;
1143                }
1144    
1145                public Conjunction<Graph> on( String path ) {
1146                    return on(Location.create(createPath(path)));
1147                }
1148    
1149                public Conjunction<Graph> on( Path path ) {
1150                    return on(Location.create(path));
1151                }
1152    
1153                public Conjunction<Graph> on( Property idProperty ) {
1154                    return on(Location.create(idProperty));
1155                }
1156    
1157                public Conjunction<Graph> on( Property firstIdProperty,
1158                                              Property... additionalIdProperties ) {
1159                    return on(Location.create(firstIdProperty, additionalIdProperties));
1160                }
1161    
1162                public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1163                    return on(Location.create(idProperties));
1164                }
1165    
1166                public Conjunction<Graph> on( UUID uuid ) {
1167                    return on(Location.create(uuid));
1168                }
1169            };
1170        }
1171    
1172        /**
1173         * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
1174         * value(s) and the location of the node onto which the property should be set.
1175         * 
1176         * @param propertyName the property name
1177         * @return the interface used to specify the values
1178         */
1179        public SetValues<Conjunction<Graph>> set( String propertyName ) {
1180            Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
1181            return set(name);
1182        }
1183    
1184        /**
1185         * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
1186         * value(s) and the location of the node onto which the property should be set.
1187         * 
1188         * @param propertyName the property name
1189         * @return the interface used to specify the values
1190         */
1191        public SetValues<Conjunction<Graph>> set( final Name propertyName ) {
1192            return new SetValues<Conjunction<Graph>>() {
1193                public SetValuesTo<Conjunction<Graph>> on( final Location location ) {
1194                    return new SetValuesTo<Conjunction<Graph>>() {
1195                        public Conjunction<Graph> to( Node value ) {
1196                            Reference ref = (Reference)convertReferenceValue(value);
1197                            Property property = getContext().getPropertyFactory().create(propertyName, ref);
1198                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1199                            return nextGraph;
1200                        }
1201    
1202                        public Conjunction<Graph> to( Location value ) {
1203                            Reference ref = (Reference)convertReferenceValue(value);
1204                            Property property = getContext().getPropertyFactory().create(propertyName, ref);
1205                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1206                            return nextGraph;
1207                        }
1208    
1209                        protected Conjunction<Graph> toValue( Object value ) {
1210                            Property property = getContext().getPropertyFactory().create(propertyName, value);
1211                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1212                            return nextGraph;
1213                        }
1214    
1215                        public Conjunction<Graph> to( String value ) {
1216                            return toValue(value);
1217                        }
1218    
1219                        public Conjunction<Graph> to( int value ) {
1220                            return toValue(Integer.valueOf(value));
1221                        }
1222    
1223                        public Conjunction<Graph> to( long value ) {
1224                            return toValue(Long.valueOf(value));
1225                        }
1226    
1227                        public Conjunction<Graph> to( boolean value ) {
1228                            return toValue(Boolean.valueOf(value));
1229                        }
1230    
1231                        public Conjunction<Graph> to( float value ) {
1232                            return toValue(Float.valueOf(value));
1233                        }
1234    
1235                        public Conjunction<Graph> to( double value ) {
1236                            return toValue(Double.valueOf(value));
1237                        }
1238    
1239                        public Conjunction<Graph> to( BigDecimal value ) {
1240                            return toValue(value);
1241                        }
1242    
1243                        public Conjunction<Graph> to( Calendar value ) {
1244                            return toValue(value);
1245                        }
1246    
1247                        public Conjunction<Graph> to( Date value ) {
1248                            return toValue(value);
1249                        }
1250    
1251                        public Conjunction<Graph> to( DateTime value ) {
1252                            return toValue(value);
1253                        }
1254    
1255                        public Conjunction<Graph> to( Name value ) {
1256                            return toValue(value);
1257                        }
1258    
1259                        public Conjunction<Graph> to( Path value ) {
1260                            return toValue(value);
1261                        }
1262    
1263                        public Conjunction<Graph> to( Reference value ) {
1264                            return toValue(value);
1265                        }
1266    
1267                        public Conjunction<Graph> to( URI value ) {
1268                            return toValue(value);
1269                        }
1270    
1271                        public Conjunction<Graph> to( UUID value ) {
1272                            return toValue(value);
1273                        }
1274    
1275                        public Conjunction<Graph> to( Binary value ) {
1276                            return toValue(value);
1277                        }
1278    
1279                        public Conjunction<Graph> to( byte[] value ) {
1280                            return toValue(value);
1281                        }
1282    
1283                        public Conjunction<Graph> to( InputStream stream,
1284                                                      long approximateLength ) {
1285                            Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
1286                            return toValue(value);
1287                        }
1288    
1289                        public Conjunction<Graph> to( Reader reader,
1290                                                      long approximateLength ) {
1291                            Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
1292                            return toValue(value);
1293                        }
1294    
1295                        public Conjunction<Graph> to( Object value ) {
1296                            value = convertReferenceValue(value);
1297                            Property property = getContext().getPropertyFactory().create(propertyName, value);
1298                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1299                            return nextGraph;
1300                        }
1301    
1302                        public Conjunction<Graph> to( Object firstValue,
1303                                                      Object... otherValues ) {
1304                            firstValue = convertReferenceValue(firstValue);
1305                            for (int i = 0, len = otherValues.length; i != len; ++i) {
1306                                otherValues[i] = convertReferenceValue(otherValues[i]);
1307                            }
1308                            Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
1309                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1310                            return nextGraph;
1311                        }
1312    
1313                        public Conjunction<Graph> to( Iterable<?> values ) {
1314                            List<Object> valueList = new LinkedList<Object>();
1315                            for (Object value : values) {
1316                                value = convertReferenceValue(value);
1317                                valueList.add(value);
1318                            }
1319                            Property property = getContext().getPropertyFactory().create(propertyName, valueList);
1320                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1321                            return nextGraph;
1322                        }
1323    
1324                        public Conjunction<Graph> to( Iterator<?> values ) {
1325                            List<Object> valueList = new LinkedList<Object>();
1326                            while (values.hasNext()) {
1327                                Object value = values.next();
1328                                valueList.add(value);
1329                            }
1330                            Property property = getContext().getPropertyFactory().create(propertyName, valueList);
1331                            requests.setProperty(location, getCurrentWorkspaceName(), property);
1332                            return nextGraph;
1333                        }
1334                    };
1335                }
1336    
1337                public SetValuesTo<Conjunction<Graph>> on( String path ) {
1338                    return on(Location.create(createPath(path)));
1339                }
1340    
1341                public SetValuesTo<Conjunction<Graph>> on( Path path ) {
1342                    return on(Location.create(path));
1343                }
1344    
1345                public SetValuesTo<Conjunction<Graph>> on( Property idProperty ) {
1346                    return on(Location.create(idProperty));
1347                }
1348    
1349                public SetValuesTo<Conjunction<Graph>> on( Property firstIdProperty,
1350                                                           Property... additionalIdProperties ) {
1351                    return on(Location.create(firstIdProperty, additionalIdProperties));
1352                }
1353    
1354                public SetValuesTo<Conjunction<Graph>> on( Iterable<Property> idProperties ) {
1355                    return on(Location.create(idProperties));
1356                }
1357    
1358                public SetValuesTo<Conjunction<Graph>> on( UUID uuid ) {
1359                    return on(Location.create(uuid));
1360                }
1361    
1362                public On<Conjunction<Graph>> to( Node node ) {
1363                    Reference value = (Reference)convertReferenceValue(node);
1364                    return set(getContext().getPropertyFactory().create(propertyName, value));
1365                }
1366    
1367                public On<Conjunction<Graph>> to( Location location ) {
1368                    Reference value = (Reference)convertReferenceValue(location);
1369                    return set(getContext().getPropertyFactory().create(propertyName, value));
1370                }
1371    
1372                protected On<Conjunction<Graph>> toValue( Object value ) {
1373                    return set(getContext().getPropertyFactory().create(propertyName, value));
1374                }
1375    
1376                public On<Conjunction<Graph>> to( String value ) {
1377                    return toValue(value);
1378                }
1379    
1380                public On<Conjunction<Graph>> to( int value ) {
1381                    return toValue(Integer.valueOf(value));
1382                }
1383    
1384                public On<Conjunction<Graph>> to( long value ) {
1385                    return toValue(Long.valueOf(value));
1386                }
1387    
1388                public On<Conjunction<Graph>> to( boolean value ) {
1389                    return toValue(Boolean.valueOf(value));
1390                }
1391    
1392                public On<Conjunction<Graph>> to( float value ) {
1393                    return toValue(Float.valueOf(value));
1394                }
1395    
1396                public On<Conjunction<Graph>> to( double value ) {
1397                    return toValue(Double.valueOf(value));
1398                }
1399    
1400                public On<Conjunction<Graph>> to( BigDecimal value ) {
1401                    return toValue(value);
1402                }
1403    
1404                public On<Conjunction<Graph>> to( Calendar value ) {
1405                    return toValue(value);
1406                }
1407    
1408                public On<Conjunction<Graph>> to( Date value ) {
1409                    return toValue(value);
1410                }
1411    
1412                public On<Conjunction<Graph>> to( DateTime value ) {
1413                    return toValue(value);
1414                }
1415    
1416                public On<Conjunction<Graph>> to( Name value ) {
1417                    return toValue(value);
1418                }
1419    
1420                public On<Conjunction<Graph>> to( Path value ) {
1421                    return toValue(value);
1422                }
1423    
1424                public On<Conjunction<Graph>> to( Reference value ) {
1425                    return toValue(value);
1426                }
1427    
1428                public On<Conjunction<Graph>> to( URI value ) {
1429                    return toValue(value);
1430                }
1431    
1432                public On<Conjunction<Graph>> to( UUID value ) {
1433                    return toValue(value);
1434                }
1435    
1436                public On<Conjunction<Graph>> to( Binary value ) {
1437                    return toValue(value);
1438                }
1439    
1440                public On<Conjunction<Graph>> to( byte[] value ) {
1441                    return toValue(value);
1442                }
1443    
1444                public On<Conjunction<Graph>> to( InputStream stream,
1445                                                  long approximateLength ) {
1446                    Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
1447                    return toValue(value);
1448                }
1449    
1450                public On<Conjunction<Graph>> to( Reader reader,
1451                                                  long approximateLength ) {
1452                    Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
1453                    return toValue(value);
1454                }
1455    
1456                public On<Conjunction<Graph>> to( Object value ) {
1457                    value = convertReferenceValue(value);
1458                    return set(getContext().getPropertyFactory().create(propertyName, value));
1459                }
1460    
1461                public On<Conjunction<Graph>> to( Object firstValue,
1462                                                  Object... otherValues ) {
1463                    firstValue = convertReferenceValue(firstValue);
1464                    for (int i = 0, len = otherValues.length; i != len; ++i) {
1465                        otherValues[i] = convertReferenceValue(otherValues[i]);
1466                    }
1467                    return set(getContext().getPropertyFactory().create(propertyName, firstValue, otherValues));
1468                }
1469    
1470                public On<Conjunction<Graph>> to( Iterable<?> values ) {
1471                    List<Object> valueList = new LinkedList<Object>();
1472                    for (Object value : values) {
1473                        value = convertReferenceValue(value);
1474                        valueList.add(value);
1475                    }
1476                    return set(getContext().getPropertyFactory().create(propertyName, valueList));
1477                }
1478    
1479                public On<Conjunction<Graph>> to( Iterator<?> values ) {
1480                    List<Object> valueList = new LinkedList<Object>();
1481                    while (values.hasNext()) {
1482                        Object value = values.next();
1483                        valueList.add(value);
1484                    }
1485                    return set(getContext().getPropertyFactory().create(propertyName, valueList));
1486                }
1487            };
1488        }
1489    
1490        /**
1491         * Remove properties from the node at the given location.
1492         * 
1493         * @param propertyNames the names of the properties to be removed
1494         * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1495         */
1496        public On<Conjunction<Graph>> remove( final Name... propertyNames ) {
1497            return new On<Conjunction<Graph>>() {
1498                public Conjunction<Graph> on( Location location ) {
1499                    requests.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
1500                    return nextGraph;
1501                }
1502    
1503                public Conjunction<Graph> on( String path ) {
1504                    return on(Location.create(createPath(path)));
1505                }
1506    
1507                public Conjunction<Graph> on( Path path ) {
1508                    return on(Location.create(path));
1509                }
1510    
1511                public Conjunction<Graph> on( Property idProperty ) {
1512                    return on(Location.create(idProperty));
1513                }
1514    
1515                public Conjunction<Graph> on( Property firstIdProperty,
1516                                              Property... additionalIdProperties ) {
1517                    return on(Location.create(firstIdProperty, additionalIdProperties));
1518                }
1519    
1520                public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1521                    return on(Location.create(idProperties));
1522                }
1523    
1524                public Conjunction<Graph> on( UUID uuid ) {
1525                    return on(Location.create(uuid));
1526                }
1527            };
1528        }
1529    
1530        /**
1531         * Remove properties from the node at the given location.
1532         * 
1533         * @param propertyNames the names of the properties to be removed
1534         * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1535         */
1536        public On<Conjunction<Graph>> remove( final String... propertyNames ) {
1537            NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
1538            int number = propertyNames.length;
1539            final Name[] names = new Name[number];
1540            for (int i = 0; i != number; ++i) {
1541                names[i] = nameFactory.create(propertyNames[i]);
1542            }
1543            return new On<Conjunction<Graph>>() {
1544                public Conjunction<Graph> on( Location location ) {
1545                    requests.removeProperties(location, getCurrentWorkspaceName(), names);
1546                    return nextGraph;
1547                }
1548    
1549                public Conjunction<Graph> on( String path ) {
1550                    return on(Location.create(createPath(path)));
1551                }
1552    
1553                public Conjunction<Graph> on( Path path ) {
1554                    return on(Location.create(path));
1555                }
1556    
1557                public Conjunction<Graph> on( Property idProperty ) {
1558                    return on(Location.create(idProperty));
1559                }
1560    
1561                public Conjunction<Graph> on( Property firstIdProperty,
1562                                              Property... additionalIdProperties ) {
1563                    return on(Location.create(firstIdProperty, additionalIdProperties));
1564                }
1565    
1566                public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1567                    return on(Location.create(idProperties));
1568                }
1569    
1570                public Conjunction<Graph> on( UUID uuid ) {
1571                    return on(Location.create(uuid));
1572                }
1573            };
1574        }
1575    
1576        /**
1577         * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
1578         * object. Once the location is specified, the {@link Collection collection of properties} are read and then returned.
1579         * 
1580         * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
1581         */
1582        public On<Collection<Property>> getProperties() {
1583            return new On<Collection<Property>>() {
1584                public Collection<Property> on( Location location ) {
1585                    return requests.readAllProperties(location, getCurrentWorkspaceName()).getProperties();
1586                }
1587    
1588                public Collection<Property> on( String path ) {
1589                    return on(Location.create(createPath(path)));
1590                }
1591    
1592                public Collection<Property> on( Path path ) {
1593                    return on(Location.create(path));
1594                }
1595    
1596                public Collection<Property> on( Property idProperty ) {
1597                    return on(Location.create(idProperty));
1598                }
1599    
1600                public Collection<Property> on( Property firstIdProperty,
1601                                                Property... additionalIdProperties ) {
1602                    return on(Location.create(firstIdProperty, additionalIdProperties));
1603                }
1604    
1605                public Collection<Property> on( Iterable<Property> idProperties ) {
1606                    return on(Location.create(idProperties));
1607                }
1608    
1609                public Collection<Property> on( UUID uuid ) {
1610                    return on(Location.create(uuid));
1611                }
1612            };
1613        }
1614    
1615        /**
1616         * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
1617         * object. Once the location is specified, the {@link Map map of properties} are read and then returned.
1618         * 
1619         * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
1620         *         as a map keyed by their name
1621         */
1622        public On<Map<Name, Property>> getPropertiesByName() {
1623            return new On<Map<Name, Property>>() {
1624                public Map<Name, Property> on( Location location ) {
1625                    return requests.readAllProperties(location, getCurrentWorkspaceName()).getPropertiesByName();
1626                }
1627    
1628                public Map<Name, Property> on( String path ) {
1629                    return on(Location.create(createPath(path)));
1630                }
1631    
1632                public Map<Name, Property> on( Path path ) {
1633                    return on(Location.create(path));
1634                }
1635    
1636                public Map<Name, Property> on( Property idProperty ) {
1637                    return on(Location.create(idProperty));
1638                }
1639    
1640                public Map<Name, Property> on( Property firstIdProperty,
1641                                               Property... additionalIdProperties ) {
1642                    return on(Location.create(firstIdProperty, additionalIdProperties));
1643                }
1644    
1645                public Map<Name, Property> on( Iterable<Property> idProperties ) {
1646                    return on(Location.create(idProperties));
1647                }
1648    
1649                public Map<Name, Property> on( UUID uuid ) {
1650                    return on(Location.create(uuid));
1651                }
1652            };
1653        }
1654    
1655        /**
1656         * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
1657         * object. The returned object is used to supply the remaining information, including either the {@link Children#of(Location)
1658         * location of the parent}, or that a subset of the children should be retrieved {@link Children#inBlockOf(int) in a block}.
1659         * 
1660         * @return the object that is used to specify the remaining inputs for the request, and which will return the children
1661         */
1662        public Children<List<Location>> getChildren() {
1663            return new Children<List<Location>>() {
1664                public List<Location> of( String path ) {
1665                    return of(Location.create(createPath(path)));
1666                }
1667    
1668                public List<Location> of( Path path ) {
1669                    return of(Location.create(path));
1670                }
1671    
1672                public List<Location> of( Property idProperty ) {
1673                    return of(Location.create(idProperty));
1674                }
1675    
1676                public List<Location> of( Property firstIdProperty,
1677                                          Property... additionalIdProperties ) {
1678                    return of(Location.create(firstIdProperty, additionalIdProperties));
1679                }
1680    
1681                public List<Location> of( Iterable<Property> idProperties ) {
1682                    return of(Location.create(idProperties));
1683                }
1684    
1685                public List<Location> of( UUID uuid ) {
1686                    return of(Location.create(uuid));
1687                }
1688    
1689                public List<Location> of( Location at ) {
1690                    return requests.readAllChildren(at, getCurrentWorkspaceName()).getChildren();
1691                }
1692    
1693                public BlockOfChildren<List<Location>> inBlockOf( final int blockSize ) {
1694                    return new BlockOfChildren<List<Location>>() {
1695                        public Under<List<Location>> startingAt( final int startingIndex ) {
1696                            return new Under<List<Location>>() {
1697                                public List<Location> under( String path ) {
1698                                    return under(Location.create(createPath(path)));
1699                                }
1700    
1701                                public List<Location> under( Path path ) {
1702                                    return under(Location.create(path));
1703                                }
1704    
1705                                public List<Location> under( Property idProperty ) {
1706                                    return under(Location.create(idProperty));
1707                                }
1708    
1709                                public List<Location> under( Property firstIdProperty,
1710                                                             Property... additionalIdProperties ) {
1711                                    return under(Location.create(firstIdProperty, additionalIdProperties));
1712                                }
1713    
1714                                public List<Location> under( UUID uuid ) {
1715                                    return under(Location.create(uuid));
1716                                }
1717    
1718                                public List<Location> under( Location at ) {
1719                                    return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
1720                                                   .getChildren();
1721                                }
1722                            };
1723                        }
1724    
1725                        public List<Location> startingAfter( final Location previousSibling ) {
1726                            return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
1727                                           .getChildren();
1728                        }
1729    
1730                        public List<Location> startingAfter( String pathOfPreviousSibling ) {
1731                            return startingAfter(Location.create(createPath(pathOfPreviousSibling)));
1732                        }
1733    
1734                        public List<Location> startingAfter( Path pathOfPreviousSibling ) {
1735                            return startingAfter(Location.create(pathOfPreviousSibling));
1736                        }
1737    
1738                        public List<Location> startingAfter( UUID uuidOfPreviousSibling ) {
1739                            return startingAfter(Location.create(uuidOfPreviousSibling));
1740                        }
1741    
1742                        public List<Location> startingAfter( Property idPropertyOfPreviousSibling ) {
1743                            return startingAfter(Location.create(idPropertyOfPreviousSibling));
1744                        }
1745    
1746                        public List<Location> startingAfter( Property firstIdProperyOfPreviousSibling,
1747                                                             Property... additionalIdPropertiesOfPreviousSibling ) {
1748                            return startingAfter(Location.create(firstIdProperyOfPreviousSibling,
1749                                                                 additionalIdPropertiesOfPreviousSibling));
1750                        }
1751                    };
1752                }
1753            };
1754        }
1755    
1756        /**
1757         * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
1758         * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
1759         * 
1760         * @param name the name of the property that is to be read
1761         * @return the object that is used to specified the node whose property is to be read, and which will return the property
1762         */
1763        public On<Property> getProperty( final String name ) {
1764            Name nameObj = context.getValueFactories().getNameFactory().create(name);
1765            return getProperty(nameObj);
1766        }
1767    
1768        /**
1769         * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
1770         * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
1771         * 
1772         * @param name the name of the property that is to be read
1773         * @return the object that is used to specified the node whose property is to be read, and which will return the property
1774         */
1775        public On<Property> getProperty( final Name name ) {
1776            return new On<Property>() {
1777                public Property on( String path ) {
1778                    return on(Location.create(createPath(path)));
1779                }
1780    
1781                public Property on( Path path ) {
1782                    return on(Location.create(path));
1783                }
1784    
1785                public Property on( Property idProperty ) {
1786                    return on(Location.create(idProperty));
1787                }
1788    
1789                public Property on( Property firstIdProperty,
1790                                    Property... additionalIdProperties ) {
1791                    return on(Location.create(firstIdProperty, additionalIdProperties));
1792                }
1793    
1794                public Property on( Iterable<Property> idProperties ) {
1795                    return on(Location.create(idProperties));
1796                }
1797    
1798                public Property on( UUID uuid ) {
1799                    return on(Location.create(uuid));
1800                }
1801    
1802                public Property on( Location at ) {
1803                    return requests.readProperty(at, getCurrentWorkspaceName(), name).getProperty();
1804                }
1805            };
1806        }
1807    
1808        /**
1809         * Request to read the node with the supplied UUID.
1810         * 
1811         * @param uuid the UUID of the node that is to be read
1812         * @return the node that is read from the repository
1813         */
1814        public Node getNodeAt( UUID uuid ) {
1815            return getNodeAt(Location.create(uuid));
1816        }
1817    
1818        /**
1819         * Request to read the node at the supplied location.
1820         * 
1821         * @param location the location of the node that is to be read
1822         * @return the node that is read from the repository
1823         */
1824        public Node getNodeAt( Location location ) {
1825            return new GraphNode(requests.readNode(location, getCurrentWorkspaceName()));
1826        }
1827    
1828        /**
1829         * Request to read the node at the supplied path.
1830         * 
1831         * @param path the path of the node that is to be read
1832         * @return the node that is read from the repository
1833         */
1834        public Node getNodeAt( String path ) {
1835            return getNodeAt(Location.create(createPath(path)));
1836        }
1837    
1838        /**
1839         * Request to read the node at the supplied path.
1840         * 
1841         * @param path the path of the node that is to be read
1842         * @return the node that is read from the repository
1843         */
1844        public Node getNodeAt( Path path ) {
1845            return getNodeAt(Location.create(path));
1846        }
1847    
1848        /**
1849         * Request to read the node with the supplied unique identifier property.
1850         * 
1851         * @param idProperty the identification property that is unique to the node that is to be read
1852         * @return the node that is read from the repository
1853         */
1854        public Node getNodeAt( Property idProperty ) {
1855            return getNodeAt(Location.create(idProperty));
1856        }
1857    
1858        /**
1859         * Request to read the node with the supplied unique identifier properties.
1860         * 
1861         * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
1862         * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be read
1863         * @return the node that is read from the repository
1864         */
1865        public Node getNodeAt( Property firstIdProperty,
1866                               Property... additionalIdProperties ) {
1867            return getNodeAt(Location.create(firstIdProperty, additionalIdProperties));
1868        }
1869    
1870        /**
1871         * Request to read the node with the supplied unique identifier properties.
1872         * 
1873         * @param idProperties the identification properties that uniquely identify the node that is to be read
1874         * @return the node that is read from the repository
1875         */
1876        public Node getNodeAt( Iterable<Property> idProperties ) {
1877            return getNodeAt(Location.create(idProperties));
1878        }
1879    
1880        /**
1881         * Request to read the node given by the supplied reference value.
1882         * 
1883         * @param reference the reference property value that is to be resolved into a node
1884         * @return the node that is read from the repository
1885         * @throws ValueFormatException if the supplied reference could not be converted to an identifier property value
1886         */
1887        public Node resolve( Reference reference ) {
1888            CheckArg.isNotNull(reference, "reference");
1889            UUID uuid = context.getValueFactories().getUuidFactory().create(reference);
1890            return getNodeAt(uuid);
1891        }
1892    
1893        /**
1894         * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code> in
1895         * the resulting {@link At} object. All properties and children of every node in the subgraph will be read and returned in the
1896         * {@link Subgraph} object returned from the <code>at(...)</code> methods.
1897         * 
1898         * @param depth the maximum depth of the subgraph that should be read
1899         * @return the component that should be used to specify the location of the node that is the top of the subgraph, and which
1900         *         will return the {@link Subgraph} containing the results
1901         */
1902        public At<Subgraph> getSubgraphOfDepth( final int depth ) {
1903            return new At<Subgraph>() {
1904                public Subgraph at( Location location ) {
1905                    return new SubgraphResults(requests.readBranch(location, getCurrentWorkspaceName(), depth));
1906                }
1907    
1908                public Subgraph at( String path ) {
1909                    return at(Location.create(createPath(path)));
1910                }
1911    
1912                public Subgraph at( Path path ) {
1913                    return at(Location.create(path));
1914                }
1915    
1916                public Subgraph at( UUID uuid ) {
1917                    return at(Location.create(uuid));
1918                }
1919    
1920                public Subgraph at( Property idProperty ) {
1921                    return at(Location.create(idProperty));
1922                }
1923    
1924                public Subgraph at( Property firstIdProperty,
1925                                    Property... additionalIdProperties ) {
1926                    return at(Location.create(firstIdProperty, additionalIdProperties));
1927                }
1928    
1929                public Subgraph at( Iterable<Property> idProperties ) {
1930                    return at(Location.create(idProperties));
1931                }
1932            };
1933        }
1934    
1935        /**
1936         * Import the content from the XML file at the supplied URI, specifying via the returned {@link ImportInto object} where the
1937         * content is to be imported.
1938         * 
1939         * @param uri the URI where the importer can read the content that is to be imported
1940         * @return the object that should be used to specify into which the content is to be imported
1941         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
1942         */
1943        public ImportInto<Conjunction<Graph>> importXmlFrom( final URI uri ) {
1944            return new ImportInto<Conjunction<Graph>>() {
1945                private boolean skipRootElement = false;
1946    
1947                public ImportInto<Conjunction<Graph>> skippingRootElement( boolean skipRootElement ) {
1948                    this.skipRootElement = skipRootElement;
1949                    return this;
1950                }
1951    
1952                public Conjunction<Graph> into( String path ) throws IOException, SAXException {
1953                    return into(Location.create(createPath(path)));
1954                }
1955    
1956                public Conjunction<Graph> into( Path path ) throws IOException, SAXException {
1957                    return into(Location.create(path));
1958                }
1959    
1960                public Conjunction<Graph> into( Property idProperty ) throws IOException, SAXException {
1961                    return into(Location.create(idProperty));
1962                }
1963    
1964                public Conjunction<Graph> into( Property firstIdProperty,
1965                                                Property... additionalIdProperties ) throws IOException, SAXException {
1966                    return into(Location.create(firstIdProperty, additionalIdProperties));
1967                }
1968    
1969                public Conjunction<Graph> into( Iterable<Property> idProperties ) throws IOException, SAXException {
1970                    return into(Location.create(idProperties));
1971                }
1972    
1973                public Conjunction<Graph> into( UUID uuid ) throws IOException, SAXException {
1974                    return into(Location.create(uuid));
1975                }
1976    
1977                public Conjunction<Graph> into( Location at ) throws IOException, SAXException {
1978                    GraphImporter importer = new GraphImporter(Graph.this);
1979                    importer.importXml(uri, at, skipRootElement).execute(); // 'importXml' creates and uses a new batch
1980                    return Graph.this.nextGraph;
1981                }
1982            };
1983        }
1984    
1985        /**
1986         * Import the content from the XML file at the supplied file location, specifying via the returned {@link ImportInto object}
1987         * where the content is to be imported.
1988         * 
1989         * @param pathToFile the path to the XML file that should be imported.
1990         * @return the object that should be used to specify into which the content is to be imported
1991         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
1992         */
1993        public ImportInto<Conjunction<Graph>> importXmlFrom( String pathToFile ) {
1994            CheckArg.isNotNull(pathToFile, "pathToFile");
1995            return importXmlFrom(new File(pathToFile).toURI());
1996        }
1997    
1998        /**
1999         * Import the content from the XML file at the supplied file, specifying via the returned {@link ImportInto object} where the
2000         * content is to be imported.
2001         * 
2002         * @param file the XML file that should be imported.
2003         * @return the object that should be used to specify into which the content is to be imported
2004         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
2005         */
2006        public ImportInto<Conjunction<Graph>> importXmlFrom( File file ) {
2007            CheckArg.isNotNull(file, "file");
2008            return importXmlFrom(file.toURI());
2009        }
2010    
2011        protected Path createPath( String path ) {
2012            return getContext().getValueFactories().getPathFactory().create(path);
2013        }
2014    
2015        protected List<Segment> getSegments( List<Location> locations ) {
2016            List<Segment> segments = new ArrayList<Segment>(locations.size());
2017            for (Location location : locations) {
2018                segments.add(location.getPath().getLastSegment());
2019            }
2020            return segments;
2021        }
2022    
2023        /**
2024         * Begin a batch of requests to perform various operations. Use this approach when multiple operations are to be built and
2025         * then executed with one submission to the underlying {@link #getSourceName() repository source}. The {@link Results results}
2026         * are not available until the {@link Batch#execute()} method is invoked.
2027         * 
2028         * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
2029         * @see Batch#execute()
2030         * @see Results
2031         */
2032        public Batch batch() {
2033            return new Batch(new BatchRequestBuilder());
2034        }
2035    
2036        /**
2037         * Begin a batch of requests to perform various operations, but specify the queue where all accumulated requests should be
2038         * placed. Use this approach when multiple operations are to be built and then executed with one submission to the underlying
2039         * {@link #getSourceName() repository source}. The {@link Results results} are not available until the {@link Batch#execute()}
2040         * method is invoked.
2041         * 
2042         * @param builder the request builder that should be used; may not be null
2043         * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
2044         * @see Batch#execute()
2045         * @see Results
2046         */
2047        public Batch batch( BatchRequestBuilder builder ) {
2048            CheckArg.isNotNull(builder, "builder");
2049            return new Batch(builder);
2050        }
2051    
2052        /**
2053         * Interface for creating multiple requests to perform various operations. Note that all the requests are accumulated until
2054         * the {@link #execute()} method is called. The results of all the operations are then available in the {@link Results} object
2055         * returned by the {@link #execute()}.
2056         * 
2057         * @author Randall Hauch
2058         */
2059        @Immutable
2060        public final class Batch implements Executable<Node> {
2061            protected final BatchRequestBuilder requestQueue;
2062            protected final BatchConjunction nextRequests;
2063            protected final String workspaceName;
2064            protected boolean executed = false;
2065    
2066            /*package*/Batch( BatchRequestBuilder builder ) {
2067                assert builder != null;
2068                this.requestQueue = builder;
2069                this.workspaceName = Graph.this.getCurrentWorkspaceName();
2070                this.nextRequests = new BatchConjunction() {
2071                    public Batch and() {
2072                        return Batch.this;
2073                    }
2074    
2075                    public Results execute() {
2076                        return Batch.this.execute();
2077                    }
2078                };
2079            }
2080    
2081            /**
2082             * Return whether this batch has been {@link #execute() executed}.
2083             * 
2084             * @return true if this batch has already been executed, or false otherwise
2085             */
2086            public boolean hasExecuted() {
2087                return executed;
2088            }
2089    
2090            /**
2091             * Determine whether this batch needs to be executed (there are requests and the batch has not been executed yet).
2092             * 
2093             * @return true if there are some requests in this batch that need to be executed, or false execution is not required
2094             */
2095            public boolean isExecuteRequired() {
2096                return !executed && requestQueue.hasRequests();
2097            }
2098    
2099            /**
2100             * Obtain the graph that this batch uses.
2101             * 
2102             * @return the graph; never null
2103             */
2104            public Graph getGraph() {
2105                return Graph.this;
2106            }
2107    
2108            /**
2109             * Get the name of the workspace that this batch is using. This is always constant throughout the lifetime of the batch.
2110             * 
2111             * @return the name of the workspace; never null
2112             */
2113            public String getCurrentWorkspaceName() {
2114                return this.workspaceName;
2115            }
2116    
2117            protected final void assertNotExecuted() {
2118                if (executed) {
2119                    throw new IllegalStateException(GraphI18n.unableToAddMoreRequestsToAlreadyExecutedBatch.text());
2120                }
2121            }
2122    
2123            /**
2124             * Begin the request to move the specified node into a parent node at a different location, which is specified via the
2125             * <code>into(...)</code> method on the returned {@link Move} object.
2126             * <p>
2127             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2128             * called.
2129             * </p>
2130             * 
2131             * @param from the node that is to be moved.
2132             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2133             *         to be moved
2134             */
2135            public Move<BatchConjunction> move( Node from ) {
2136                return move(from.getLocation());
2137            }
2138    
2139            /**
2140             * Begin the request to move a node at the specified location into a parent node at a different location, which is
2141             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
2142             * <p>
2143             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2144             * called.
2145             * </p>
2146             * 
2147             * @param from the location of the node that is to be moved.
2148             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2149             *         to be moved
2150             */
2151            public final Move<BatchConjunction> move( Location from ) {
2152                assertNotExecuted();
2153                return new MoveAction<BatchConjunction>(this.nextRequests, from) {
2154                    @Override
2155                    protected BatchConjunction submit( Locations from,
2156                                                       Location into,
2157                                                       Name newName ) {
2158                        String workspaceName = getCurrentWorkspaceName();
2159                        do {
2160                            requestQueue.moveBranch(from.getLocation(), into, workspaceName, newName);
2161                        } while ((from = from.next()) != null);
2162                        return and();
2163                    }
2164                };
2165            }
2166    
2167            /**
2168             * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
2169             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
2170             * <p>
2171             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2172             * called.
2173             * </p>
2174             * 
2175             * @param fromPath the path to the node that is to be moved.
2176             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2177             *         to be moved
2178             */
2179            public Move<BatchConjunction> move( String fromPath ) {
2180                return move(Location.create(createPath(fromPath)));
2181            }
2182    
2183            /**
2184             * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
2185             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
2186             * <p>
2187             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2188             * called.
2189             * </p>
2190             * 
2191             * @param from the path to the node that is to be moved.
2192             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2193             *         to be moved
2194             */
2195            public Move<BatchConjunction> move( Path from ) {
2196                return move(Location.create(from));
2197            }
2198    
2199            /**
2200             * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which
2201             * is specified via the <code>into(...)</code> method on the returned {@link Move} object.
2202             * <p>
2203             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2204             * called.
2205             * </p>
2206             * 
2207             * @param from the UUID of the node that is to be moved.
2208             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2209             *         to be moved
2210             */
2211            public Move<BatchConjunction> move( UUID from ) {
2212                return move(Location.create(from));
2213            }
2214    
2215            /**
2216             * Begin the request to move a node with the specified unique identification property into a parent node at a different
2217             * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
2218             * identification property should uniquely identify a single node.
2219             * <p>
2220             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2221             * called.
2222             * </p>
2223             * 
2224             * @param idProperty the unique identification property of the node that is to be moved.
2225             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2226             *         to be moved
2227             */
2228            public Move<BatchConjunction> move( Property idProperty ) {
2229                return move(Location.create(idProperty));
2230            }
2231    
2232            /**
2233             * Begin the request to move a node with the specified identification properties into a parent node at a different
2234             * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
2235             * identification properties should uniquely identify a single node.
2236             * <p>
2237             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2238             * called.
2239             * </p>
2240             * 
2241             * @param firstIdProperty the first identification property of the node that is to be moved
2242             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be moved
2243             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2244             *         to be moved
2245             */
2246            public Move<BatchConjunction> move( Property firstIdProperty,
2247                                                Property... additionalIdProperties ) {
2248                return move(Location.create(firstIdProperty, additionalIdProperties));
2249            }
2250    
2251            /**
2252             * Begin the request to move a node with the specified identification properties into a parent node at a different
2253             * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
2254             * identification properties should uniquely identify a single node.
2255             * <p>
2256             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2257             * called.
2258             * </p>
2259             * 
2260             * @param idProperties the idenficiation properties of the node that is to be moved
2261             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
2262             *         to be moved
2263             */
2264            public Move<BatchConjunction> move( Iterable<Property> idProperties ) {
2265                return move(Location.create(idProperties));
2266            }
2267    
2268            /**
2269             * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
2270             * <code>into(...)</code> method on the returned {@link Copy} object.
2271             * <p>
2272             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2273             * called.
2274             * </p>
2275             * 
2276             * @param from the node that is to be copied.
2277             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2278             *         is to be copied
2279             */
2280            public Copy<BatchConjunction> copy( Node from ) {
2281                return copy(from.getLocation());
2282            }
2283    
2284            /**
2285             * Begin the request to copy a node at the specified location into a parent node at a different location, which is
2286             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
2287             * <p>
2288             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2289             * called.
2290             * </p>
2291             * 
2292             * @param from the location of the node that is to be copied.
2293             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2294             *         is to be copied
2295             */
2296            public Copy<BatchConjunction> copy( Location from ) {
2297                assertNotExecuted();
2298                return new CopyAction<BatchConjunction>(this.nextRequests, from) {
2299                    @Override
2300                    protected BatchConjunction submit( Locations from,
2301                                                       Location into,
2302                                                       Name copyName ) {
2303                        String workspaceName = getCurrentWorkspaceName();
2304                        do {
2305                            requestQueue.copyBranch(from.getLocation(), workspaceName, into, workspaceName, copyName);
2306                        } while ((from = from.next()) != null);
2307                        return and();
2308                    }
2309                };
2310            }
2311    
2312            /**
2313             * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
2314             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
2315             * <p>
2316             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2317             * called.
2318             * </p>
2319             * 
2320             * @param fromPath the path to the node that is to be copied.
2321             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2322             *         is to be copied
2323             */
2324            public Copy<BatchConjunction> copy( String fromPath ) {
2325                return copy(Location.create(createPath(fromPath)));
2326            }
2327    
2328            /**
2329             * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
2330             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
2331             * <p>
2332             * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the
2333             * <code>into(...)</code> method is called.
2334             * </p>
2335             * 
2336             * @param from the path to the node that is to be copied.
2337             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2338             *         is to be copied
2339             */
2340            public Copy<BatchConjunction> copy( Path from ) {
2341                return copy(Location.create(from));
2342            }
2343    
2344            /**
2345             * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which
2346             * is specified via the <code>into(...)</code> method on the returned {@link Copy} object.
2347             * <p>
2348             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2349             * called.
2350             * </p>
2351             * 
2352             * @param from the UUID of the node that is to be copied.
2353             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2354             *         is to be copied
2355             */
2356            public Copy<BatchConjunction> copy( UUID from ) {
2357                return copy(Location.create(from));
2358            }
2359    
2360            /**
2361             * Begin the request to copy a node with the specified unique identification property into a parent node at a different
2362             * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
2363             * identification property should uniquely identify a single node.
2364             * <p>
2365             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2366             * called.
2367             * </p>
2368             * 
2369             * @param idProperty the unique identification property of the node that is to be copied.
2370             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2371             *         is to be copied
2372             */
2373            public Copy<BatchConjunction> copy( Property idProperty ) {
2374                return copy(Location.create(idProperty));
2375            }
2376    
2377            /**
2378             * Begin the request to copy a node with the specified identification properties into a parent node at a different
2379             * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
2380             * identification properties should uniquely identify a single node.
2381             * <p>
2382             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2383             * called.
2384             * </p>
2385             * 
2386             * @param firstIdProperty the first identification property of the node that is to be copied
2387             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
2388             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2389             *         is to be copied
2390             */
2391            public Copy<BatchConjunction> copy( Property firstIdProperty,
2392                                                Property... additionalIdProperties ) {
2393                return copy(Location.create(firstIdProperty, additionalIdProperties));
2394            }
2395    
2396            /**
2397             * Begin the request to copy a node with the specified identification properties into a parent node at a different
2398             * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
2399             * identification properties should uniquely identify a single node.
2400             * <p>
2401             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2402             * called.
2403             * </p>
2404             * 
2405             * @param idProperties the identification properties of the node that is to be copied
2406             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
2407             *         is to be copied
2408             */
2409            public Copy<BatchConjunction> copy( Iterable<Property> idProperties ) {
2410                return copy(Location.create(idProperties));
2411            }
2412    
2413            /**
2414             * Request to delete the specified node.
2415             * <p>
2416             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2417             * called.
2418             * </p>
2419             * 
2420             * @param at the node that is to be deleted
2421             * @return an object that may be used to start another request
2422             */
2423            public BatchConjunction delete( Node at ) {
2424                return delete(at.getLocation());
2425            }
2426    
2427            /**
2428             * Request to delete the node at the given location.
2429             * <p>
2430             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2431             * called.
2432             * </p>
2433             * 
2434             * @param at the location of the node that is to be deleted
2435             * @return an object that may be used to start another request
2436             */
2437            public BatchConjunction delete( Location at ) {
2438                assertNotExecuted();
2439                this.requestQueue.deleteBranch(at, getCurrentWorkspaceName());
2440                return nextRequests;
2441            }
2442    
2443            /**
2444             * Request to delete the node at the given path.
2445             * <p>
2446             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2447             * called.
2448             * </p>
2449             * 
2450             * @param atPath the path of the node that is to be deleted
2451             * @return an object that may be used to start another request
2452             */
2453            public BatchConjunction delete( String atPath ) {
2454                return delete(Location.create(createPath(atPath)));
2455            }
2456    
2457            /**
2458             * Request to delete the node at the given path.
2459             * <p>
2460             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2461             * called.
2462             * </p>
2463             * 
2464             * @param at the path of the node that is to be deleted
2465             * @return an object that may be used to start another request
2466             */
2467            public BatchConjunction delete( Path at ) {
2468                return delete(Location.create(at));
2469            }
2470    
2471            /**
2472             * Request to delete the node with the given UUID.
2473             * <p>
2474             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2475             * called.
2476             * </p>
2477             * 
2478             * @param at the UUID of the node that is to be deleted
2479             * @return an object that may be used to start another request
2480             */
2481            public BatchConjunction delete( UUID at ) {
2482                return delete(Location.create(at));
2483            }
2484    
2485            /**
2486             * Request to delete the node with the given unique identification property.
2487             * <p>
2488             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2489             * called.
2490             * </p>
2491             * 
2492             * @param idProperty the unique identifying property of the node that is to be deleted
2493             * @return an object that may be used to start another request
2494             */
2495            public BatchConjunction delete( Property idProperty ) {
2496                return delete(Location.create(idProperty));
2497            }
2498    
2499            /**
2500             * Request to delete the node with the given identification properties. The identification properties should uniquely
2501             * identify a single node.
2502             * <p>
2503             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2504             * called.
2505             * </p>
2506             * 
2507             * @param firstIdProperty the first identification property of the node that is to be copied
2508             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
2509             * @return an object that may be used to start another request
2510             */
2511            public BatchConjunction delete( Property firstIdProperty,
2512                                            Property... additionalIdProperties ) {
2513                return delete(Location.create(firstIdProperty, additionalIdProperties));
2514            }
2515    
2516            /**
2517             * Request to delete the node with the given identification properties. The identification properties should uniquely
2518             * identify a single node.
2519             * <p>
2520             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2521             * called.
2522             * </p>
2523             * 
2524             * @param idProperties the identification property of the node that is to be copied
2525             * @return an object that may be used to start another request
2526             */
2527            public BatchConjunction delete( Iterable<Property> idProperties ) {
2528                return delete(Location.create(idProperties));
2529            }
2530    
2531            /**
2532             * Begin the request to create a node located at the supplied path.
2533             * <p>
2534             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2535             * called.
2536             * </p>
2537             * 
2538             * @param atPath the path to the node that is to be created.
2539             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2540             *         node where the node is to be created
2541             */
2542            public Create<Batch> create( String atPath ) {
2543                return create(createPath(atPath));
2544            }
2545    
2546            /**
2547             * Begin the request to create a node located at the supplied path.
2548             * <p>
2549             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2550             * called.
2551             * </p>
2552             * 
2553             * @param atPath the path to the node that is to be created.
2554             * @param property a property for the new node
2555             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2556             *         node where the node is to be created
2557             */
2558            public Create<Batch> create( String atPath,
2559                                         Property property ) {
2560                return create(createPath(atPath)).with(property);
2561            }
2562    
2563            /**
2564             * Begin the request to create a node located at the supplied path.
2565             * <p>
2566             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2567             * called.
2568             * </p>
2569             * 
2570             * @param atPath the path to the node that is to be created.
2571             * @param firstProperty a property for the new node
2572             * @param additionalProperties additional properties for the new node
2573             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2574             *         node where the node is to be created
2575             */
2576            public Create<Batch> create( String atPath,
2577                                         Property firstProperty,
2578                                         Property... additionalProperties ) {
2579                return create(createPath(atPath)).with(firstProperty, additionalProperties);
2580            }
2581    
2582            /**
2583             * Begin the request to create a node located at the supplied path.
2584             * <p>
2585             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2586             * called.
2587             * </p>
2588             * 
2589             * @param at the path to the node that is to be created.
2590             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2591             *         node where the node is to be created
2592             */
2593            public final Create<Batch> create( Path at ) {
2594                assertNotExecuted();
2595                CheckArg.isNotNull(at, "at");
2596                Path parent = at.getParent();
2597                Name name = at.getLastSegment().getName();
2598                return create(Location.create(parent), name);
2599            }
2600    
2601            protected final CreateAction<Batch> create( Location parent,
2602                                                        Name child ) {
2603                return new CreateAction<Batch>(this, parent, getCurrentWorkspaceName(), child) {
2604                    @Override
2605                    protected Batch submit( Location parent,
2606                                            String workspaceName,
2607                                            Name childName,
2608                                            Collection<Property> properties ) {
2609                        requestQueue.createNode(parent, workspaceName, childName, properties.iterator());
2610                        return Batch.this;
2611                    }
2612    
2613                    /**
2614                     * {@inheritDoc}
2615                     * 
2616                     * @see org.jboss.dna.graph.Graph.Executable#execute()
2617                     */
2618                    public Results execute() {
2619                        and();
2620                        return Batch.this.execute();
2621                    }
2622                };
2623            }
2624    
2625            /**
2626             * Begin the request to create a node located at the supplied path.
2627             * <p>
2628             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2629             * called.
2630             * </p>
2631             * 
2632             * @param at the path to the node that is to be created.
2633             * @param properties the iterator over the properties for the new node
2634             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2635             *         node where the node is to be created
2636             */
2637            public Create<Batch> create( Path at,
2638                                         Iterable<Property> properties ) {
2639                Create<Batch> action = create(at);
2640                for (Property property : properties) {
2641                    action.and(property);
2642                }
2643                return action;
2644            }
2645    
2646            /**
2647             * Begin the request to create a node located at the supplied path.
2648             * <p>
2649             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2650             * called.
2651             * </p>
2652             * 
2653             * @param at the path to the node that is to be created.
2654             * @param property a property for the new node
2655             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2656             *         node where the node is to be created
2657             */
2658            public Create<Batch> create( Path at,
2659                                         Property property ) {
2660                return create(at).with(property);
2661            }
2662    
2663            /**
2664             * Begin the request to create a node located at the supplied path.
2665             * <p>
2666             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
2667             * called.
2668             * </p>
2669             * 
2670             * @param at the path to the node that is to be created.
2671             * @param firstProperty a property for the new node
2672             * @param additionalProperties additional properties for the new node
2673             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
2674             *         node where the node is to be created
2675             */
2676            public Create<Batch> create( Path at,
2677                                         Property firstProperty,
2678                                         Property... additionalProperties ) {
2679                return create(at).with(firstProperty, additionalProperties);
2680            }
2681    
2682            /**
2683             * Begin the request to create a node under the existing parent node at the supplied location. This request is submitted
2684             * to the repository after the returned components are completed.
2685             * 
2686             * @param parent the location of the parent
2687             * @return the object used to start creating a node
2688             */
2689            public CreateNodeNamed<Batch> createUnder( Location parent ) {
2690                CheckArg.isNotNull(parent, "parent");
2691                return new CreateNodeNamedAction<Batch>(this, parent) {
2692                    @Override
2693                    protected CreateAction<Batch> createWith( Batch batch,
2694                                                              Location parent,
2695                                                              Name childName ) {
2696                        return Batch.this.create(parent, childName);
2697                    }
2698                };
2699            }
2700    
2701            /**
2702             * Set the properties on a node.
2703             * 
2704             * @param properties the properties to set
2705             * @return the interface that should be used to specify the node on which the properties are to be set.
2706             */
2707            public On<BatchConjunction> set( final Property... properties ) {
2708                return new On<BatchConjunction>() {
2709                    public BatchConjunction on( Location location ) {
2710                        requestQueue.setProperties(location, getCurrentWorkspaceName(), properties);
2711                        return nextRequests;
2712                    }
2713    
2714                    public BatchConjunction on( String path ) {
2715                        return on(Location.create(createPath(path)));
2716                    }
2717    
2718                    public BatchConjunction on( Path path ) {
2719                        return on(Location.create(path));
2720                    }
2721    
2722                    public BatchConjunction on( Property idProperty ) {
2723                        return on(Location.create(idProperty));
2724                    }
2725    
2726                    public BatchConjunction on( Property firstIdProperty,
2727                                                Property... additionalIdProperties ) {
2728                        return on(Location.create(firstIdProperty, additionalIdProperties));
2729                    }
2730    
2731                    public BatchConjunction on( Iterable<Property> idProperties ) {
2732                        return on(Location.create(idProperties));
2733                    }
2734    
2735                    public BatchConjunction on( UUID uuid ) {
2736                        return on(Location.create(uuid));
2737                    }
2738                };
2739            }
2740    
2741            /**
2742             * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
2743             * value(s) and the location of the node onto which the property should be set.
2744             * 
2745             * @param propertyName the property name
2746             * @return the interface used to specify the values
2747             */
2748            public SetValues<BatchConjunction> set( String propertyName ) {
2749                Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
2750                return set(name);
2751            }
2752    
2753            /**
2754             * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
2755             * value(s) and the location of the node onto which the property should be set.
2756             * 
2757             * @param propertyName the property name
2758             * @return the interface used to specify the values
2759             */
2760            public SetValues<BatchConjunction> set( final Name propertyName ) {
2761                return new SetValues<BatchConjunction>() {
2762                    public SetValuesTo<BatchConjunction> on( final Location location ) {
2763                        return new SetValuesTo<BatchConjunction>() {
2764                            public BatchConjunction to( Node value ) {
2765                                return to(value.getLocation());
2766                            }
2767    
2768                            public BatchConjunction to( Location value ) {
2769                                Reference ref = (Reference)convertReferenceValue(value);
2770                                Property property = getContext().getPropertyFactory().create(propertyName, ref);
2771                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2772                                return nextRequests;
2773                            }
2774    
2775                            protected BatchConjunction toValue( Object value ) {
2776                                Property property = getContext().getPropertyFactory().create(propertyName, value);
2777                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2778                                return nextRequests;
2779                            }
2780    
2781                            public BatchConjunction to( String value ) {
2782                                return toValue(value);
2783                            }
2784    
2785                            public BatchConjunction to( int value ) {
2786                                return toValue(Integer.valueOf(value));
2787                            }
2788    
2789                            public BatchConjunction to( long value ) {
2790                                return toValue(Long.valueOf(value));
2791                            }
2792    
2793                            public BatchConjunction to( boolean value ) {
2794                                return toValue(Boolean.valueOf(value));
2795                            }
2796    
2797                            public BatchConjunction to( float value ) {
2798                                return toValue(Float.valueOf(value));
2799                            }
2800    
2801                            public BatchConjunction to( double value ) {
2802                                return toValue(Double.valueOf(value));
2803                            }
2804    
2805                            public BatchConjunction to( BigDecimal value ) {
2806                                return toValue(value);
2807                            }
2808    
2809                            public BatchConjunction to( Calendar value ) {
2810                                return toValue(value);
2811                            }
2812    
2813                            public BatchConjunction to( Date value ) {
2814                                return toValue(value);
2815                            }
2816    
2817                            public BatchConjunction to( DateTime value ) {
2818                                return toValue(value);
2819                            }
2820    
2821                            public BatchConjunction to( Name value ) {
2822                                return toValue(value);
2823                            }
2824    
2825                            public BatchConjunction to( Path value ) {
2826                                return toValue(value);
2827                            }
2828    
2829                            public BatchConjunction to( Reference value ) {
2830                                return toValue(value);
2831                            }
2832    
2833                            public BatchConjunction to( URI value ) {
2834                                return toValue(value);
2835                            }
2836    
2837                            public BatchConjunction to( UUID value ) {
2838                                return toValue(value);
2839                            }
2840    
2841                            public BatchConjunction to( Binary value ) {
2842                                return toValue(value);
2843                            }
2844    
2845                            public BatchConjunction to( byte[] value ) {
2846                                return toValue(value);
2847                            }
2848    
2849                            public BatchConjunction to( InputStream stream,
2850                                                        long approximateLength ) {
2851                                Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
2852                                return toValue(value);
2853                            }
2854    
2855                            public BatchConjunction to( Reader reader,
2856                                                        long approximateLength ) {
2857                                Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
2858                                return toValue(value);
2859                            }
2860    
2861                            public BatchConjunction to( Object value ) {
2862                                value = convertReferenceValue(value);
2863                                Property property = getContext().getPropertyFactory().create(propertyName, value);
2864                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2865                                return nextRequests;
2866                            }
2867    
2868                            public BatchConjunction to( Object firstValue,
2869                                                        Object... otherValues ) {
2870                                firstValue = convertReferenceValue(firstValue);
2871                                for (int i = 0, len = otherValues.length; i != len; ++i) {
2872                                    otherValues[i] = convertReferenceValue(otherValues[i]);
2873                                }
2874                                Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
2875                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2876                                return nextRequests;
2877                            }
2878    
2879                            public BatchConjunction to( Iterable<?> values ) {
2880                                List<Object> valueList = new LinkedList<Object>();
2881                                for (Object value : values) {
2882                                    value = convertReferenceValue(value);
2883                                    valueList.add(value);
2884                                }
2885                                Property property = getContext().getPropertyFactory().create(propertyName, valueList);
2886                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2887                                return nextRequests;
2888                            }
2889    
2890                            public BatchConjunction to( Iterator<?> values ) {
2891                                List<Object> valueList = new LinkedList<Object>();
2892                                while (values.hasNext()) {
2893                                    Object value = values.next();
2894                                    valueList.add(value);
2895                                }
2896                                Property property = getContext().getPropertyFactory().create(propertyName, valueList);
2897                                requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
2898                                return nextRequests;
2899                            }
2900    
2901                        };
2902                    }
2903    
2904                    public SetValuesTo<BatchConjunction> on( String path ) {
2905                        return on(Location.create(createPath(path)));
2906                    }
2907    
2908                    public SetValuesTo<BatchConjunction> on( Path path ) {
2909                        return on(Location.create(path));
2910                    }
2911    
2912                    public SetValuesTo<BatchConjunction> on( Property idProperty ) {
2913                        return on(Location.create(idProperty));
2914                    }
2915    
2916                    public SetValuesTo<BatchConjunction> on( Property firstIdProperty,
2917                                                             Property... additionalIdProperties ) {
2918                        return on(Location.create(firstIdProperty, additionalIdProperties));
2919                    }
2920    
2921                    public SetValuesTo<BatchConjunction> on( Iterable<Property> idProperties ) {
2922                        return on(Location.create(idProperties));
2923                    }
2924    
2925                    public SetValuesTo<BatchConjunction> on( UUID uuid ) {
2926                        return on(Location.create(uuid));
2927                    }
2928    
2929                    public On<BatchConjunction> to( Node value ) {
2930                        Object reference = convertReferenceValue(value);
2931                        return set(getContext().getPropertyFactory().create(propertyName, reference));
2932                    }
2933    
2934                    public On<BatchConjunction> to( Location value ) {
2935                        Object reference = convertReferenceValue(value);
2936                        return set(getContext().getPropertyFactory().create(propertyName, reference));
2937                    }
2938    
2939                    protected On<BatchConjunction> toValue( Object value ) {
2940                        return set(getContext().getPropertyFactory().create(propertyName, value));
2941                    }
2942    
2943                    public On<BatchConjunction> to( String value ) {
2944                        return toValue(value);
2945                    }
2946    
2947                    public On<BatchConjunction> to( int value ) {
2948                        return toValue(Integer.valueOf(value));
2949                    }
2950    
2951                    public On<BatchConjunction> to( long value ) {
2952                        return toValue(Long.valueOf(value));
2953                    }
2954    
2955                    public On<BatchConjunction> to( boolean value ) {
2956                        return toValue(Boolean.valueOf(value));
2957                    }
2958    
2959                    public On<BatchConjunction> to( float value ) {
2960                        return toValue(Float.valueOf(value));
2961                    }
2962    
2963                    public On<BatchConjunction> to( double value ) {
2964                        return toValue(Double.valueOf(value));
2965                    }
2966    
2967                    public On<BatchConjunction> to( BigDecimal value ) {
2968                        return toValue(value);
2969                    }
2970    
2971                    public On<BatchConjunction> to( Calendar value ) {
2972                        return toValue(value);
2973                    }
2974    
2975                    public On<BatchConjunction> to( Date value ) {
2976                        return toValue(value);
2977                    }
2978    
2979                    public On<BatchConjunction> to( DateTime value ) {
2980                        return toValue(value);
2981                    }
2982    
2983                    public On<BatchConjunction> to( Name value ) {
2984                        return toValue(value);
2985                    }
2986    
2987                    public On<BatchConjunction> to( Path value ) {
2988                        return toValue(value);
2989                    }
2990    
2991                    public On<BatchConjunction> to( Reference value ) {
2992                        return toValue(value);
2993                    }
2994    
2995                    public On<BatchConjunction> to( URI value ) {
2996                        return toValue(value);
2997                    }
2998    
2999                    public On<BatchConjunction> to( UUID value ) {
3000                        return toValue(value);
3001                    }
3002    
3003                    public On<BatchConjunction> to( Binary value ) {
3004                        return toValue(value);
3005                    }
3006    
3007                    public On<BatchConjunction> to( byte[] value ) {
3008                        return toValue(value);
3009                    }
3010    
3011                    public On<BatchConjunction> to( InputStream stream,
3012                                                    long approximateLength ) {
3013                        Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
3014                        return toValue(value);
3015                    }
3016    
3017                    public On<BatchConjunction> to( Reader reader,
3018                                                    long approximateLength ) {
3019                        Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
3020                        return toValue(value);
3021                    }
3022    
3023                    public On<BatchConjunction> to( Object value ) {
3024                        value = convertReferenceValue(value);
3025                        return set(getContext().getPropertyFactory().create(propertyName, value));
3026                    }
3027    
3028                    public On<BatchConjunction> to( Object firstValue,
3029                                                    Object... otherValues ) {
3030                        Object[] values = new Object[otherValues.length + 1];
3031                        values[0] = convertReferenceValue(firstValue);
3032                        for (int i = 0, len = otherValues.length; i != len; ++i) {
3033                            values[i + 1] = convertReferenceValue(otherValues[i]);
3034                        }
3035                        return set(getContext().getPropertyFactory().create(propertyName, values));
3036                    }
3037    
3038                    public On<BatchConjunction> to( Iterable<?> values ) {
3039                        List<Object> valueList = new LinkedList<Object>();
3040                        for (Object value : values) {
3041                            value = convertReferenceValue(value);
3042                            valueList.add(value);
3043                        }
3044                        return set(getContext().getPropertyFactory().create(propertyName, valueList));
3045                    }
3046    
3047                    public On<BatchConjunction> to( Iterator<?> values ) {
3048                        List<Object> valueList = new LinkedList<Object>();
3049                        while (values.hasNext()) {
3050                            Object value = values.next();
3051                            valueList.add(value);
3052                        }
3053                        return set(getContext().getPropertyFactory().create(propertyName, valueList));
3054                    }
3055                };
3056            }
3057    
3058            /**
3059             * Remove properties from the node at the given location.
3060             * 
3061             * @param propertyNames the names of the properties to be removed
3062             * @return the remove request object that should be used to specify the node from which the properties are to be removed.
3063             */
3064            public On<BatchConjunction> remove( final Name... propertyNames ) {
3065                return new On<BatchConjunction>() {
3066                    public BatchConjunction on( Location location ) {
3067                        requestQueue.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
3068                        return nextRequests;
3069                    }
3070    
3071                    public BatchConjunction on( String path ) {
3072                        return on(Location.create(createPath(path)));
3073                    }
3074    
3075                    public BatchConjunction on( Path path ) {
3076                        return on(Location.create(path));
3077                    }
3078    
3079                    public BatchConjunction on( Property idProperty ) {
3080                        return on(Location.create(idProperty));
3081                    }
3082    
3083                    public BatchConjunction on( Property firstIdProperty,
3084                                                Property... additionalIdProperties ) {
3085                        return on(Location.create(firstIdProperty, additionalIdProperties));
3086                    }
3087    
3088                    public BatchConjunction on( Iterable<Property> idProperties ) {
3089                        return on(Location.create(idProperties));
3090                    }
3091    
3092                    public BatchConjunction on( UUID uuid ) {
3093                        return on(Location.create(uuid));
3094                    }
3095                };
3096            }
3097    
3098            /**
3099             * Remove properties from the node at the given location.
3100             * 
3101             * @param propertyNames the names of the properties to be removed
3102             * @return the remove request object that should be used to specify the node from which the properties are to be removed.
3103             */
3104            public On<BatchConjunction> remove( String... propertyNames ) {
3105                NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
3106                int number = propertyNames.length;
3107                final Name[] names = new Name[number];
3108                for (int i = 0; i != number; ++i) {
3109                    names[i] = nameFactory.create(propertyNames[i]);
3110                }
3111                return new On<BatchConjunction>() {
3112                    public BatchConjunction on( Location location ) {
3113                        requestQueue.removeProperties(location, getCurrentWorkspaceName(), names);
3114                        return nextRequests;
3115                    }
3116    
3117                    public BatchConjunction on( String path ) {
3118                        return on(Location.create(createPath(path)));
3119                    }
3120    
3121                    public BatchConjunction on( Path path ) {
3122                        return on(Location.create(path));
3123                    }
3124    
3125                    public BatchConjunction on( Property idProperty ) {
3126                        return on(Location.create(idProperty));
3127                    }
3128    
3129                    public BatchConjunction on( Property firstIdProperty,
3130                                                Property... additionalIdProperties ) {
3131                        return on(Location.create(firstIdProperty, additionalIdProperties));
3132                    }
3133    
3134                    public BatchConjunction on( Iterable<Property> idProperties ) {
3135                        return on(Location.create(idProperties));
3136                    }
3137    
3138                    public BatchConjunction on( UUID uuid ) {
3139                        return on(Location.create(uuid));
3140                    }
3141                };
3142            }
3143    
3144            /**
3145             * Request to read the node with the supplied UUID.
3146             * <p>
3147             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3148             * called.
3149             * </p>
3150             * 
3151             * @param uuid the UUID of the node that is to be read
3152             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3153             */
3154            public BatchConjunction read( UUID uuid ) {
3155                return read(Location.create(uuid));
3156            }
3157    
3158            /**
3159             * Request to read the node at the supplied location.
3160             * <p>
3161             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3162             * called.
3163             * </p>
3164             * 
3165             * @param location the location of the node that is to be read
3166             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3167             */
3168            public BatchConjunction read( Location location ) {
3169                assertNotExecuted();
3170                requestQueue.readNode(location, getCurrentWorkspaceName());
3171                return nextRequests;
3172            }
3173    
3174            /**
3175             * Request to read the node at the supplied path.
3176             * <p>
3177             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3178             * called.
3179             * </p>
3180             * 
3181             * @param path the path of the node that is to be read
3182             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3183             */
3184            public BatchConjunction read( String path ) {
3185                return read(Location.create(createPath(path)));
3186            }
3187    
3188            /**
3189             * Request to read the node at the supplied path.
3190             * <p>
3191             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3192             * called.
3193             * </p>
3194             * 
3195             * @param path the path of the node that is to be read
3196             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3197             */
3198            public BatchConjunction read( Path path ) {
3199                return read(Location.create(path));
3200            }
3201    
3202            /**
3203             * Request to read the node with the supplied unique identifier property.
3204             * <p>
3205             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3206             * called.
3207             * </p>
3208             * 
3209             * @param idProperty the identification property that is unique to the node that is to be read
3210             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3211             */
3212            public BatchConjunction read( Property idProperty ) {
3213                return read(Location.create(idProperty));
3214            }
3215    
3216            /**
3217             * Request to read the node with the supplied unique identifier properties.
3218             * <p>
3219             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3220             * called.
3221             * </p>
3222             * 
3223             * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
3224             * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be
3225             *        read
3226             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3227             */
3228            public BatchConjunction read( Property firstIdProperty,
3229                                          Property... additionalIdProperties ) {
3230                return read(Location.create(firstIdProperty, additionalIdProperties));
3231            }
3232    
3233            /**
3234             * Request to read the node with the supplied unique identifier properties.
3235             * <p>
3236             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3237             * called.
3238             * </p>
3239             * 
3240             * @param idProperties the identification properties that uniquely identify the node that is to be read
3241             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3242             */
3243            public BatchConjunction read( Iterable<Property> idProperties ) {
3244                return read(Location.create(idProperties));
3245            }
3246    
3247            /**
3248             * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
3249             * returned {@link On} object.
3250             * <p>
3251             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3252             * called.
3253             * </p>
3254             * 
3255             * @param propertyName the name of the property that is to be read
3256             * @return the object that is used to specified the node whose property is to be read
3257             */
3258            public On<BatchConjunction> readProperty( String propertyName ) {
3259                assertNotExecuted();
3260                Name name = Graph.this.getContext().getValueFactories().getNameFactory().create(propertyName);
3261                return readProperty(name);
3262            }
3263    
3264            /**
3265             * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
3266             * returned {@link On} object.
3267             * <p>
3268             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3269             * called.
3270             * </p>
3271             * 
3272             * @param name the name of the property that is to be read
3273             * @return the object that is used to specified the node whose property is to be read
3274             */
3275            public On<BatchConjunction> readProperty( final Name name ) {
3276                assertNotExecuted();
3277                return new On<BatchConjunction>() {
3278                    public BatchConjunction on( String path ) {
3279                        return on(Location.create(createPath(path)));
3280                    }
3281    
3282                    public BatchConjunction on( Path path ) {
3283                        return on(Location.create(path));
3284                    }
3285    
3286                    public BatchConjunction on( Property idProperty ) {
3287                        return on(Location.create(idProperty));
3288                    }
3289    
3290                    public BatchConjunction on( Property firstIdProperty,
3291                                                Property... additionalIdProperties ) {
3292                        return on(Location.create(firstIdProperty, additionalIdProperties));
3293                    }
3294    
3295                    public BatchConjunction on( Iterable<Property> idProperties ) {
3296                        return on(Location.create(idProperties));
3297                    }
3298    
3299                    public BatchConjunction on( UUID uuid ) {
3300                        return on(Location.create(uuid));
3301                    }
3302    
3303                    public BatchConjunction on( Location at ) {
3304                        requestQueue.readProperty(at, getCurrentWorkspaceName(), name);
3305                        return Batch.this.nextRequests;
3306                    }
3307                };
3308            }
3309    
3310            /**
3311             * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
3312             * object.
3313             * <p>
3314             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3315             * called.
3316             * </p>
3317             * 
3318             * @return the object that is used to specified the node whose properties are to be read,
3319             */
3320            public On<BatchConjunction> readProperties() {
3321                assertNotExecuted();
3322                return new On<BatchConjunction>() {
3323                    public BatchConjunction on( Location location ) {
3324                        requestQueue.readAllProperties(location, getCurrentWorkspaceName());
3325                        return Batch.this.nextRequests;
3326                    }
3327    
3328                    public BatchConjunction on( String path ) {
3329                        return on(Location.create(createPath(path)));
3330                    }
3331    
3332                    public BatchConjunction on( Path path ) {
3333                        return on(Location.create(path));
3334                    }
3335    
3336                    public BatchConjunction on( Property idProperty ) {
3337                        return on(Location.create(idProperty));
3338                    }
3339    
3340                    public BatchConjunction on( Property firstIdProperty,
3341                                                Property... additionalIdProperties ) {
3342                        return on(Location.create(firstIdProperty, additionalIdProperties));
3343                    }
3344    
3345                    public BatchConjunction on( Iterable<Property> idProperties ) {
3346                        return on(Location.create(idProperties));
3347                    }
3348    
3349                    public BatchConjunction on( UUID uuid ) {
3350                        return on(Location.create(uuid));
3351                    }
3352                };
3353            }
3354    
3355            /**
3356             * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
3357             * object.
3358             * <p>
3359             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3360             * called.
3361             * </p>
3362             * 
3363             * @return the object that is used to specified the node whose children are to be read
3364             */
3365            public Of<BatchConjunction> readChildren() {
3366                assertNotExecuted();
3367                return new Of<BatchConjunction>() {
3368                    public BatchConjunction of( String path ) {
3369                        return of(Location.create(createPath(path)));
3370                    }
3371    
3372                    public BatchConjunction of( Path path ) {
3373                        return of(Location.create(path));
3374                    }
3375    
3376                    public BatchConjunction of( Property idProperty ) {
3377                        return of(Location.create(idProperty));
3378                    }
3379    
3380                    public BatchConjunction of( Property firstIdProperty,
3381                                                Property... additionalIdProperties ) {
3382                        return of(Location.create(firstIdProperty, additionalIdProperties));
3383                    }
3384    
3385                    public BatchConjunction of( Iterable<Property> idProperties ) {
3386                        return of(Location.create(idProperties));
3387                    }
3388    
3389                    public BatchConjunction of( UUID uuid ) {
3390                        return of(Location.create(uuid));
3391                    }
3392    
3393                    public BatchConjunction of( Location at ) {
3394                        requestQueue.readAllChildren(at, getCurrentWorkspaceName());
3395                        return Batch.this.nextRequests;
3396                    }
3397                };
3398            }
3399    
3400            /**
3401             * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code>
3402             * in the resulting {@link At} object.
3403             * <p>
3404             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3405             * called.
3406             * </p>
3407             * 
3408             * @param depth the maximum depth of the subgraph that should be read
3409             * @return the component that should be used to specify the location of the node that is the top of the subgraph
3410             */
3411            public At<BatchConjunction> readSubgraphOfDepth( final int depth ) {
3412                assertNotExecuted();
3413                return new At<BatchConjunction>() {
3414                    public BatchConjunction at( Location location ) {
3415                        requestQueue.readBranch(location, getCurrentWorkspaceName());
3416                        return Batch.this.nextRequests;
3417                    }
3418    
3419                    public BatchConjunction at( String path ) {
3420                        return at(Location.create(createPath(path)));
3421                    }
3422    
3423                    public BatchConjunction at( Path path ) {
3424                        return at(Location.create(path));
3425                    }
3426    
3427                    public BatchConjunction at( UUID uuid ) {
3428                        return at(Location.create(uuid));
3429                    }
3430    
3431                    public BatchConjunction at( Property idProperty ) {
3432                        return at(Location.create(idProperty));
3433                    }
3434    
3435                    public BatchConjunction at( Property firstIdProperty,
3436                                                Property... additionalIdProperties ) {
3437                        return at(Location.create(firstIdProperty, additionalIdProperties));
3438                    }
3439    
3440                    public BatchConjunction at( Iterable<Property> idProperties ) {
3441                        return at(Location.create(idProperties));
3442                    }
3443                };
3444            }
3445    
3446            /**
3447             * {@inheritDoc}
3448             * 
3449             * @see org.jboss.dna.graph.Graph.Executable#execute()
3450             */
3451            public Results execute() {
3452                executed = true;
3453                Request request = requestQueue.pop();
3454                if (request == null) {
3455                    return new BatchResults();
3456                }
3457                Graph.this.execute(request);
3458                if (request instanceof CompositeRequest) {
3459                    CompositeRequest composite = (CompositeRequest)request;
3460                    return new BatchResults(composite.getRequests());
3461                }
3462                return new BatchResults(request);
3463            }
3464        }
3465    
3466        /**
3467         * Utility method for checking a property value. If the value is a {@link Node} or {@link Location}, a {@link Reference} value
3468         * is created (if the node/location has a UUID); otherwise, the value is returned as is.
3469         * 
3470         * @param value the property value
3471         * @return the property value, which may be a {@link Reference} if the input value is a Node or Location
3472         */
3473        protected Object convertReferenceValue( Object value ) {
3474            if (value instanceof Node) {
3475                Node node = (Node)value;
3476                UUID uuid = node.getLocation().getUuid();
3477                if (uuid == null) {
3478                    // Look for a property ...
3479                    Property uuidProperty = node.getProperty(DnaLexicon.UUID);
3480                    if (uuidProperty != null) {
3481                        uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
3482                    } else {
3483                        uuidProperty = node.getProperty(JcrLexicon.UUID);
3484                        if (uuidProperty != null) {
3485                            uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
3486                        }
3487                    }
3488                }
3489                if (uuid == null) {
3490                    String nodeString = node.getLocation().getString(getContext().getNamespaceRegistry());
3491                    String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
3492                    throw new IllegalArgumentException(msg);
3493                }
3494                return getContext().getValueFactories().getReferenceFactory().create(uuid);
3495            }
3496            if (value instanceof Location) {
3497                Location location = (Location)value;
3498                UUID uuid = location.getUuid();
3499                if (uuid == null) {
3500                    String nodeString = location.getString(getContext().getNamespaceRegistry());
3501                    String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
3502                    throw new IllegalArgumentException(msg);
3503                }
3504                return getContext().getValueFactories().getReferenceFactory().create(uuid);
3505            }
3506            return value;
3507        }
3508    
3509        /**
3510         * The interface used to specify the name of a new workspace.
3511         */
3512        public interface NameWorkspace {
3513    
3514            /**
3515             * Specify the name of the new workspace that is to be created.
3516             * 
3517             * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
3518             * @return the workspace; never null
3519             * @throws IllegalArgumentException if the name of the new workspace is null
3520             * @throws InvalidWorkspaceException if there is already an existing workspace with the supplied name
3521             */
3522            Workspace named( String workspaceName );
3523    
3524            /**
3525             * Specify the name of the new workspace that is to be created. If a workspace with the supplied name already exists, the
3526             * new workspace name will be adjusted so that it is unique.
3527             * 
3528             * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
3529             * @return the workspace; never null
3530             * @throws IllegalArgumentException if the name of the new workspace is null
3531             */
3532            Workspace namedSomethingLike( String workspaceName );
3533        }
3534    
3535        /**
3536         * The interface used to create a new workspace.
3537         */
3538        public interface CreateWorkspace extends NameWorkspace {
3539            /**
3540             * Specify that the new workspace should be initialized as a clone of another existing workspace.
3541             * 
3542             * @param originalWorkspaceName the name of the existing workspace that will be cloned to create the new workspace;
3543             * @return the interface that should be used to set the name of the new workspace; never null
3544             * @throws IllegalArgumentException if the name of the original workspace is null
3545             * @throws InvalidWorkspaceException if there is no such workspace with the supplied name
3546             */
3547            NameWorkspace clonedFrom( String originalWorkspaceName );
3548        }
3549    
3550        /**
3551         * A interface used to execute the accumulated {@link Batch requests}.
3552         * 
3553         * @author Randall Hauch
3554         * @param <NodeType> the type of node that is returned
3555         */
3556        public interface Executable<NodeType extends Node> {
3557            /**
3558             * Stop accumulating the requests, submit them to the repository source, and return the results.
3559             * 
3560             * @return the results containing the requested information from the repository.
3561             * @throws PathNotFoundException if a request used a node that did not exist
3562             * @throws InvalidRequestException if a request was not valid
3563             * @throws InvalidWorkspaceException if the workspace used in a request was not valid
3564             * @throws UnsupportedRequestException if a request was not supported by the source
3565             * @throws RepositorySourceException if an error occurs during execution
3566             * @throws RuntimeException if a runtime error occurs during execution
3567             */
3568            Results execute();
3569        }
3570    
3571        /**
3572         * A interface that can be used to finish the current request and start another.
3573         * 
3574         * @param <Next> the interface that will be used to start another request
3575         * @author Randall Hauch
3576         */
3577        public interface Conjunction<Next> {
3578            /**
3579             * Finish the request and prepare to start another.
3580             * 
3581             * @return the interface that can be used to start another request; never null
3582             */
3583            Next and();
3584        }
3585    
3586        /**
3587         * A component that defines the location into which a node should be copied or moved.
3588         * 
3589         * @param <Next> The interface that is to be returned when this request is completed
3590         * @author Randall Hauch
3591         */
3592        public interface Into<Next> {
3593            /**
3594             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3595             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3596             * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
3597             * {@link To#to(Location)} instead.
3598             * 
3599             * @param parentLocation the location of the new parent
3600             * @return the interface for additional requests or actions
3601             * @see To#to(Location)
3602             */
3603            Next into( Location parentLocation );
3604    
3605            /**
3606             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3607             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3608             * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
3609             * {@link To#to(String)} instead.
3610             * 
3611             * @param parentPath the path of the new parent
3612             * @return the interface for additional requests or actions
3613             * @see To#to(String)
3614             */
3615            Next into( String parentPath );
3616    
3617            /**
3618             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3619             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3620             * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
3621             * {@link To#to(Path)} instead.
3622             * 
3623             * @param parentPath the path of the new parent
3624             * @return the interface for additional requests or actions
3625             * @see To#to(Path)
3626             */
3627            Next into( Path parentPath );
3628    
3629            /**
3630             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3631             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3632             * same-name-sibling index).
3633             * 
3634             * @param parentUuid the UUID of the new parent
3635             * @return the interface for additional requests or actions
3636             */
3637            Next into( UUID parentUuid );
3638    
3639            /**
3640             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3641             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3642             * same-name-sibling index).
3643             * 
3644             * @param parentIdProperty the property that uniquely identifies the new parent
3645             * @return the interface for additional requests or actions
3646             */
3647            Next into( Property parentIdProperty );
3648    
3649            /**
3650             * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
3651             * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
3652             * same-name-sibling index).
3653             * 
3654             * @param firstParentIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies
3655             *        the new parent
3656             * @param additionalParentIdProperties the additional properties that, with the <code>additionalIdProperties</code>,
3657             *        uniquely identifies the new parent
3658             * @return the interface for additional requests or actions
3659             */
3660            Next into( Property firstParentIdProperty,
3661                       Property... additionalParentIdProperties );
3662        }
3663    
3664        /**
3665         * A component that defines the location to which a node should be copied or moved.
3666         * 
3667         * @param <Next> The interface that is to be returned when this request is completed
3668         * @author Randall Hauch
3669         */
3670        public interface To<Next> {
3671            /**
3672             * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
3673             * {@link Into#into(Location)}, which specifies the location of the parent and which assumes the new node should have the
3674             * same name as the original, this method allows the caller to specify a new name for the new node.
3675             * 
3676             * @param desiredLocation the desired location for the new node, which must have a {@link Location#getPath() path}
3677             * @return the interface for additional requests or actions
3678             * @see Into#into(Location)
3679             */
3680            Next to( Location desiredLocation );
3681    
3682            /**
3683             * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
3684             * {@link Into#into(String)}, which specifies the location of the parent and which assumes the new node should have the
3685             * same name as the original, this method allows the caller to specify a new name for the new node.
3686             * 
3687             * @param desiredPath the path for the new node
3688             * @return the interface for additional requests or actions
3689             * @see Into#into(String)
3690             */
3691            Next to( String desiredPath );
3692    
3693            /**
3694             * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
3695             * {@link Into#into(Path)} , which specifies the location of the parent and which assumes the new node should have the
3696             * same name as the original, this method allows the caller to specify a new name for the new node.
3697             * 
3698             * @param desiredPath the path for the new node
3699             * @return the interface for additional requests or actions
3700             * @see Into#into(Path)
3701             */
3702            Next to( Path desiredPath );
3703        }
3704    
3705        /**
3706         * A component that defines a new name for a node.
3707         * 
3708         * @param <Next> The interface that is to be returned when this request is completed
3709         * @author Randall Hauch
3710         */
3711        public interface AsName<Next> {
3712            /**
3713             * Finish the request by specifying the new name.
3714             * 
3715             * @param newName the new name
3716             * @return the interface for additional requests or actions
3717             */
3718            Next as( String newName );
3719    
3720            /**
3721             * Finish the request by specifying the new name.
3722             * 
3723             * @param newName the new name
3724             * @return the interface for additional requests or actions
3725             */
3726            Next as( Name newName );
3727        }
3728    
3729        /**
3730         * A interface that is used to add more locations that are to be copied/moved.
3731         * 
3732         * @param <Next> The interface that is to be returned when this request is completed
3733         * @author Randall Hauch
3734         */
3735        public interface And<Next> {
3736            /**
3737             * Specify that another node should also be copied or moved.
3738             * 
3739             * @param from the location of the node to be copied or moved
3740             * @return the interface for finishing the request
3741             */
3742            Next and( Location from );
3743    
3744            /**
3745             * Specify that another node should also be copied or moved.
3746             * 
3747             * @param fromPath the path of the node to be copied or moved
3748             * @return the interface for finishing the request
3749             */
3750            Next and( String fromPath );
3751    
3752            /**
3753             * Specify that another node should also be copied or moved.
3754             * 
3755             * @param from the path of the node to be copied or moved
3756             * @return the interface for finishing the request
3757             */
3758            Next and( Path from );
3759    
3760            /**
3761             * Specify that another node should also be copied or moved.
3762             * 
3763             * @param from the UUID of the node to be copied or moved
3764             * @return the interface for finishing the request
3765             */
3766            Next and( UUID from );
3767    
3768            /**
3769             * Specify that another node should also be copied or moved.
3770             * 
3771             * @param idProperty the property that uniquely identifies the node to be copied or moved
3772             * @return the interface for finishing the request
3773             */
3774            Next and( Property idProperty );
3775    
3776            /**
3777             * Specify that another node should also be copied or moved.
3778             * 
3779             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
3780             *        node to be copied or moved
3781             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
3782             *        identifies the node to be copied or moved
3783             * @return the interface for finishing the request
3784             */
3785            Next and( Property firstIdProperty,
3786                      Property... additionalIdProperties );
3787    
3788            /**
3789             * Specify that another node should also be copied or moved.
3790             * 
3791             * @param idProperties the properties that uniquely identifies the node to be copied or moved
3792             * @return the interface for finishing the request
3793             */
3794            Next and( Iterable<Property> idProperties );
3795        }
3796    
3797        /**
3798         * The interface for defining additional nodes to be moved and the parent into which the node(s) are to be moved.
3799         * 
3800         * @param <Next> The interface that is to be returned when this request is completed
3801         * @author Randall Hauch
3802         */
3803        public interface Move<Next> extends AsName<Into<Next>>, Into<Next>, And<Move<Next>> {
3804        }
3805    
3806        /**
3807         * The interface for defining additional nodes to be copied and the locations where the copy is to be placed. The
3808         * <code>to(...)</code> methods allow you to specify the location of the copy, including the name for the node that results
3809         * from the copy. Alternatively, you can use the <code>into(...)</code> methods to specify the parent location where the copy
3810         * is to be placed, which will assume the new copy will have the same name as the original.
3811         * 
3812         * @param <Next> The interface that is to be returned when this request is completed
3813         * @author Randall Hauch
3814         */
3815        public interface Copy<Next> extends To<Next>, Into<Next>, And<Copy<Next>> {
3816        }
3817    
3818        /**
3819         * The interface for defining additional properties on a new node.
3820         * 
3821         * @param <Next> The interface that is to be returned when this create request is completed
3822         * @author Randall Hauch
3823         */
3824        public interface Create<Next> extends Conjunction<Next>, Executable<Node> {
3825            /**
3826             * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
3827             * 
3828             * @param uuid the UUID
3829             * @return this same interface so additional properties may be added
3830             */
3831            Create<Next> with( UUID uuid );
3832    
3833            /**
3834             * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
3835             * 
3836             * @param property the property
3837             * @return this same interface so additional properties may be added
3838             */
3839            Create<Next> with( Property property );
3840    
3841            /**
3842             * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
3843             * 
3844             * @param properties the properties that should be added
3845             * @return this same interface so additional properties may be added
3846             */
3847            Create<Next> with( Iterable<Property> properties );
3848    
3849            /**
3850             * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
3851             * 
3852             * @param propertyName the name of the property
3853             * @param values the property values
3854             * @return this same interface so additional properties may be added
3855             */
3856            Create<Next> with( String propertyName,
3857                               Object... values );
3858    
3859            /**
3860             * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
3861             * 
3862             * @param propertyName the name of the property
3863             * @param values the property values
3864             * @return this same interface so additional properties may be added
3865             */
3866            Create<Next> with( Name propertyName,
3867                               Object... values );
3868    
3869            /**
3870             * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
3871             * 
3872             * @param firstProperty the first property
3873             * @param additionalProperties the additional property
3874             * @return this same interface so additional properties may be added
3875             */
3876            Create<Next> with( Property firstProperty,
3877                               Property... additionalProperties );
3878    
3879            /**
3880             * Specify the UUID that should the new node should have.
3881             * 
3882             * @param uuid the UUID
3883             * @return this same interface so additional properties may be added
3884             */
3885            Create<Next> and( UUID uuid );
3886    
3887            /**
3888             * Specify a property that should the new node should have.
3889             * 
3890             * @param property the property
3891             * @return this same interface so additional properties may be added
3892             */
3893            Create<Next> and( Property property );
3894    
3895            /**
3896             * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
3897             * the properties in the supplied {@link Iterable}.
3898             * 
3899             * @param properties the properties that should be added
3900             * @return this same interface so additional properties may be added
3901             */
3902            Create<Next> and( Iterable<Property> properties );
3903    
3904            /**
3905             * Specify a property that should the new node should have.
3906             * 
3907             * @param propertyName the name of the property
3908             * @param values the property values
3909             * @return this same interface so additional properties may be added
3910             */
3911            Create<Next> and( String propertyName,
3912                              Object... values );
3913    
3914            /**
3915             * Specify a property that should the new node should have.
3916             * 
3917             * @param propertyName the name of the property
3918             * @param values the property values
3919             * @return this same interface so additional properties may be added
3920             */
3921            Create<Next> and( Name propertyName,
3922                              Object... values );
3923    
3924            /**
3925             * Specify properties that should the new node should have.
3926             * 
3927             * @param firstProperty the first property
3928             * @param additionalProperties the additional property
3929             * @return this same interface so additional properties may be added
3930             */
3931            Create<Next> and( Property firstProperty,
3932                              Property... additionalProperties );
3933        }
3934    
3935        /**
3936         * The interface for defining additional properties on a new node.
3937         * 
3938         * @param <Next> The interface that is to be returned when this create request is completed
3939         * @author Randall Hauch
3940         */
3941        public interface CreateAt<Next> extends Conjunction<Next> {
3942            /**
3943             * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
3944             * 
3945             * @param uuid the UUID
3946             * @return this same interface so additional properties may be added
3947             */
3948            CreateAt<Next> with( UUID uuid );
3949    
3950            /**
3951             * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
3952             * 
3953             * @param property the property
3954             * @return this same interface so additional properties may be added
3955             */
3956            CreateAt<Next> with( Property property );
3957    
3958            /**
3959             * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
3960             * 
3961             * @param properties the properties that should be added
3962             * @return this same interface so additional properties may be added
3963             */
3964            CreateAt<Next> with( Iterable<Property> properties );
3965    
3966            /**
3967             * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
3968             * 
3969             * @param propertyName the name of the property
3970             * @param values the property values
3971             * @return this same interface so additional properties may be added
3972             */
3973            CreateAt<Next> with( String propertyName,
3974                                 Object... values );
3975    
3976            /**
3977             * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
3978             * 
3979             * @param propertyName the name of the property
3980             * @param values the property values
3981             * @return this same interface so additional properties may be added
3982             */
3983            CreateAt<Next> with( Name propertyName,
3984                                 Object... values );
3985    
3986            /**
3987             * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
3988             * 
3989             * @param firstProperty the first property
3990             * @param additionalProperties the additional property
3991             * @return this same interface so additional properties may be added
3992             */
3993            CreateAt<Next> with( Property firstProperty,
3994                                 Property... additionalProperties );
3995    
3996            /**
3997             * Specify the UUID that should the new node should have.
3998             * 
3999             * @param uuid the UUID
4000             * @return this same interface so additional properties may be added
4001             */
4002            CreateAt<Next> and( UUID uuid );
4003    
4004            /**
4005             * Specify a property that should the new node should have.
4006             * 
4007             * @param property the property
4008             * @return this same interface so additional properties may be added
4009             */
4010            CreateAt<Next> and( Property property );
4011    
4012            /**
4013             * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
4014             * the properties in the supplied {@link Iterable}.
4015             * 
4016             * @param properties the properties that should be added
4017             * @return this same interface so additional properties may be added
4018             */
4019            CreateAt<Next> and( Iterable<Property> properties );
4020    
4021            /**
4022             * Specify a property that should the new node should have.
4023             * 
4024             * @param propertyName the name of the property
4025             * @param values the property values
4026             * @return this same interface so additional properties may be added
4027             */
4028            CreateAt<Next> and( String propertyName,
4029                                Object... values );
4030    
4031            /**
4032             * Specify a property that should the new node should have.
4033             * 
4034             * @param propertyName the name of the property
4035             * @param values the property values
4036             * @return this same interface so additional properties may be added
4037             */
4038            CreateAt<Next> and( Name propertyName,
4039                                Object... values );
4040    
4041            /**
4042             * Specify properties that should the new node should have.
4043             * 
4044             * @param firstProperty the first property
4045             * @param additionalProperties the additional property
4046             * @return this same interface so additional properties may be added
4047             */
4048            CreateAt<Next> and( Property firstProperty,
4049                                Property... additionalProperties );
4050    
4051            /**
4052             * Complete this request, submit it, and return the actual location of the created node.
4053             * 
4054             * @return the actual location of the just-created node; never null
4055             */
4056            Location getLocation();
4057    
4058            /**
4059             * Complete this request, submit it, and return the actual node that was created.
4060             * 
4061             * @return the actual node that was just created; never null
4062             */
4063            Node getNode();
4064        }
4065    
4066        /**
4067         * The interface for defining the node upon which a request operates.
4068         * 
4069         * @param <Next> The interface that is to be returned when the request is completed
4070         * @author Randall Hauch
4071         */
4072        public interface On<Next> {
4073            /**
4074             * Specify the location of the node upon which the request is to operate.
4075             * 
4076             * @param to the location of the new parent
4077             * @return the interface for additional requests or actions
4078             */
4079            Next on( Location to );
4080    
4081            /**
4082             * Specify the path of the node upon which the request is to operate.
4083             * 
4084             * @param toPath the path of the new parent
4085             * @return the interface for additional requests or actions
4086             */
4087            Next on( String toPath );
4088    
4089            /**
4090             * Specify the path of the node upon which the request is to operate.
4091             * 
4092             * @param to the path of the new parent
4093             * @return the interface for additional requests or actions
4094             */
4095            Next on( Path to );
4096    
4097            /**
4098             * Specify the UUID of the node upon which the request is to operate.
4099             * 
4100             * @param to the UUID of the new parent
4101             * @return the interface for additional requests or actions
4102             */
4103            Next on( UUID to );
4104    
4105            /**
4106             * Specify the unique identification property that identifies the node upon which the request is to operate.
4107             * 
4108             * @param idProperty the property that uniquely identifies the new parent
4109             * @return the interface for additional requests or actions
4110             */
4111            Next on( Property idProperty );
4112    
4113            /**
4114             * Specify the unique identification properties that identify the node upon which the request is to operate.
4115             * 
4116             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
4117             *        new parent
4118             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
4119             *        identifies the new parent
4120             * @return the interface for additional requests or actions
4121             */
4122            Next on( Property firstIdProperty,
4123                     Property... additionalIdProperties );
4124    
4125            /**
4126             * Specify the unique identification properties that identify the node upon which the request is to operate.
4127             * 
4128             * @param idProperties the properties that uniquely identifies the new parent
4129             * @return the interface for additional requests or actions
4130             */
4131            Next on( Iterable<Property> idProperties );
4132        }
4133    
4134        /**
4135         * The interface for defining the node upon which a request operates.
4136         * 
4137         * @param <Next> The interface that is to be returned when the request is completed
4138         * @author Randall Hauch
4139         */
4140        public interface Of<Next> {
4141            /**
4142             * Specify the location of the node upon which the request is to operate.
4143             * 
4144             * @param to the location of the new parent
4145             * @return the interface for additional requests or actions
4146             */
4147            Next of( Location to );
4148    
4149            /**
4150             * Specify the path of the node upon which the request is to operate.
4151             * 
4152             * @param toPath the path of the new parent
4153             * @return the interface for additional requests or actions
4154             */
4155            Next of( String toPath );
4156    
4157            /**
4158             * Specify the path of the node upon which the request is to operate.
4159             * 
4160             * @param to the path of the new parent
4161             * @return the interface for additional requests or actions
4162             */
4163            Next of( Path to );
4164    
4165            /**
4166             * Specify the UUID of the node upon which the request is to operate.
4167             * 
4168             * @param to the UUID of the new parent
4169             * @return the interface for additional requests or actions
4170             */
4171            Next of( UUID to );
4172    
4173            /**
4174             * Specify the unique identification property that identifies the node upon which the request is to operate.
4175             * 
4176             * @param idProperty the property that uniquely identifies the new parent
4177             * @return the interface for additional requests or actions
4178             */
4179            Next of( Property idProperty );
4180    
4181            /**
4182             * Specify the unique identification properties that identify the node upon which the request is to operate.
4183             * 
4184             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
4185             *        new parent
4186             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
4187             *        identifies the new parent
4188             * @return the interface for additional requests or actions
4189             */
4190            Next of( Property firstIdProperty,
4191                     Property... additionalIdProperties );
4192    
4193            /**
4194             * Specify the unique identification properties that identify the node upon which the request is to operate.
4195             * 
4196             * @param idProperties the properties that uniquely identifies the new parent
4197             * @return the interface for additional requests or actions
4198             */
4199            Next of( Iterable<Property> idProperties );
4200        }
4201    
4202        /**
4203         * The interface for defining the node upon which which a request operates.
4204         * 
4205         * @param <Next> The interface that is to be returned when the request is completed
4206         * @author Randall Hauch
4207         */
4208        public interface At<Next> {
4209            /**
4210             * Specify the location of the node upon which the request is to operate.
4211             * 
4212             * @param to the location of the new parent
4213             * @return the interface for additional requests or actions
4214             */
4215            Next at( Location to );
4216    
4217            /**
4218             * Specify the path of the node upon which the request is to operate.
4219             * 
4220             * @param toPath the path of the new parent
4221             * @return the interface for additional requests or actions
4222             */
4223            Next at( String toPath );
4224    
4225            /**
4226             * Specify the path of the node upon which the request is to operate.
4227             * 
4228             * @param to the path of the new parent
4229             * @return the interface for additional requests or actions
4230             */
4231            Next at( Path to );
4232    
4233            /**
4234             * Specify the UUID of the node upon which the request is to operate.
4235             * 
4236             * @param to the UUID of the new parent
4237             * @return the interface for additional requests or actions
4238             */
4239            Next at( UUID to );
4240    
4241            /**
4242             * Specify the unique identification property that identifies the node upon which the request is to operate.
4243             * 
4244             * @param idProperty the property that uniquely identifies the new parent
4245             * @return the interface for additional requests or actions
4246             */
4247            Next at( Property idProperty );
4248    
4249            /**
4250             * Specify the unique identification properties that identify the node upon which the request is to operate.
4251             * 
4252             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
4253             *        new parent
4254             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
4255             *        identifies the new parent
4256             * @return the interface for additional requests or actions
4257             */
4258            Next at( Property firstIdProperty,
4259                     Property... additionalIdProperties );
4260    
4261            /**
4262             * Specify the unique identification properties that identify the node upon which the request is to operate.
4263             * 
4264             * @param idProperties the properties that uniquely identifies the new parent
4265             * @return the interface for additional requests or actions
4266             */
4267            Next at( Iterable<Property> idProperties );
4268        }
4269    
4270        /**
4271         * A component used to supply the details for getting children of another node. If all of the children are to be obtained,
4272         * then the parent can be specified using one of the <code>of(...)</code> methods on this component. If, however, only some of
4273         * the nodes are to be returned (e.g., a "block" of children), then specify the {@link #inBlockOf(int) block size} followed by
4274         * the {@link BlockOfChildren block size and parent}.
4275         * 
4276         * @param <Next>
4277         * @author Randall Hauch
4278         */
4279        public interface Children<Next> extends Of<Next> {
4280            /**
4281             * Specify that a block of children are to be retreived, and in particular the number of children that are to be returned.
4282             * 
4283             * @param blockSize the number of children that are to be retrieved in the block; must be positive
4284             * @return the interface used to specify the starting point for the block and the parent
4285             */
4286            BlockOfChildren<Next> inBlockOf( int blockSize );
4287        }
4288    
4289        /**
4290         * A component used to specify a block of children starting either {@link #startingAt(int) at a particular index} or
4291         * {@link #startingAfter(Location) after a previous sibling}.
4292         * 
4293         * @param <Next>
4294         * @author Randall Hauch
4295         */
4296        public interface BlockOfChildren<Next> {
4297            /**
4298             * Specify the block of children is to start at the supplied index.
4299             * 
4300             * @param startingIndex the zero-based index of the first child to be returned in the block
4301             * @return interface used to specify the parent of the children; never null
4302             */
4303            Under<Next> startingAt( int startingIndex );
4304    
4305            /**
4306             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4307             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4308             * block.
4309             * 
4310             * @param previousSibling the location of the sibling node that is before the first node in the block
4311             * @return the children; never null
4312             */
4313            Next startingAfter( Location previousSibling );
4314    
4315            /**
4316             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4317             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4318             * block.
4319             * 
4320             * @param pathToPreviousSiblingName the path of the sibling node that is before the first node in the block
4321             * @return the children; never null
4322             */
4323            Next startingAfter( String pathToPreviousSiblingName );
4324    
4325            /**
4326             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4327             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4328             * block.
4329             * 
4330             * @param previousSibling the path of the sibling node that is before the first node in the block
4331             * @return the children; never null
4332             */
4333            Next startingAfter( Path previousSibling );
4334    
4335            /**
4336             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4337             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4338             * block.
4339             * 
4340             * @param previousSiblingUuid the UUID of the sibling node that is before the first node in the block
4341             * @return the children; never null
4342             */
4343            Next startingAfter( UUID previousSiblingUuid );
4344    
4345            /**
4346             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4347             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4348             * block.
4349             * 
4350             * @param idPropertyOfPreviousSibling the property that uniquely identifies the previous sibling
4351             * @return the children; never null
4352             */
4353            Next startingAfter( Property idPropertyOfPreviousSibling );
4354    
4355            /**
4356             * Specify the block of children is to start with the child immediately following the supplied node. This method is
4357             * typically used when a previous block of children has already been retrieved and this request is retrieving the next
4358             * block.
4359             * 
4360             * @param firstIdPropertyOfPreviousSibling the first property that, with the <code>additionalIdProperties</code>, uniquely
4361             *        identifies the previous sibling
4362             * @param additionalIdPropertiesOfPreviousSibling the additional properties that, with the
4363             *        <code>additionalIdProperties</code>, uniquely identifies the previous sibling
4364             * @return the children; never null
4365             */
4366            Next startingAfter( Property firstIdPropertyOfPreviousSibling,
4367                                Property... additionalIdPropertiesOfPreviousSibling );
4368        }
4369    
4370        /**
4371         * The interface for defining the node under which which a request operates.
4372         * 
4373         * @param <Next> The interface that is to be returned when the request is completed
4374         * @author Randall Hauch
4375         */
4376        public interface Under<Next> {
4377            /**
4378             * Specify the location of the node under which the request is to operate.
4379             * 
4380             * @param to the location of the new parent
4381             * @return the interface for additional requests or actions
4382             */
4383            Next under( Location to );
4384    
4385            /**
4386             * Specify the path of the node under which the request is to operate.
4387             * 
4388             * @param toPath the path of the new parent
4389             * @return the interface for additional requests or actions
4390             */
4391            Next under( String toPath );
4392    
4393            /**
4394             * Specify the path of the node under which the request is to operate.
4395             * 
4396             * @param to the path of the new parent
4397             * @return the interface for additional requests or actions
4398             */
4399            Next under( Path to );
4400    
4401            /**
4402             * Specify the UUID of the node under which the request is to operate.
4403             * 
4404             * @param to the UUID of the new parent
4405             * @return the interface for additional requests or actions
4406             */
4407            Next under( UUID to );
4408    
4409            /**
4410             * Specify the unique identification property that identifies the node under which the request is to operate.
4411             * 
4412             * @param idProperty the property that uniquely identifies the new parent
4413             * @return the interface for additional requests or actions
4414             */
4415            Next under( Property idProperty );
4416    
4417            /**
4418             * Specify the unique identification properties that identify the node under which the request is to operate.
4419             * 
4420             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
4421             *        new parent
4422             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
4423             *        identifies the new parent
4424             * @return the interface for additional requests or actions
4425             */
4426            Next under( Property firstIdProperty,
4427                        Property... additionalIdProperties );
4428        }
4429    
4430        /**
4431         * A component used to set the values on a property.
4432         * 
4433         * @param <Next> the next command
4434         * @author Randall Hauch
4435         */
4436        public interface SetValues<Next> extends On<SetValuesTo<Next>>, SetValuesTo<On<Next>> {
4437        }
4438    
4439        /**
4440         * A component used to set the values on a property.
4441         * 
4442         * @param <Next>
4443         * @author Randall Hauch
4444         */
4445        public interface SetValuesTo<Next> {
4446    
4447            /**
4448             * Set the property value to be a reference to the given node. Note that it is an error if the Node does not have a
4449             * {@link Location#getUuid() UUID}.
4450             * 
4451             * @param node the node to which a reference should be set
4452             * @return the interface for additional requests or actions
4453             * @throws IllegalArgumentException if the value is a Node that has no {@link Location#getUuid() UUID}
4454             */
4455            Next to( Node node );
4456    
4457            /**
4458             * Set the property value to be a reference to the given location. Note that it is an error if the Location does not have
4459             * a {@link Location#getUuid() UUID}.
4460             * 
4461             * @param location the location to which a reference should be set
4462             * @return the interface for additional requests or actions
4463             * @throws IllegalArgumentException if the value is a Location that has no {@link Location#getUuid() UUID}
4464             */
4465            Next to( Location location );
4466    
4467            /**
4468             * Set the property value to the given string.
4469             * 
4470             * @param value the property value
4471             * @return the interface for additional requests or actions
4472             */
4473            Next to( String value );
4474    
4475            /**
4476             * Set the property value to the given integer value.
4477             * 
4478             * @param value the property value
4479             * @return the interface for additional requests or actions
4480             */
4481            Next to( int value );
4482    
4483            /**
4484             * Set the property value to the given long value.
4485             * 
4486             * @param value the property value
4487             * @return the interface for additional requests or actions
4488             */
4489            Next to( long value );
4490    
4491            /**
4492             * Set the property value to the given boolean value.
4493             * 
4494             * @param value the property value
4495             * @return the interface for additional requests or actions
4496             */
4497            Next to( boolean value );
4498    
4499            /**
4500             * Set the property value to the given float value.
4501             * 
4502             * @param value the property value
4503             * @return the interface for additional requests or actions
4504             */
4505            Next to( float value );
4506    
4507            /**
4508             * Set the property value to the given double value.
4509             * 
4510             * @param value the property value
4511             * @return the interface for additional requests or actions
4512             */
4513            Next to( double value );
4514    
4515            /**
4516             * Set the property value to the given decimal value.
4517             * 
4518             * @param value the property value
4519             * @return the interface for additional requests or actions
4520             */
4521            Next to( BigDecimal value );
4522    
4523            /**
4524             * Set the property value to the date given by the supplied calendar.
4525             * 
4526             * @param value the property value
4527             * @return the interface for additional requests or actions
4528             */
4529            Next to( Calendar value );
4530    
4531            /**
4532             * Set the property value to the given date.
4533             * 
4534             * @param value the property value
4535             * @return the interface for additional requests or actions
4536             */
4537            Next to( Date value );
4538    
4539            /**
4540             * Set the property value to the given date-time instant.
4541             * 
4542             * @param value the property value
4543             * @return the interface for additional requests or actions
4544             */
4545            Next to( DateTime value );
4546    
4547            /**
4548             * Set the property value to the given Name.
4549             * 
4550             * @param value the property value
4551             * @return the interface for additional requests or actions
4552             */
4553            Next to( Name value );
4554    
4555            /**
4556             * Set the property value to the given Path.
4557             * 
4558             * @param value the property value
4559             * @return the interface for additional requests or actions
4560             */
4561            Next to( Path value );
4562    
4563            /**
4564             * Set the property value to the given Reference. See also {@link #to(Node)}.
4565             * 
4566             * @param value the property value
4567             * @return the interface for additional requests or actions
4568             */
4569            Next to( Reference value );
4570    
4571            /**
4572             * Set the property value to the given URI.
4573             * 
4574             * @param value the property value
4575             * @return the interface for additional requests or actions
4576             */
4577            Next to( URI value );
4578    
4579            /**
4580             * Set the property value to the given UUID.
4581             * 
4582             * @param value the property value
4583             * @return the interface for additional requests or actions
4584             */
4585            Next to( UUID value );
4586    
4587            /**
4588             * Set the property value to the given binary value.
4589             * 
4590             * @param value the property value
4591             * @return the interface for additional requests or actions
4592             */
4593            Next to( Binary value );
4594    
4595            /**
4596             * Set the property value to the given byte array.
4597             * 
4598             * @param value the property value
4599             * @return the interface for additional requests or actions
4600             */
4601            Next to( byte[] value );
4602    
4603            /**
4604             * Set the property value to the given string.
4605             * 
4606             * @param stream the stream containing the content to be used for the property value
4607             * @param approximateLength the approximate length of the content (in bytes)
4608             * @return the interface for additional requests or actions
4609             */
4610            Next to( InputStream stream,
4611                     long approximateLength );
4612    
4613            /**
4614             * Set the property value to the given string.
4615             * 
4616             * @param reader the reader containing the content to be used for the property value
4617             * @param approximateLength the approximate length of the content (in bytes)
4618             * @return the interface for additional requests or actions
4619             */
4620            Next to( Reader reader,
4621                     long approximateLength );
4622    
4623            /**
4624             * Set the property value to the given object. The supplied <code>value</code> should be a valid property value, or a
4625             * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
4626             * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
4627             * 
4628             * @param value the property value
4629             * @return the interface for additional requests or actions
4630             * @throws IllegalArgumentException if the value is a Node or Location that has no {@link Location#getUuid() UUID}
4631             */
4632            Next to( Object value );
4633    
4634            /**
4635             * Set the property value to the given objects. Each of the supplied values should be a valid property value, or a
4636             * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
4637             * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
4638             * 
4639             * @param firstValue the first property value
4640             * @param otherValues the remaining property values
4641             * @return the interface for additional requests or actions
4642             * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
4643             *         UUID}
4644             */
4645            Next to( Object firstValue,
4646                     Object... otherValues );
4647    
4648            /**
4649             * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
4650             * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
4651             * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
4652             * 
4653             * @param values the container for the property values
4654             * @return the interface for additional requests or actions
4655             * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
4656             *         UUID}
4657             */
4658            Next to( Iterable<?> values );
4659    
4660            /**
4661             * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
4662             * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
4663             * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
4664             * 
4665             * @param values the iterator over the property values
4666             * @return the interface for additional requests or actions
4667             * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
4668             *         UUID}
4669             */
4670            Next to( Iterator<?> values );
4671        }
4672    
4673        /**
4674         * A component that defines a node that is to be created.
4675         * 
4676         * @param <Next> The interface that is to be returned to complete the create request
4677         * @author Randall Hauch
4678         */
4679        public interface CreateNode<Next> {
4680            /**
4681             * Specify the name of the node that is to be created.
4682             * 
4683             * @param nodeName the name of the new node
4684             * @param properties the properties for the new node
4685             * @return the next component for making additional requests.
4686             */
4687            Next node( String nodeName,
4688                       Property... properties );
4689    
4690            /**
4691             * Specify the name of the node that is to be created.
4692             * 
4693             * @param nodeName the name of the new node
4694             * @param properties the properties for the new node
4695             * @return the next component for making additional requests.
4696             */
4697            Next node( String nodeName,
4698                       Iterator<Property> properties );
4699    
4700            /**
4701             * Specify the name of the node that is to be created.
4702             * 
4703             * @param nodeName the name of the new node
4704             * @param properties the properties for the new node
4705             * @return the next component for making additional requests.
4706             */
4707            Next node( String nodeName,
4708                       Iterable<Property> properties );
4709        }
4710    
4711        /**
4712         * A component that defines a node that is to be created.
4713         * 
4714         * @param <Next> The interface that is to be returned to complete the create request
4715         * @author Randall Hauch
4716         */
4717        public interface CreateNodeNamed<Next> {
4718            /**
4719             * Specify the name of the node that is to be created.
4720             * 
4721             * @param nodeName the name of the new node
4722             * @return the interface used to complete the request
4723             */
4724            Create<Next> nodeNamed( String nodeName );
4725    
4726            /**
4727             * Specify the name of the node that is to be created.
4728             * 
4729             * @param nodeName the name of the new node
4730             * @return the interface used to complete the request
4731             */
4732            Create<Next> nodeNamed( Name nodeName );
4733        }
4734    
4735        /**
4736         * A component that defines the location into which a node should be copied or moved.
4737         * 
4738         * @param <Next> The interface that is to be returned when this request is completed
4739         * @author Randall Hauch
4740         */
4741        public interface ImportInto<Next> {
4742            /**
4743             * Specify whether the root element in the XML document should be skipped (that is, not be represented by a node). By
4744             * default, the root element is not skipped.
4745             * 
4746             * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
4747             * @return the interface used to specify the location where the content should be placed
4748             */
4749            ImportInto<Next> skippingRootElement( boolean skip );
4750    
4751            /**
4752             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4753             * 
4754             * @param to the location of the new parent
4755             * @return the interface for additional requests or actions
4756             * @throws IOException if there is a problem reading the content being imported
4757             * @throws SAXException if there is a problem with the SAX Parser
4758             */
4759            Next into( Location to ) throws IOException, SAXException;
4760    
4761            /**
4762             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4763             * 
4764             * @param toPath the path of the new parent
4765             * @return the interface for additional requests or actions
4766             * @throws IOException if there is a problem reading the content being imported
4767             * @throws SAXException if there is a problem with the SAX Parser
4768             */
4769            Next into( String toPath ) throws IOException, SAXException;
4770    
4771            /**
4772             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4773             * 
4774             * @param to the path of the new parent
4775             * @return the interface for additional requests or actions
4776             * @throws IOException if there is a problem reading the content being imported
4777             * @throws SAXException if there is a problem with the SAX Parser
4778             */
4779            Next into( Path to ) throws IOException, SAXException;
4780    
4781            /**
4782             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4783             * 
4784             * @param to the UUID of the new parent
4785             * @return the interface for additional requests or actions
4786             * @throws IOException if there is a problem reading the content being imported
4787             * @throws SAXException if there is a problem with the SAX Parser
4788             */
4789            Next into( UUID to ) throws IOException, SAXException;
4790    
4791            /**
4792             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4793             * 
4794             * @param idProperty the property that uniquely identifies the new parent
4795             * @return the interface for additional requests or actions
4796             * @throws IOException if there is a problem reading the content being imported
4797             * @throws SAXException if there is a problem with the SAX Parser
4798             */
4799            Next into( Property idProperty ) throws IOException, SAXException;
4800    
4801            /**
4802             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4803             * 
4804             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
4805             *        new parent
4806             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
4807             *        identifies the new parent
4808             * @return the interface for additional requests or actions
4809             * @throws IOException if there is a problem reading the content being imported
4810             * @throws SAXException if there is a problem with the SAX Parser
4811             */
4812            Next into( Property firstIdProperty,
4813                       Property... additionalIdProperties ) throws IOException, SAXException;
4814    
4815            /**
4816             * Finish the import by specifying the Location.create into which the node should be copied/moved.
4817             * 
4818             * @param idProperties the properties that uniquely identifies the new parent
4819             * @return the interface for additional requests or actions
4820             * @throws IOException if there is a problem reading the content being imported
4821             * @throws SAXException if there is a problem with the SAX Parser
4822             */
4823            Next into( Iterable<Property> idProperties ) throws IOException, SAXException;
4824        }
4825    
4826        public interface BatchConjunction extends Conjunction<Batch>, Executable<Node> {
4827        }
4828    
4829        public interface GetNodeConjunction<Next> extends Conjunction<Next> {
4830            Node andReturn();
4831        }
4832    
4833        protected class GetNodeOrReturnGraph implements GetNodeConjunction<Graph> {
4834            private final Location location;
4835    
4836            GetNodeOrReturnGraph( Location location ) {
4837                assert location != null;
4838                this.location = location;
4839            }
4840    
4841            /**
4842             * {@inheritDoc}
4843             * 
4844             * @see org.jboss.dna.graph.Graph.Conjunction#and()
4845             */
4846            public Graph and() {
4847                return Graph.this;
4848            }
4849    
4850            /**
4851             * {@inheritDoc}
4852             * 
4853             * @see org.jboss.dna.graph.Graph.GetNodeConjunction#andReturn()
4854             */
4855            public Node andReturn() {
4856                return and().getNodeAt(location);
4857            }
4858        }
4859    
4860        // ----------------------------------------------------------------------------------------------------------------
4861        // Node Implementation
4862        // ----------------------------------------------------------------------------------------------------------------
4863        @Immutable
4864        protected class GraphNode implements Node {
4865            private final ReadNodeRequest request;
4866    
4867            /*package*/GraphNode( ReadNodeRequest request ) {
4868                this.request = request;
4869            }
4870    
4871            public Location getLocation() {
4872                return request.getActualLocationOfNode();
4873            }
4874    
4875            public Graph getGraph() {
4876                return Graph.this;
4877            }
4878    
4879            public Collection<Property> getProperties() {
4880                return request.getProperties();
4881            }
4882    
4883            public Property getProperty( Name name ) {
4884                return getPropertiesByName().get(name);
4885            }
4886    
4887            public Property getProperty( String nameStr ) {
4888                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
4889                return getPropertiesByName().get(name);
4890            }
4891    
4892            public Map<Name, Property> getPropertiesByName() {
4893                return request.getPropertiesByName();
4894            }
4895    
4896            public List<Location> getChildren() {
4897                return request.getChildren();
4898            }
4899    
4900            public boolean hasChildren() {
4901                return request.getChildren().size() > 0;
4902            }
4903    
4904            public List<Segment> getChildrenSegments() {
4905                return getSegments(getChildren());
4906            }
4907    
4908            public Iterator<Location> iterator() {
4909                return request.getChildren().iterator();
4910            }
4911    
4912            @Override
4913            public int hashCode() {
4914                return getLocation().hashCode();
4915            }
4916    
4917            @Override
4918            public boolean equals( Object obj ) {
4919                if (obj instanceof Node) {
4920                    Node that = (Node)obj;
4921                    return this.getLocation().equals(that.getLocation());
4922                }
4923                return false;
4924            }
4925    
4926            @Override
4927            public String toString() {
4928                return "Node " + getLocation().toString();
4929            }
4930        }
4931    
4932        // ----------------------------------------------------------------------------------------------------------------
4933        // Results implementation for the batched requests
4934        // ----------------------------------------------------------------------------------------------------------------
4935        @Immutable
4936        class BatchResults implements Results {
4937            private final Map<Path, BatchResultsNode> nodes = new HashMap<Path, BatchResultsNode>();
4938    
4939            /*package*/BatchResults( List<Request> requests ) {
4940                for (Request request : requests) {
4941                    if (request instanceof ReadAllPropertiesRequest) {
4942                        ReadAllPropertiesRequest read = (ReadAllPropertiesRequest)request;
4943                        getOrCreateNode(read.getActualLocationOfNode()).setProperties(read.getPropertiesByName());
4944                    } else if (request instanceof ReadPropertyRequest) {
4945                        ReadPropertyRequest read = (ReadPropertyRequest)request;
4946                        getOrCreateNode(read.getActualLocationOfNode()).addProperty(read.getProperty());
4947                    } else if (request instanceof ReadNodeRequest) {
4948                        ReadNodeRequest read = (ReadNodeRequest)request;
4949                        BatchResultsNode node = getOrCreateNode(read.getActualLocationOfNode());
4950                        node.setProperties(read.getPropertiesByName());
4951                        node.setChildren(read.getChildren());
4952                    } else if (request instanceof ReadBlockOfChildrenRequest) {
4953                        throw new IllegalStateException();
4954                    } else if (request instanceof ReadAllChildrenRequest) {
4955                        ReadAllChildrenRequest read = (ReadAllChildrenRequest)request;
4956                        getOrCreateNode(read.getActualLocationOfNode()).setChildren(read.getChildren());
4957                    } else if (request instanceof ReadBranchRequest) {
4958                        ReadBranchRequest read = (ReadBranchRequest)request;
4959                        for (Location location : read) {
4960                            BatchResultsNode node = getOrCreateNode(location);
4961                            node.setProperties(read.getPropertiesFor(location));
4962                            node.setChildren(read.getChildren(location));
4963                        }
4964                    }
4965                }
4966                for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
4967                    entry.getValue().freeze();
4968                }
4969            }
4970    
4971            /*package*/BatchResults( Request request ) {
4972                if (request instanceof ReadAllPropertiesRequest) {
4973                    ReadAllPropertiesRequest read = (ReadAllPropertiesRequest)request;
4974                    getOrCreateNode(read.getActualLocationOfNode()).setProperties(read.getPropertiesByName());
4975                } else if (request instanceof ReadPropertyRequest) {
4976                    ReadPropertyRequest read = (ReadPropertyRequest)request;
4977                    getOrCreateNode(read.getActualLocationOfNode()).addProperty(read.getProperty());
4978                } else if (request instanceof ReadNodeRequest) {
4979                    ReadNodeRequest read = (ReadNodeRequest)request;
4980                    BatchResultsNode node = getOrCreateNode(read.getActualLocationOfNode());
4981                    node.setProperties(read.getPropertiesByName());
4982                    node.setChildren(read.getChildren());
4983                } else if (request instanceof ReadBlockOfChildrenRequest) {
4984                    throw new IllegalStateException();
4985                } else if (request instanceof ReadAllChildrenRequest) {
4986                    ReadAllChildrenRequest read = (ReadAllChildrenRequest)request;
4987                    getOrCreateNode(read.getActualLocationOfNode()).setChildren(read.getChildren());
4988                } else if (request instanceof ReadBranchRequest) {
4989                    ReadBranchRequest read = (ReadBranchRequest)request;
4990                    for (Location location : read) {
4991                        BatchResultsNode node = getOrCreateNode(location);
4992                        node.setProperties(read.getPropertiesFor(location));
4993                        node.setChildren(read.getChildren(location));
4994                    }
4995                }
4996                for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
4997                    entry.getValue().freeze();
4998                }
4999            }
5000    
5001            /*package*/BatchResults() {
5002            }
5003    
5004            private BatchResultsNode getOrCreateNode( Location location ) {
5005                BatchResultsNode node = nodes.get(location);
5006                if (node == null) {
5007                    node = new BatchResultsNode(location);
5008                    assert location.getPath() != null;
5009                    nodes.put(location.getPath(), node);
5010                }
5011                return node;
5012            }
5013    
5014            public Graph getGraph() {
5015                return Graph.this;
5016            }
5017    
5018            protected void checkIsAbsolute( Path path ) {
5019                if (!path.isAbsolute()) {
5020                    throw new IllegalArgumentException(GraphI18n.pathIsNotAbsolute.text(path));
5021                }
5022            }
5023    
5024            public Node getNode( String pathStr ) {
5025                Path path = createPath(pathStr);
5026                checkIsAbsolute(path);
5027                return nodes.get(path);
5028            }
5029    
5030            public Node getNode( Path path ) {
5031                CheckArg.isNotNull(path, "path");
5032                checkIsAbsolute(path);
5033                return nodes.get(path);
5034            }
5035    
5036            public Node getNode( Location location ) {
5037                CheckArg.isNotNull(location, "location");
5038                CheckArg.isNotNull(location.getPath(), "location.getPath()");
5039                return nodes.get(location.getPath());
5040            }
5041    
5042            public boolean includes( String path ) {
5043                return getNode(path) != null;
5044            }
5045    
5046            public boolean includes( Path path ) {
5047                return getNode(path) != null;
5048            }
5049    
5050            public boolean includes( Location location ) {
5051                return getNode(location) != null;
5052            }
5053    
5054            public Iterator<Node> iterator() {
5055                List<Path> paths = new ArrayList<Path>(nodes.keySet());
5056                Collections.sort(paths);
5057                final Iterator<Path> pathIter = paths.iterator();
5058                return new Iterator<Node>() {
5059                    public boolean hasNext() {
5060                        return pathIter.hasNext();
5061                    }
5062    
5063                    public Node next() {
5064                        Path nextPath = pathIter.next();
5065                        return getNode(nextPath);
5066                    }
5067    
5068                    public void remove() {
5069                        throw new UnsupportedOperationException();
5070                    }
5071                };
5072            }
5073        }
5074    
5075        @Immutable
5076        class BatchResultsNode implements Node {
5077            private final Location location;
5078            private Map<Name, Property> properties;
5079            private List<Location> children;
5080    
5081            BatchResultsNode( Location location ) {
5082                this.location = location;
5083            }
5084    
5085            void addProperty( Property property ) {
5086                if (this.properties == null) this.properties = new HashMap<Name, Property>();
5087                this.properties.put(property.getName(), property);
5088            }
5089    
5090            void setProperties( Map<Name, Property> properties ) {
5091                this.properties = properties;
5092            }
5093    
5094            void setChildren( List<Location> children ) {
5095                this.children = children;
5096            }
5097    
5098            void freeze() {
5099                if (properties != null) properties = Collections.unmodifiableMap(properties);
5100                else properties = Collections.emptyMap();
5101                if (children != null) children = Collections.unmodifiableList(children);
5102                else children = Collections.emptyList();
5103            }
5104    
5105            public List<Segment> getChildrenSegments() {
5106                return getSegments(getChildren());
5107            }
5108    
5109            public Graph getGraph() {
5110                return Graph.this;
5111            }
5112    
5113            public Location getLocation() {
5114                return location;
5115            }
5116    
5117            public Collection<Property> getProperties() {
5118                return properties.values();
5119            }
5120    
5121            public Map<Name, Property> getPropertiesByName() {
5122                return properties;
5123            }
5124    
5125            public Property getProperty( Name name ) {
5126                return properties.get(name);
5127            }
5128    
5129            public Property getProperty( String nameStr ) {
5130                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
5131                return properties.get(name);
5132            }
5133    
5134            public List<Location> getChildren() {
5135                return children;
5136            }
5137    
5138            public boolean hasChildren() {
5139                return children.size() != 0;
5140            }
5141    
5142            public Iterator<Location> iterator() {
5143                return children.iterator();
5144            }
5145    
5146            @Override
5147            public int hashCode() {
5148                return location.hashCode();
5149            }
5150    
5151            @Override
5152            public boolean equals( Object obj ) {
5153                if (obj instanceof Node) {
5154                    Node that = (Node)obj;
5155                    return this.location.equals(that.getLocation());
5156                }
5157                return false;
5158            }
5159    
5160            @Override
5161            public String toString() {
5162                return "Node " + getLocation().toString();
5163            }
5164    
5165        }
5166    
5167        // ----------------------------------------------------------------------------------------------------------------
5168        // Subgraph and SubgraphNode implementations
5169        // ----------------------------------------------------------------------------------------------------------------
5170        @Immutable
5171        class SubgraphResults implements Subgraph {
5172            private final ReadBranchRequest request;
5173    
5174            SubgraphResults( ReadBranchRequest request ) {
5175                this.request = request;
5176            }
5177    
5178            public Graph getGraph() {
5179                return Graph.this;
5180            }
5181    
5182            public Location getLocation() {
5183                return request.getActualLocationOfNode();
5184            }
5185    
5186            public SubgraphNode getRoot() {
5187                return getNode(getLocation());
5188            }
5189    
5190            public int getMaximumDepth() {
5191                return request.maximumDepth();
5192            }
5193    
5194            public Iterator<SubgraphNode> iterator() {
5195                final Iterator<Location> iter = request.iterator();
5196                return new Iterator<SubgraphNode>() {
5197                    public boolean hasNext() {
5198                        return iter.hasNext();
5199                    }
5200    
5201                    public SubgraphNode next() {
5202                        return getNode(iter.next());
5203                    }
5204    
5205                    public void remove() {
5206                        throw new UnsupportedOperationException();
5207                    }
5208                };
5209            }
5210    
5211            public boolean includes( Path path ) {
5212                CheckArg.isNotNull(path, "path");
5213                path = getAbsolutePath(path);
5214                return request.includes(path);
5215            }
5216    
5217            public boolean includes( Location location ) {
5218                CheckArg.isNotNull(location, "location");
5219                return request.includes(location);
5220            }
5221    
5222            public boolean includes( String pathStr ) {
5223                Path path = createPath(pathStr);
5224                path = getAbsolutePath(path);
5225                return includes(path);
5226            }
5227    
5228            public SubgraphNode getNode( Location location ) {
5229                if (!location.hasPath()) return null;
5230                Location actualLocation = request.getLocationFor(location.getPath());
5231                if (actualLocation == null) return null;
5232                return new SubgraphNodeImpl(actualLocation, request);
5233            }
5234    
5235            public SubgraphNode getNode( Path path ) {
5236                path = getAbsolutePath(path);
5237                if (!includes(path)) return null;
5238                Location location = request.getLocationFor(path);
5239                if (location == null) return null;
5240                return new SubgraphNodeImpl(location, request);
5241            }
5242    
5243            public SubgraphNode getNode( String pathStr ) {
5244                CheckArg.isNotEmpty(pathStr, "path");
5245                Path path = createPath(pathStr);
5246                path = getAbsolutePath(path);
5247                return getNode(path);
5248            }
5249    
5250            public SubgraphNode getNode( Name relativePath ) {
5251                Path path = getGraph().getContext()
5252                                      .getValueFactories()
5253                                      .getPathFactory()
5254                                      .create(getLocation().getPath(), relativePath);
5255                path = path.getNormalizedPath();
5256                return getNode(path);
5257            }
5258    
5259            protected Path getAbsolutePath( Path absoluteOrRelative ) {
5260                Path result = absoluteOrRelative;
5261                if (!result.isAbsolute()) {
5262                    result = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), result);
5263                    result = result.getNormalizedPath();
5264                }
5265                return result;
5266            }
5267    
5268            @Override
5269            public int hashCode() {
5270                return getLocation().hashCode();
5271            }
5272    
5273            @Override
5274            public String toString() {
5275                return "Subgraph " + getLocation().toString();
5276            }
5277        }
5278    
5279        protected static final List<Location> NO_CHILDREN = Collections.emptyList();
5280    
5281        @Immutable
5282        class SubgraphNodeImpl implements SubgraphNode {
5283            private final Location location;
5284            private final ReadBranchRequest request;
5285    
5286            SubgraphNodeImpl( Location location,
5287                              ReadBranchRequest request ) {
5288                this.location = location;
5289                this.request = request;
5290            }
5291    
5292            public List<Location> getChildren() {
5293                List<Location> children = request.getChildren(location);
5294                if (children == null) children = NO_CHILDREN;
5295                return children;
5296            }
5297    
5298            public Graph getGraph() {
5299                return Graph.this;
5300            }
5301    
5302            public Location getLocation() {
5303                return location;
5304            }
5305    
5306            public Collection<Property> getProperties() {
5307                return getPropertiesByName().values();
5308            }
5309    
5310            public Map<Name, Property> getPropertiesByName() {
5311                return request.getPropertiesFor(location);
5312            }
5313    
5314            public Property getProperty( Name name ) {
5315                return getPropertiesByName().get(name);
5316            }
5317    
5318            public Property getProperty( String nameStr ) {
5319                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
5320                return getPropertiesByName().get(name);
5321            }
5322    
5323            public boolean hasChildren() {
5324                return getChildren().size() != 0;
5325            }
5326    
5327            public List<Segment> getChildrenSegments() {
5328                return getSegments(getChildren());
5329            }
5330    
5331            public Iterator<Location> iterator() {
5332                return getChildren().iterator();
5333            }
5334    
5335            public SubgraphNode getNode( Name childName ) {
5336                Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childName);
5337                Location location = request.getLocationFor(path);
5338                if (location == null) return null;
5339                return new SubgraphNodeImpl(location, request);
5340            }
5341    
5342            public SubgraphNode getNode( Path relativePath ) {
5343                Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), relativePath);
5344                path = path.getNormalizedPath();
5345                Location location = request.getLocationFor(path);
5346                if (location == null) return null;
5347                return new SubgraphNodeImpl(location, request);
5348            }
5349    
5350            @Override
5351            public int hashCode() {
5352                return location.hashCode();
5353            }
5354    
5355            @Override
5356            public boolean equals( Object obj ) {
5357                if (obj instanceof Node) {
5358                    Node that = (Node)obj;
5359                    return this.location.equals(that.getLocation());
5360                }
5361                return false;
5362            }
5363    
5364            @Override
5365            public String toString() {
5366                return "Node " + getLocation().toString();
5367            }
5368        }
5369    
5370        // ----------------------------------------------------------------------------------------------------------------
5371        // Action Implementations
5372        // ----------------------------------------------------------------------------------------------------------------
5373        @Immutable
5374        protected abstract class AbstractAction<T> implements Conjunction<T> {
5375            private final T afterConjunction;
5376    
5377            /*package*/AbstractAction( T afterConjunction ) {
5378                this.afterConjunction = afterConjunction;
5379            }
5380    
5381            /*package*/T afterConjunction() {
5382                return this.afterConjunction;
5383            }
5384    
5385            public T and() {
5386                return this.afterConjunction;
5387            }
5388    
5389            /*package*/Path createPath( String path ) {
5390                return Graph.this.getContext().getValueFactories().getPathFactory().create(path);
5391            }
5392    
5393            /*package*/Name createName( String name ) {
5394                return Graph.this.getContext().getValueFactories().getNameFactory().create(name);
5395            }
5396        }
5397    
5398        @NotThreadSafe
5399        protected abstract class MoveAction<T> extends AbstractAction<T> implements Move<T> {
5400            private final Locations from;
5401            private Name newName;
5402    
5403            /*package*/MoveAction( T afterConjunction,
5404                                    Location from ) {
5405                super(afterConjunction);
5406                this.from = new Locations(from);
5407            }
5408    
5409            public Move<T> and( Location from ) {
5410                this.from.add(from);
5411                return this;
5412            }
5413    
5414            public Move<T> and( String from ) {
5415                this.from.add(Location.create(createPath(from)));
5416                return this;
5417            }
5418    
5419            public Move<T> and( Path from ) {
5420                this.from.add(Location.create(from));
5421                return this;
5422            }
5423    
5424            public Move<T> and( Property firstFrom,
5425                                Property... additionalFroms ) {
5426                this.from.add(Location.create(firstFrom, additionalFroms));
5427                return this;
5428            }
5429    
5430            public Move<T> and( Iterable<Property> idPropertiesFrom ) {
5431                this.from.add(Location.create(idPropertiesFrom));
5432                return this;
5433            }
5434    
5435            public Move<T> and( Property from ) {
5436                this.from.add(Location.create(from));
5437                return this;
5438            }
5439    
5440            public Move<T> and( UUID from ) {
5441                this.from.add(Location.create(from));
5442                return this;
5443            }
5444    
5445            public Into<T> as( Name newName ) {
5446                this.newName = newName;
5447                return this;
5448            }
5449    
5450            /**
5451             * {@inheritDoc}
5452             * 
5453             * @see org.jboss.dna.graph.Graph.AsName#as(java.lang.String)
5454             */
5455            public Into<T> as( String newName ) {
5456                return as(createName(newName));
5457            }
5458    
5459            /**
5460             * Submit any requests to move the targets into the supplied parent location
5461             * 
5462             * @param from the location(s) that are being moved; never null
5463             * @param into the parent location
5464             * @param newName the new name for the node being moved; may be null
5465             * @return this object, for method chaining
5466             */
5467            protected abstract T submit( Locations from,
5468                                         Location into,
5469                                         Name newName );
5470    
5471            public T into( Location into ) {
5472                return submit(from, into, newName);
5473            }
5474    
5475            public T into( Path into ) {
5476                return submit(from, Location.create(into), newName);
5477            }
5478    
5479            public T into( UUID into ) {
5480                return submit(from, Location.create(into), newName);
5481            }
5482    
5483            public T into( Property firstIdProperty,
5484                           Property... additionalIdProperties ) {
5485                return submit(from, Location.create(firstIdProperty, additionalIdProperties), newName);
5486            }
5487    
5488            public T into( Property into ) {
5489                return submit(from, Location.create(into), newName);
5490            }
5491    
5492            public T into( String into ) {
5493                return submit(from, Location.create(createPath(into)), newName);
5494            }
5495        }
5496    
5497        @NotThreadSafe
5498        protected abstract class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
5499            private final Locations from;
5500    
5501            /*package*/CopyAction( T afterConjunction,
5502                                    Location from ) {
5503                super(afterConjunction);
5504                this.from = new Locations(from);
5505            }
5506    
5507            public Copy<T> and( Location from ) {
5508                this.from.add(from);
5509                return this;
5510            }
5511    
5512            public Copy<T> and( String from ) {
5513                this.from.add(Location.create(createPath(from)));
5514                return this;
5515            }
5516    
5517            public Copy<T> and( Path from ) {
5518                this.from.add(Location.create(from));
5519                return this;
5520            }
5521    
5522            public Copy<T> and( Property firstFrom,
5523                                Property... additionalFroms ) {
5524                this.from.add(Location.create(firstFrom, additionalFroms));
5525                return this;
5526            }
5527    
5528            public Copy<T> and( Iterable<Property> idProperties ) {
5529                this.from.add(Location.create(idProperties));
5530                return this;
5531            }
5532    
5533            public Copy<T> and( Property from ) {
5534                this.from.add(Location.create(from));
5535                return this;
5536            }
5537    
5538            public Copy<T> and( UUID from ) {
5539                this.from.add(Location.create(from));
5540                return this;
5541            }
5542    
5543            /**
5544             * Submit any requests to move the targets into the supplied parent location
5545             * 
5546             * @param from the locations that are being copied
5547             * @param into the parent location
5548             * @param nameForCopy the name that should be used for the copy, or null if the name should be the same as the original
5549             * @return this object, for method chaining
5550             */
5551            protected abstract T submit( Locations from,
5552                                         Location into,
5553                                         Name nameForCopy );
5554    
5555            public T into( Location into ) {
5556                return submit(from, into, null);
5557            }
5558    
5559            public T into( Path into ) {
5560                return submit(from, Location.create(into), null);
5561            }
5562    
5563            public T into( UUID into ) {
5564                return submit(from, Location.create(into), null);
5565            }
5566    
5567            public T into( Property firstIdProperty,
5568                           Property... additionalIdProperties ) {
5569                return submit(from, Location.create(firstIdProperty, additionalIdProperties), null);
5570            }
5571    
5572            public T into( Property into ) {
5573                return submit(from, Location.create(into), null);
5574            }
5575    
5576            public T into( String into ) {
5577                return submit(from, Location.create(createPath(into)), null);
5578            }
5579    
5580            public T to( Location desiredLocation ) {
5581                if (!desiredLocation.hasPath()) {
5582                    throw new IllegalArgumentException(GraphI18n.unableToCopyToLocationWithoutAPath.text(this.from, desiredLocation));
5583                }
5584                Path desiredPath = desiredLocation.getPath();
5585                if (desiredPath.isRoot()) {
5586                    throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredLocation));
5587                }
5588                Path parent = desiredPath.getParent();
5589                return submit(from, Location.create(parent), desiredPath.getLastSegment().getName());
5590            }
5591    
5592            public T to( Path desiredPath ) {
5593                if (desiredPath.isRoot()) {
5594                    throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredPath));
5595                }
5596                Path parent = desiredPath.getParent();
5597                return submit(from, Location.create(parent), desiredPath.getLastSegment().getName());
5598            }
5599    
5600            public T to( String desiredPath ) {
5601                return to(createPath(desiredPath));
5602            }
5603        }
5604    
5605        @NotThreadSafe
5606        protected abstract class CreateAction<T> extends AbstractAction<T> implements Create<T> {
5607            private final String workspaceName;
5608            private final Location parent;
5609            private final Name childName;
5610            private final Map<Name, Property> properties = new HashMap<Name, Property>();
5611            private boolean submitted = false;
5612    
5613            /*package*/CreateAction( T afterConjunction,
5614                                      Location parent,
5615                                      String workspaceName,
5616                                      Name childName ) {
5617                super(afterConjunction);
5618                this.parent = parent;
5619                this.workspaceName = workspaceName;
5620                this.childName = childName;
5621            }
5622    
5623            public Create<T> and( UUID uuid ) {
5624                PropertyFactory factory = getContext().getPropertyFactory();
5625                properties.put(DnaLexicon.UUID, factory.create(DnaLexicon.UUID, uuid));
5626                return this;
5627            }
5628    
5629            public Create<T> and( Property property ) {
5630                properties.put(property.getName(), property);
5631                return this;
5632            }
5633    
5634            public Create<T> and( Iterable<Property> properties ) {
5635                for (Property property : properties) {
5636                    this.properties.put(property.getName(), property);
5637                }
5638                return this;
5639            }
5640    
5641            public Create<T> and( String name,
5642                                  Object... values ) {
5643                ExecutionContext context = getContext();
5644                PropertyFactory factory = context.getPropertyFactory();
5645                NameFactory nameFactory = context.getValueFactories().getNameFactory();
5646                Name propertyName = nameFactory.create(name);
5647                properties.put(propertyName, factory.create(propertyName, values));
5648                return this;
5649            }
5650    
5651            public Create<T> and( Name name,
5652                                  Object... values ) {
5653                PropertyFactory factory = getContext().getPropertyFactory();
5654                properties.put(name, factory.create(name, values));
5655                return this;
5656            }
5657    
5658            public Create<T> and( Property property,
5659                                  Property... additionalProperties ) {
5660                properties.put(property.getName(), property);
5661                for (Property additionalProperty : additionalProperties) {
5662                    properties.put(additionalProperty.getName(), additionalProperty);
5663                }
5664                return this;
5665            }
5666    
5667            public Create<T> with( UUID uuid ) {
5668                return and(uuid);
5669            }
5670    
5671            public Create<T> with( Property property ) {
5672                return and(property);
5673            }
5674    
5675            public Create<T> with( Iterable<Property> properties ) {
5676                return and(properties);
5677            }
5678    
5679            public Create<T> with( Property property,
5680                                   Property... additionalProperties ) {
5681                return and(property, additionalProperties);
5682            }
5683    
5684            public Create<T> with( String name,
5685                                   Object... values ) {
5686                return and(name, values);
5687            }
5688    
5689            public Create<T> with( Name name,
5690                                   Object... values ) {
5691                return and(name, values);
5692            }
5693    
5694            protected abstract T submit( Location parent,
5695                                         String workspaceName,
5696                                         Name childName,
5697                                         Collection<Property> properties );
5698    
5699            @Override
5700            public T and() {
5701                if (!submitted) {
5702                    submit(parent, workspaceName, childName, this.properties.values());
5703                    submitted = true;
5704                }
5705                return super.and();
5706            }
5707        }
5708    
5709        @NotThreadSafe
5710        protected abstract class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
5711            private final Location parent;
5712    
5713            protected CreateNodeNamedAction( T afterConjunction,
5714                                             Location parent ) {
5715                super(afterConjunction);
5716                this.parent = parent;
5717            }
5718    
5719            public CreateAction<T> nodeNamed( String name ) {
5720                NameFactory factory = getContext().getValueFactories().getNameFactory();
5721                Name nameObj = factory.create(name);
5722                return createWith(afterConjunction(), parent, nameObj);
5723            }
5724    
5725            public CreateAction<T> nodeNamed( Name name ) {
5726                return createWith(afterConjunction(), parent, name);
5727            }
5728    
5729            protected abstract CreateAction<T> createWith( T afterConjunction,
5730                                                           Location parent,
5731                                                           Name nodeName );
5732        }
5733    
5734        @Immutable
5735        protected static final class GraphWorkspace implements Workspace {
5736            private final String name;
5737            private final Location root;
5738    
5739            GraphWorkspace( String name,
5740                            Location root ) {
5741                assert name != null;
5742                assert root != null;
5743                this.name = name;
5744                this.root = root;
5745            }
5746    
5747            /**
5748             * {@inheritDoc}
5749             * 
5750             * @see org.jboss.dna.graph.Workspace#getName()
5751             */
5752            public String getName() {
5753                return name;
5754            }
5755    
5756            /**
5757             * {@inheritDoc}
5758             * 
5759             * @see org.jboss.dna.graph.Workspace#getRoot()
5760             */
5761            public Location getRoot() {
5762                return root;
5763            }
5764    
5765            /**
5766             * {@inheritDoc}
5767             * 
5768             * @see java.lang.Object#hashCode()
5769             */
5770            @Override
5771            public int hashCode() {
5772                return this.name.hashCode();
5773            }
5774    
5775            /**
5776             * {@inheritDoc}
5777             * 
5778             * @see java.lang.Object#equals(java.lang.Object)
5779             */
5780            @Override
5781            public boolean equals( Object obj ) {
5782                if (obj == this) return true;
5783                if (obj instanceof GraphWorkspace) {
5784                    GraphWorkspace that = (GraphWorkspace)obj;
5785                    if (!this.getName().equals(that.getName())) return false;
5786                    // all root nodes should be equivalent, so no need to check
5787                    return true;
5788                }
5789                return false;
5790            }
5791    
5792            /**
5793             * {@inheritDoc}
5794             * 
5795             * @see java.lang.Object#toString()
5796             */
5797            @Override
5798            public String toString() {
5799                return "Workspace \"" + this.name + "\" (root = " + this.root + " )";
5800            }
5801        }
5802    
5803        /**
5804         * A set of nodes returned from a {@link Graph graph}, with methods to access the properties and children of the nodes in the
5805         * result. The {@link #iterator()} method can be used to iterate all over the nodes in the result.
5806         * 
5807         * @author Randall Hauch
5808         * @param <NodeType> the type of node that tis results deals with
5809         */
5810        @Immutable
5811        public interface BaseResults<NodeType extends Node> extends Iterable<NodeType> {
5812    
5813            /**
5814             * Get the graph containing the node.
5815             * 
5816             * @return the graph
5817             */
5818            Graph getGraph();
5819    
5820            /**
5821             * Get the node at the supplied location.
5822             * 
5823             * @param path the path of the node in these results
5824             * @return the node, or null if the node is not {@link #includes(Path) included} in these results
5825             */
5826            NodeType getNode( String path );
5827    
5828            /**
5829             * Get the node at the supplied location.
5830             * 
5831             * @param path the path of the node in these results
5832             * @return the node, or null if the node is not {@link #includes(Path) included} in these results
5833             */
5834            NodeType getNode( Path path );
5835    
5836            /**
5837             * Get the node at the supplied location.
5838             * 
5839             * @param location the location of the node
5840             * @return the node, or null if the node is not {@link #includes(Path) included} in these results
5841             */
5842            NodeType getNode( Location location );
5843    
5844            /**
5845             * Return whether these results include a node at the supplied location.
5846             * 
5847             * @param path the path of the node in these results
5848             * @return true if this subgraph includes the supplied location, or false otherwise
5849             */
5850            boolean includes( String path );
5851    
5852            /**
5853             * Return whether this subgraph has a node at the supplied location.
5854             * 
5855             * @param path the path of the node in these results
5856             * @return true if these results includes the supplied location, or false otherwise
5857             */
5858            boolean includes( Path path );
5859    
5860            /**
5861             * Return whether this subgraph has a node at the supplied location.
5862             * 
5863             * @param location the location of the node in these results
5864             * @return true if these results includes the supplied location, or false otherwise
5865             */
5866            boolean includes( Location location );
5867    
5868        }
5869    }