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