View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.Reader;
30  import java.math.BigDecimal;
31  import java.net.URI;
32  import java.net.URISyntaxException;
33  import java.util.ArrayList;
34  import java.util.Calendar;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.Date;
38  import java.util.HashMap;
39  import java.util.Iterator;
40  import java.util.LinkedList;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Set;
44  import java.util.UUID;
45  import java.util.concurrent.TimeUnit;
46  import net.jcip.annotations.Immutable;
47  import net.jcip.annotations.NotThreadSafe;
48  import org.modeshape.common.collection.EmptyIterator;
49  import org.modeshape.common.collection.Problems;
50  import org.modeshape.common.i18n.I18n;
51  import org.modeshape.common.util.CheckArg;
52  import org.modeshape.graph.cache.CachePolicy;
53  import org.modeshape.graph.connector.RepositoryConnection;
54  import org.modeshape.graph.connector.RepositoryConnectionFactory;
55  import org.modeshape.graph.connector.RepositorySource;
56  import org.modeshape.graph.connector.RepositorySourceException;
57  import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource;
58  import org.modeshape.graph.io.GraphImporter;
59  import org.modeshape.graph.property.Binary;
60  import org.modeshape.graph.property.DateTime;
61  import org.modeshape.graph.property.Name;
62  import org.modeshape.graph.property.NameFactory;
63  import org.modeshape.graph.property.Path;
64  import org.modeshape.graph.property.PathNotFoundException;
65  import org.modeshape.graph.property.Property;
66  import org.modeshape.graph.property.PropertyFactory;
67  import org.modeshape.graph.property.Reference;
68  import org.modeshape.graph.property.ValueFactory;
69  import org.modeshape.graph.property.ValueFormatException;
70  import org.modeshape.graph.property.Path.Segment;
71  import org.modeshape.graph.query.QueryContext;
72  import org.modeshape.graph.query.QueryEngine;
73  import org.modeshape.graph.query.QueryResults;
74  import org.modeshape.graph.query.QueryResults.Columns;
75  import org.modeshape.graph.query.model.QueryCommand;
76  import org.modeshape.graph.query.model.TypeSystem;
77  import org.modeshape.graph.query.optimize.Optimizer;
78  import org.modeshape.graph.query.optimize.RuleBasedOptimizer;
79  import org.modeshape.graph.query.plan.CanonicalPlanner;
80  import org.modeshape.graph.query.plan.PlanHints;
81  import org.modeshape.graph.query.plan.PlanNode;
82  import org.modeshape.graph.query.plan.Planner;
83  import org.modeshape.graph.query.process.AbstractAccessComponent;
84  import org.modeshape.graph.query.process.ProcessingComponent;
85  import org.modeshape.graph.query.process.Processor;
86  import org.modeshape.graph.query.process.QueryProcessor;
87  import org.modeshape.graph.query.process.SelectComponent.Analyzer;
88  import org.modeshape.graph.query.validate.Schemata;
89  import org.modeshape.graph.request.AccessQueryRequest;
90  import org.modeshape.graph.request.BatchRequestBuilder;
91  import org.modeshape.graph.request.CacheableRequest;
92  import org.modeshape.graph.request.CloneWorkspaceRequest;
93  import org.modeshape.graph.request.CompositeRequest;
94  import org.modeshape.graph.request.CreateNodeRequest;
95  import org.modeshape.graph.request.CreateWorkspaceRequest;
96  import org.modeshape.graph.request.DestroyWorkspaceRequest;
97  import org.modeshape.graph.request.FullTextSearchRequest;
98  import org.modeshape.graph.request.InvalidRequestException;
99  import org.modeshape.graph.request.InvalidWorkspaceException;
100 import org.modeshape.graph.request.ReadAllChildrenRequest;
101 import org.modeshape.graph.request.ReadAllPropertiesRequest;
102 import org.modeshape.graph.request.ReadBranchRequest;
103 import org.modeshape.graph.request.ReadNodeRequest;
104 import org.modeshape.graph.request.ReadPropertyRequest;
105 import org.modeshape.graph.request.Request;
106 import org.modeshape.graph.request.RequestBuilder;
107 import org.modeshape.graph.request.RequestType;
108 import org.modeshape.graph.request.UnsupportedRequestException;
109 import org.modeshape.graph.request.VerifyWorkspaceRequest;
110 import org.modeshape.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
111 import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
112 import org.xml.sax.SAXException;
113 
114 /**
115  * A graph representation of the content within a {@link RepositorySource}, including mechanisms to interact and manipulate that
116  * content. The graph is designed to be an <i><a href="http://en.wikipedia.org/wiki/Domain_Specific_Language">embedded domain
117  * specific language</a></i>, meaning calls to it are designed to read like sentences even though they are really just Java
118  * methods. And to be more readable, methods can be chained together.
119  */
120 @NotThreadSafe
121 public class Graph {
122 
123     protected static final Iterator<Property> EMPTY_PROPERTIES = new EmptyIterator<Property>();
124     protected static final Iterable<Property> NO_PROPERTIES = new Iterable<Property>() {
125         public final Iterator<Property> iterator() {
126             return EMPTY_PROPERTIES;
127         }
128     };
129 
130     /**
131      * Create a graph instance that uses the supplied repository and {@link ExecutionContext context}.
132      * 
133      * @param sourceName the name of the source that should be used
134      * @param connectionFactory the factory of repository connections
135      * @param context the context in which all executions should be performed
136      * @return the new graph
137      * @throws IllegalArgumentException if the source or context parameters are null
138      * @throws RepositorySourceException if a source with the supplied name does not exist
139      */
140     public static Graph create( String sourceName,
141                                 RepositoryConnectionFactory connectionFactory,
142                                 ExecutionContext context ) {
143         return new Graph(sourceName, connectionFactory, context);
144     }
145 
146     /**
147      * Create a graph instance that uses the supplied {@link RepositoryConnection} and {@link ExecutionContext context}.
148      * 
149      * @param connection the connection that should be used
150      * @param context the context in which all executions should be performed
151      * @return the new graph
152      * @throws IllegalArgumentException if the connection or context parameters are null
153      */
154     public static Graph create( final RepositoryConnection connection,
155                                 ExecutionContext context ) {
156         CheckArg.isNotNull(connection, "connection");
157         final String connectorSourceName = connection.getSourceName();
158         RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
159             public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
160                 if (connectorSourceName.equals(sourceName)) return connection;
161                 return null;
162             }
163         };
164         return new Graph(connectorSourceName, connectionFactory, context);
165     }
166 
167     /**
168      * Create a graph instance that uses the supplied {@link RepositoryConnection} and {@link ExecutionContext context}.
169      * 
170      * @param source the source that should be used
171      * @param context the context in which all executions should be performed
172      * @return the new graph
173      * @throws IllegalArgumentException if the connection or context parameters are null
174      */
175     public static Graph create( final RepositorySource source,
176                                 ExecutionContext context ) {
177         CheckArg.isNotNull(source, "source");
178         final String connectorSourceName = source.getName();
179         RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
180             public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
181                 if (connectorSourceName.equals(sourceName)) return source.getConnection();
182                 return null;
183             }
184         };
185         return new Graph(connectorSourceName, connectionFactory, context);
186     }
187 
188     /**
189      * Create a graph instance that uses a transient, in-memory source and the supplied {@link ExecutionContext context}.
190      * 
191      * @param context the context in which all executions should be performed
192      * @return the new graph
193      * @throws IllegalArgumentException if the context parameter is null
194      */
195     public static Graph create( ExecutionContext context ) {
196         InMemoryRepositorySource source = new InMemoryRepositorySource();
197         source.setName("Transient source");
198         return create(source, context);
199     }
200 
201     private final String sourceName;
202     private final RepositoryConnectionFactory connectionFactory;
203     protected final ExecutionContext context;
204     protected final RequestBuilder requests;
205     protected final Conjunction<Graph> nextGraph;
206     private Workspace currentWorkspace;
207     private QueryEngine queryEngine;
208 
209     protected Graph( String sourceName,
210                      RepositoryConnectionFactory connectionFactory,
211                      ExecutionContext context ) {
212         CheckArg.isNotNull(sourceName, "sourceName");
213         CheckArg.isNotNull(connectionFactory, "connectionFactory");
214         CheckArg.isNotNull(context, "context");
215         this.sourceName = sourceName;
216         this.connectionFactory = connectionFactory;
217         this.context = context;
218         this.nextGraph = new Conjunction<Graph>() {
219             public Graph and() {
220                 return Graph.this;
221             }
222         };
223         this.requests = new RequestBuilder() {
224             @Override
225             protected <T extends Request> T process( T request ) {
226                 Graph.this.execute(request);
227                 return request;
228             }
229         };
230     }
231 
232     /**
233      * Get the RepositoryConnectionFactory that this graph uses to create {@link RepositoryConnection repository connections}.
234      * 
235      * @return the factory repository connections used by this graph; never null
236      */
237     public RepositoryConnectionFactory getConnectionFactory() {
238         return connectionFactory;
239     }
240 
241     /**
242      * The name of the repository that will be used by this graph. This name is passed to the {@link #getConnectionFactory()
243      * connection factory} when this graph needs to {@link RepositoryConnectionFactory#createConnection(String) obtain} a
244      * {@link RepositoryConnection repository connection}.
245      * 
246      * @return the name of the source
247      */
248     public String getSourceName() {
249         return sourceName;
250     }
251 
252     /**
253      * Get the context of execution within which operations on this graph are performed.
254      * 
255      * @return the execution context; never null
256      */
257     public ExecutionContext getContext() {
258         return context;
259     }
260 
261     /**
262      * Obtain a connection to the source, execute the supplied request, and check the request for {@link Request#getError()
263      * errors}. If an error is found, then it is thrown (or wrapped by a {@link RepositorySourceException} if the error is not a
264      * {@link RuntimeException}.
265      * <p>
266      * This method is called automatically when the {@link #requests request builder} creates each request.
267      * </p>
268      * 
269      * @param request the request to be executed (may be a {@link CompositeRequest}.
270      * @throws PathNotFoundException if the request used a node that did not exist
271      * @throws InvalidRequestException if the request was not valid
272      * @throws InvalidWorkspaceException if the workspace used in the request was not valid
273      * @throws UnsupportedRequestException if the request was not supported by the source
274      * @throws RepositorySourceException if an error occurs during execution
275      * @throws RuntimeException if a runtime error occurs during execution
276      */
277     protected void execute( Request request ) {
278         RepositoryConnection connection = getConnectionFactory().createConnection(getSourceName());
279         if (connection == null) {
280             throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
281         }
282         try {
283             connection.execute(getContext(), request);
284         } finally {
285             connection.close();
286         }
287         if (request.hasError()) {
288             Throwable error = request.getError();
289             if (error instanceof RuntimeException) throw (RuntimeException)error;
290             throw new RepositorySourceException(getSourceName(), error);
291         }
292     }
293 
294     /**
295      * Get the default cache policy for this graph. May be null if such a policy has not been defined for thie
296      * {@link #getSourceName() source}.
297      * 
298      * @return the default cache policy, or null if no such policy has been defined for the source
299      * @throws RepositorySourceException if no repository source with the {@link #getSourceName() name} could be found
300      */
301     public CachePolicy getDefaultCachePolicy() {
302         RepositoryConnection connection = this.connectionFactory.createConnection(getSourceName());
303         if (connection == null) {
304             throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
305         }
306         try {
307             return connection.getDefaultCachePolicy();
308         } finally {
309             connection.close();
310         }
311     }
312 
313     /**
314      * Utility method to set the workspace that will be used by this graph.
315      * 
316      * @param workspaceName the name of the workspace; may not be null
317      * @param actualRootLocation the actual location of the root node in the workspace; may not be null
318      * @return the workspace; never null
319      */
320     protected Workspace setWorkspace( String workspaceName,
321                                       Location actualRootLocation ) {
322         assert workspaceName != null;
323         assert actualRootLocation != null;
324         this.currentWorkspace = new GraphWorkspace(workspaceName, actualRootLocation);
325         return this.currentWorkspace;
326     }
327 
328     /**
329      * Get the name of the current workspace being used by this graph. If the graph has not yet been instructed to
330      * {@link #useWorkspace(String) use} or {@link #createWorkspace() create} a workspace, this method will assume that the
331      * source's default workspace is to be used and will obtain from the source the name of that default workspace.
332      * 
333      * @return the name of the current workspace; never null
334      * @see #getCurrentWorkspace()
335      */
336     public String getCurrentWorkspaceName() {
337         return getCurrentWorkspace().getName();
338     }
339 
340     /**
341      * Get the name of the current workspace being used by this graph. If the graph has not yet been instructed to
342      * {@link #useWorkspace(String) use} or {@link #createWorkspace() create} a workspace, this method will assume that the
343      * source's default workspace is to be used and will obtain from the source the name of that default workspace. If the source
344      * does not have a default workspace, this method will fail with an {@link InvalidWorkspaceException}.
345      * 
346      * @return the name of the current workspace; never null
347      * @see #getCurrentWorkspaceName()
348      * @throws InvalidWorkspaceException if there is no current workspace
349      */
350     public Workspace getCurrentWorkspace() {
351         if (this.currentWorkspace == null) {
352             useWorkspace(null);
353         }
354         assert this.currentWorkspace != null;
355         return this.currentWorkspace;
356     }
357 
358     /**
359      * Get the set of workspace names that are known to this source and accessible by this {@link #getContext() context}.
360      * 
361      * @return the set of workspace names; never null
362      */
363     public Set<String> getWorkspaces() {
364         return requests.getWorkspaces().getAvailableWorkspaceNames();
365     }
366 
367     /**
368      * Switch this graph to use another existing workspace in the same source.
369      * 
370      * @param workspaceName the name of the existing workspace that this graph should begin using, or null if the graph should use
371      *        the "default" workspace in the source (if there is one)
372      * @return the workspace; never null
373      * @throws InvalidWorkspaceException if the workspace with the supplied name does not exist, or if null is supplied as the
374      *         workspace name but the source does not have a default workspace
375      */
376     public Workspace useWorkspace( String workspaceName ) {
377         VerifyWorkspaceRequest request = requests.verifyWorkspace(workspaceName);
378         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
379     }
380 
381     /**
382      * 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
383      * created, and all subsequent operations will use the new workspace (until it is changed again by
384      * {@link #useWorkspace(String) using another workspace} or {@link #createWorkspace() creating another}.
385      * 
386      * @return the interface used to complete the request to create a new workspace; never null
387      */
388     public CreateWorkspace createWorkspace() {
389         return new CreateWorkspace() {
390             /**
391              * {@inheritDoc}
392              * 
393              * @see org.modeshape.graph.Graph.NameWorkspace#named(java.lang.String)
394              */
395             public Workspace named( String workspaceName ) {
396                 CreateWorkspaceRequest request = requests.createWorkspace(workspaceName, CreateConflictBehavior.DO_NOT_CREATE);
397                 return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
398             }
399 
400             /**
401              * {@inheritDoc}
402              * 
403              * @see org.modeshape.graph.Graph.CreateWorkspace#namedSomethingLike(java.lang.String)
404              */
405             public Workspace namedSomethingLike( String workspaceName ) {
406                 CreateWorkspaceRequest request = requests.createWorkspace(workspaceName,
407                                                                           CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME);
408                 return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
409             }
410 
411             /**
412              * {@inheritDoc}
413              * 
414              * @see org.modeshape.graph.Graph.CreateWorkspace#clonedFrom(java.lang.String)
415              */
416             public NameWorkspace clonedFrom( final String nameOfWorkspaceToClone ) {
417                 return new NameWorkspace() {
418                     /**
419                      * {@inheritDoc}
420                      * 
421                      * @see org.modeshape.graph.Graph.NameWorkspace#named(java.lang.String)
422                      */
423                     public Workspace named( String nameOfWorkspaceToCreate ) {
424                         CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
425                                                                                 nameOfWorkspaceToCreate,
426                                                                                 CreateConflictBehavior.DO_NOT_CREATE,
427                                                                                 CloneConflictBehavior.DO_NOT_CLONE);
428                         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
429                     }
430 
431                     /**
432                      * {@inheritDoc}
433                      * 
434                      * @see org.modeshape.graph.Graph.NameWorkspace#namedSomethingLike(java.lang.String)
435                      */
436                     public Workspace namedSomethingLike( String nameOfWorkspaceToCreate ) {
437                         CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
438                                                                                 nameOfWorkspaceToCreate,
439                                                                                 CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME,
440                                                                                 CloneConflictBehavior.DO_NOT_CLONE);
441                         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
442                     }
443                 };
444             }
445         };
446     }
447 
448     /**
449      * Destroy an existing workspace in the source used by this graph. It is not possible to destroy the workspace that this graph
450      * is {@link #getCurrentWorkspace() currently using}.
451      * 
452      * @return the interface used to complete the request to create a new workspace; never null
453      */
454     public DestroyWorkspace destroyWorkspace() {
455         return new DestroyWorkspace() {
456             /**
457              * {@inheritDoc}
458              * 
459              * @see org.modeshape.graph.Graph.DestroyWorkspace#named(java.lang.String)
460              */
461             public boolean named( String workspaceName ) {
462                 CheckArg.isNotNull(workspaceName, "workspaceName");
463                 if (getCurrentWorkspaceName().equals(workspaceName)) {
464                     String msg = GraphI18n.currentWorkspaceCannotBeDeleted.text(workspaceName, getSourceName());
465                     throw new InvalidWorkspaceException(msg);
466                 }
467                 DestroyWorkspaceRequest request = requests.destroyWorkspace(workspaceName);
468                 if (request.hasError()) return false;
469                 return true;
470             }
471         };
472     }
473 
474     /**
475      * Request to lock the specified node. This request is submitted to the repository immediately.
476      * 
477      * @param at the node that is to be locked
478      * @return an object that allows the scope of the lock to be defined
479      */
480     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Node at ) {
481         return lock(at.getLocation());
482     }
483 
484     /**
485      * Request to lock the node at the given path. This request is submitted to the repository immediately.
486      * 
487      * @param atPath the path of the node that is to be locked
488      * @return an object that allows the scope of the lock to be defined
489      */
490     public LockScope<LockTimeout<Conjunction<Graph>>> lock( String atPath ) {
491         return lock(Location.create(createPath(atPath)));
492     }
493 
494     /**
495      * Request to lock the node at the given path. This request is submitted to the repository immediately.
496      * 
497      * @param at the path of the node that is to be locked
498      * @return an object that allows the scope of the lock to be defined
499      */
500     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Path at ) {
501         return lock(Location.create(at));
502     }
503 
504     /**
505      * Request to lock the node with the given UUID. This request is submitted to the repository immediately.
506      * 
507      * @param at the UUID of the node that is to be locked
508      * @return an object that allows the scope of the lock to be defined
509      */
510     public LockScope<LockTimeout<Conjunction<Graph>>> lock( UUID at ) {
511         return lock(Location.create(at));
512     }
513 
514     /**
515      * Request to lock the node with the given unique identification property. This request is submitted to the repository
516      * immediately.
517      * 
518      * @param idProperty the unique identifying property of the node that is to be locked
519      * @return an object that allows the scope of the lock to be defined
520      */
521     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Property idProperty ) {
522         return lock(Location.create(idProperty));
523     }
524 
525     /**
526      * Request to lock the node with the given identification properties. The identification properties should uniquely identify a
527      * single node. This request is submitted to the repository immediately.
528      * 
529      * @param firstIdProperty the first identification property of the node that is to be copied
530      * @param additionalIdProperties the remaining identification properties of the node that is to be copied
531      * @return an object that allows the scope of the lock to be defined
532      */
533     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Property firstIdProperty,
534                                                             Property... additionalIdProperties ) {
535         return lock(Location.create(firstIdProperty, additionalIdProperties));
536     }
537 
538     /**
539      * Request to lock the node at the given location. This request is submitted to the repository immediately.
540      * 
541      * @param at the location of the node that is to be locked
542      * @return an object that allows the scope of the lock to be defined
543      */
544     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Location at ) {
545         return new LockAction<Conjunction<Graph>>(this.nextGraph, at) {
546             @Override
547             protected Conjunction<Graph> submit( Location target,
548                                                  org.modeshape.graph.request.LockBranchRequest.LockScope lockScope,
549                                                  long lockTimeoutInMillis ) {
550                 String workspaceName = getCurrentWorkspaceName();
551                 requests.lockBranch(workspaceName, target, lockScope, lockTimeoutInMillis);
552                 return and();
553             }
554         };
555     }
556 
557     /**
558      * Request to unlock the specified node. This request is submitted to the repository immediately.
559      * 
560      * @param at the node that is to be unlocked
561      * @return an object that may be used to start another request
562      */
563     public Conjunction<Graph> unlock( Node at ) {
564         return unlock(at.getLocation());
565     }
566 
567     /**
568      * Request to unlock the node at the given path. This request is submitted to the repository immediately.
569      * 
570      * @param atPath the path of the node that is to be unlocked
571      * @return an object that may be used to start another request
572      */
573     public Conjunction<Graph> unlock( String atPath ) {
574         return unlock(Location.create(createPath(atPath)));
575     }
576 
577     /**
578      * Request to unlock the node at the given path. This request is submitted to the repository immediately.
579      * 
580      * @param at the path of the node that is to be unlocked
581      * @return an object that may be used to start another request
582      */
583     public Conjunction<Graph> unlock( Path at ) {
584         return unlock(Location.create(at));
585     }
586 
587     /**
588      * Request to unlock the node with the given UUID. This request is submitted to the repository immediately.
589      * 
590      * @param at the UUID of the node that is to be unlocked
591      * @return an object that may be used to start another request
592      */
593     public Conjunction<Graph> unlock( UUID at ) {
594         return unlock(Location.create(at));
595     }
596 
597     /**
598      * Request to unlock the node with the given unique identification property. This request is submitted to the repository
599      * immediately.
600      * 
601      * @param idProperty the unique identifying property of the node that is to be unlocked
602      * @return an object that may be used to start another request
603      */
604     public Conjunction<Graph> unlock( Property idProperty ) {
605         return unlock(Location.create(idProperty));
606     }
607 
608     /**
609      * Request to unlock the node with the given identification properties. The identification properties should uniquely identify
610      * a single node. This request is submitted to the repository immediately.
611      * 
612      * @param firstIdProperty the first identification property of the node that is to be copied
613      * @param additionalIdProperties the remaining identification properties of the node that is to be copied
614      * @return an object that may be used to start another request
615      */
616     public Conjunction<Graph> unlock( Property firstIdProperty,
617                                       Property... additionalIdProperties ) {
618         return unlock(Location.create(firstIdProperty, additionalIdProperties));
619     }
620 
621     /**
622      * Request to unlock the node at the given location. This request is submitted to the repository immediately.
623      * 
624      * @param at the location of the node that is to be unlocked
625      * @return an object that may be used to start another request
626      */
627     public Conjunction<Graph> unlock( Location at ) {
628         String workspaceName = getCurrentWorkspaceName();
629         requests.unlockBranch(workspaceName, at);
630         return this.nextGraph;
631     }
632 
633     /**
634      * Begin the request to move the specified node into a parent node at a different location, which is specified via the
635      * <code>into(...)</code> method on the returned {@link Move} object.
636      * <p>
637      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
638      * method is called.
639      * </p>
640      * 
641      * @param from the node that is to be moved.
642      * @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
643      *         be moved
644      */
645     public Move<Conjunction<Graph>> move( Node from ) {
646         return move(from.getLocation());
647     }
648 
649     /**
650      * Begin the request to move a node at the specified location into a parent node at a different location, which is specified
651      * via the <code>into(...)</code> method on the returned {@link Move} object.
652      * <p>
653      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
654      * method is called.
655      * </p>
656      * 
657      * @param from the location of the node that is to be moved.
658      * @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
659      *         be moved
660      */
661     public Move<Conjunction<Graph>> move( Location from ) {
662         return new MoveAction<Conjunction<Graph>>(this.nextGraph, from) {
663             @Override
664             protected Conjunction<Graph> submit( Locations from,
665                                                  Location into,
666                                                  Location before,
667                                                  Name newName ) {
668                 String workspaceName = getCurrentWorkspaceName();
669                 do {
670                     requests.moveBranch(from.getLocation(), into, before, workspaceName, newName);
671                 } while ((from = from.next()) != null);
672                 return and();
673             }
674         };
675     }
676 
677     /**
678      * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
679      * specified via the <code>into(...)</code> method on the returned {@link Move} object.
680      * <p>
681      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
682      * method is called.
683      * </p>
684      * 
685      * @param fromPath the path to the node that is to be moved.
686      * @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
687      *         be moved
688      */
689     public Move<Conjunction<Graph>> move( String fromPath ) {
690         return move(Location.create(createPath(fromPath)));
691     }
692 
693     /**
694      * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
695      * specified via the <code>into(...)</code> method on the returned {@link Move} object.
696      * <p>
697      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
698      * method is called.
699      * </p>
700      * 
701      * @param from the path to the node that is to be moved.
702      * @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
703      *         be moved
704      */
705     public Move<Conjunction<Graph>> move( Path from ) {
706         return move(Location.create(from));
707     }
708 
709     /**
710      * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which is
711      * specified via the <code>into(...)</code> method on the returned {@link Move} object.
712      * <p>
713      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
714      * method is called.
715      * </p>
716      * 
717      * @param from the UUID of the node that is to be moved.
718      * @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
719      *         be moved
720      */
721     public Move<Conjunction<Graph>> move( UUID from ) {
722         return move(Location.create(from));
723     }
724 
725     /**
726      * Begin the request to move a node with the specified unique identification property into a parent node at a different
727      * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification
728      * property should uniquely identify a single node.
729      * <p>
730      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
731      * method is called.
732      * </p>
733      * 
734      * @param idProperty the unique identification property of the node that is to be moved.
735      * @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
736      *         be moved
737      */
738     public Move<Conjunction<Graph>> move( Property idProperty ) {
739         return move(Location.create(idProperty));
740     }
741 
742     /**
743      * Begin the request to move a node with the specified identification properties into a parent node at a different location,
744      * which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification properties
745      * should uniquely identify a single node.
746      * <p>
747      * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
748      * method is called.
749      * </p>
750      * 
751      * @param firstIdProperty the first identification property of the node that is to be moved
752      * @param additionalIdProperties the remaining identification properties of the node that is to be moved
753      * @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
754      *         be moved
755      */
756     public Move<Conjunction<Graph>> move( Property firstIdProperty,
757                                           Property... additionalIdProperties ) {
758         return move(Location.create(firstIdProperty, additionalIdProperties));
759     }
760 
761     /**
762      * Begin the request to clone a node at the specified location into a parent node at a different location, which is specified
763      * via the <code>into(...)</code> method on the returned {@link Clone} object.
764      * <p>
765      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
766      * behavior} is specified.
767      * </p>
768      * <p>
769      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
770      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
771      * copy operation always generates new UUIDs).
772      * </p>
773      * 
774      * @param from the location of the node that is to be cloned.
775      * @return the object that can be used to specify the location of the node where the node is to be cloned
776      */
777     public Clone<Graph> clone( Location from ) {
778         return new CloneAction<Graph>(this, from) {
779             @Override
780             protected Graph submit( String fromWorkspaceName,
781                                     Location from,
782                                     String intoWorkspaceName,
783                                     Location into,
784                                     Name desiredName,
785                                     Segment desiredSegment,
786                                     boolean removeExisting ) {
787                 requests.cloneBranch(from,
788                                      fromWorkspaceName,
789                                      into,
790                                      intoWorkspaceName,
791                                      desiredName,
792                                      desiredSegment,
793                                      removeExisting);
794                 return and();
795             }
796         };
797     }
798 
799     /**
800      * Begin the request to clone the specified node into a parent node at a different location, which is specified via the
801      * <code>into(...)</code> method on the returned {@link Clone} object.
802      * <p>
803      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
804      * behavior} is specified.
805      * </p>
806      * <p>
807      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
808      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
809      * copy operation always generates new UUIDs).
810      * </p>
811      * 
812      * @param from the node that is to be copied.
813      * @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
814      *         be copied
815      */
816     public Clone<Graph> clone( Node from ) {
817         return clone(from.getLocation());
818     }
819 
820     /**
821      * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
822      * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
823      * <p>
824      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
825      * behavior} is specified.
826      * </p>
827      * <p>
828      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
829      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
830      * copy operation always generates new UUIDs).
831      * </p>
832      * 
833      * @param fromPath the path to the node that is to be copied.
834      * @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
835      *         be copied
836      */
837     public Clone<Graph> clone( String fromPath ) {
838         return clone(Location.create(createPath(fromPath)));
839     }
840 
841     /**
842      * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
843      * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
844      * <p>
845      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
846      * behavior} is specified.
847      * </p>
848      * <p>
849      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
850      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
851      * copy operation always generates new UUIDs).
852      * </p>
853      * 
854      * @param from the path to the node that is to be copied.
855      * @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
856      *         be copied
857      */
858     public Clone<Graph> clone( Path from ) {
859         return clone(Location.create(from));
860     }
861 
862     /**
863      * Begin the request to clone a node with the specified unique identifier into a parent node at a different location, which is
864      * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
865      * <p>
866      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
867      * behavior} is specified.
868      * </p>
869      * <p>
870      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
871      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
872      * copy operation always generates new UUIDs).
873      * </p>
874      * 
875      * @param from the UUID of the node that is to be copied.
876      * @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
877      *         be copied
878      */
879     public Clone<Graph> clone( UUID from ) {
880         return clone(Location.create(from));
881     }
882 
883     /**
884      * Begin the request to clone a node with the specified unique identification property into a parent node at a different
885      * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The identification
886      * property should uniquely identify a single node.
887      * <p>
888      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
889      * behavior} is specified.
890      * </p>
891      * <p>
892      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
893      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
894      * copy operation always generates new UUIDs).
895      * </p>
896      * 
897      * @param idProperty the unique identification property of the node that is to be copied.
898      * @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
899      *         be copied
900      */
901     public Clone<Graph> clone( Property idProperty ) {
902         return clone(Location.create(idProperty));
903     }
904 
905     /**
906      * Begin the request to clone a node with the specified identification properties into a parent node at a different location,
907      * which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The identification
908      * properties should uniquely identify a single node.
909      * <p>
910      * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the {@link WithUuids UUID
911      * behavior} is specified.
912      * </p>
913      * <p>
914      * The clone operation differs from the copy operation in that it must replicate nodes from one workspace to another (the copy
915      * operations supports replicating nodes within a workspace as well as across workspaces) and that it preserves UUIDs (the
916      * copy operation always generates new UUIDs).
917      * </p>
918      * 
919      * @param firstIdProperty the first identification property of the node that is to be copied
920      * @param additionalIdProperties the remaining identification properties of the node that is to be copied
921      * @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
922      *         be copied
923      */
924     public Clone<Graph> clone( Property firstIdProperty,
925                                Property... additionalIdProperties ) {
926         return clone(Location.create(firstIdProperty, additionalIdProperties));
927     }
928 
929     /**
930      * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
931      * <code>into(...)</code> method on the returned {@link Copy} object.
932      * <p>
933      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
934      * method is called.
935      * </p>
936      * 
937      * @param from the node that is to be copied.
938      * @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
939      *         be copied
940      */
941     public Copy<Graph> copy( Node from ) {
942         return copy(from.getLocation());
943     }
944 
945     /**
946      * Begin the request to copy a node at the specified location into a parent node at a different location, which is specified
947      * via the <code>into(...)</code> method on the returned {@link Copy} object.
948      * <p>
949      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
950      * method is called.
951      * </p>
952      * 
953      * @param from the location of the node that is to be copied.
954      * @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
955      *         be copied
956      */
957     public Copy<Graph> copy( Location from ) {
958         return new CopyAction<Graph>(this, from) {
959             @Override
960             protected Graph submit( String fromWorkspaceName,
961                                     Locations from,
962                                     Location into,
963                                     Name childName ) {
964                 String workspaceName = fromWorkspaceName != null ? fromWorkspaceName : getCurrentWorkspaceName();
965                 do {
966                     requests.copyBranch(from.getLocation(),
967                                         workspaceName,
968                                         into,
969                                         getCurrentWorkspaceName(),
970                                         childName,
971                                         NodeConflictBehavior.APPEND);
972                 } while ((from = from.next()) != null);
973                 return and();
974             }
975         };
976     }
977 
978     /**
979      * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
980      * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
981      * <p>
982      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
983      * method is called.
984      * </p>
985      * 
986      * @param fromPath the path to the node that is to be copied.
987      * @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
988      *         be copied
989      */
990     public Copy<Graph> copy( String fromPath ) {
991         return copy(Location.create(createPath(fromPath)));
992     }
993 
994     /**
995      * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
996      * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
997      * <p>
998      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
999      * method is called.
1000      * </p>
1001      * 
1002      * @param from the path to the node that is to be copied.
1003      * @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
1004      *         be copied
1005      */
1006     public Copy<Graph> copy( Path from ) {
1007         return copy(Location.create(from));
1008     }
1009 
1010     /**
1011      * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which is
1012      * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
1013      * <p>
1014      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
1015      * method is called.
1016      * </p>
1017      * 
1018      * @param from the UUID of the node that is to be copied.
1019      * @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
1020      *         be copied
1021      */
1022     public Copy<Graph> copy( UUID from ) {
1023         return copy(Location.create(from));
1024     }
1025 
1026     /**
1027      * Begin the request to copy a node with the specified unique identification property into a parent node at a different
1028      * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification
1029      * property should uniquely identify a single node.
1030      * <p>
1031      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
1032      * method is called.
1033      * </p>
1034      * 
1035      * @param idProperty the unique identification property of the node that is to be copied.
1036      * @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
1037      *         be copied
1038      */
1039     public Copy<Graph> copy( Property idProperty ) {
1040         return copy(Location.create(idProperty));
1041     }
1042 
1043     /**
1044      * Begin the request to copy a node with the specified identification properties into a parent node at a different location,
1045      * which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification properties
1046      * should uniquely identify a single node.
1047      * <p>
1048      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
1049      * method is called.
1050      * </p>
1051      * 
1052      * @param firstIdProperty the first identification property of the node that is to be copied
1053      * @param additionalIdProperties the remaining identification properties of the node that is to be copied
1054      * @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
1055      *         be copied
1056      */
1057     public Copy<Graph> copy( Property firstIdProperty,
1058                              Property... additionalIdProperties ) {
1059         return copy(Location.create(firstIdProperty, additionalIdProperties));
1060     }
1061 
1062     /**
1063      * Request to delete the specified node. This request is submitted to the repository immediately.
1064      * 
1065      * @param at the node that is to be deleted
1066      * @return an object that may be used to start another request
1067      */
1068     public Conjunction<Graph> delete( Node at ) {
1069         requests.deleteBranch(at.getLocation(), getCurrentWorkspaceName());
1070         return nextGraph;
1071     }
1072 
1073     /**
1074      * Request to delete the node at the given location. This request is submitted to the repository immediately.
1075      * 
1076      * @param at the location of the node that is to be deleted
1077      * @return an object that may be used to start another request
1078      */
1079     public Conjunction<Graph> delete( Location at ) {
1080         requests.deleteBranch(at, getCurrentWorkspaceName());
1081         return nextGraph;
1082     }
1083 
1084     /**
1085      * Request to delete the node at the given path. This request is submitted to the repository immediately.
1086      * 
1087      * @param atPath the path of the node that is to be deleted
1088      * @return an object that may be used to start another request
1089      */
1090     public Conjunction<Graph> delete( String atPath ) {
1091         return delete(Location.create(createPath(atPath)));
1092     }
1093 
1094     /**
1095      * Request to delete the node at the given path. This request is submitted to the repository immediately.
1096      * 
1097      * @param at the path of the node that is to be deleted
1098      * @return an object that may be used to start another request
1099      */
1100     public Conjunction<Graph> delete( Path at ) {
1101         return delete(Location.create(at));
1102     }
1103 
1104     /**
1105      * Request to delete the node with the given UUID. This request is submitted to the repository immediately.
1106      * 
1107      * @param at the UUID of the node that is to be deleted
1108      * @return an object that may be used to start another request
1109      */
1110     public Conjunction<Graph> delete( UUID at ) {
1111         return delete(Location.create(at));
1112     }
1113 
1114     /**
1115      * Request to delete the node with the given unique identification property. This request is submitted to the repository
1116      * immediately.
1117      * 
1118      * @param idProperty the unique identifying property of the node that is to be deleted
1119      * @return an object that may be used to start another request
1120      */
1121     public Conjunction<Graph> delete( Property idProperty ) {
1122         return delete(Location.create(idProperty));
1123     }
1124 
1125     /**
1126      * Request to delete the node with the given identification properties. The identification properties should uniquely identify
1127      * a single node. This request is submitted to the repository immediately.
1128      * 
1129      * @param firstIdProperty the first identification property of the node that is to be copied
1130      * @param additionalIdProperties the remaining identification properties of the node that is to be copied
1131      * @return an object that may be used to start another request
1132      */
1133     public Conjunction<Graph> delete( Property firstIdProperty,
1134                                       Property... additionalIdProperties ) {
1135         return delete(Location.create(firstIdProperty, additionalIdProperties));
1136     }
1137 
1138     /**
1139      * Begin the request to create a node located at the supplied path, and return an interface used to either add properties for
1140      * the new node, or complete/submit the request and return the location, node, or graph.
1141      * <p>
1142      * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
1143      * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
1144      * parent or new node.
1145      * </p>
1146      * 
1147      * @param atPath the path to the node that is to be created.
1148      * @return an object that may be used to start another request
1149      */
1150     public CreateAt<Graph> createAt( String atPath ) {
1151         return createAt(createPath(atPath));
1152     }
1153 
1154     /**
1155      * Begin the request to create a node located at the supplied path, and return an interface used to either add properties for
1156      * the new node, or complete/submit the request and return the location, node, or graph.
1157      * <p>
1158      * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
1159      * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
1160      * parent or new node.
1161      * </p>
1162      * 
1163      * @param at the path to the node that is to be created.
1164      * @return an object that may be used to start another request
1165      */
1166     public CreateAt<Graph> createAt( final Path at ) {
1167         CheckArg.isNotNull(at, "at");
1168         final Path parent = at.getParent();
1169         final Name childName = at.getLastSegment().getName();
1170         final String workspaceName = getCurrentWorkspaceName();
1171         return new CreateAt<Graph>() {
1172             private final List<Property> properties = new LinkedList<Property>();
1173 
1174             public CreateAt<Graph> and( UUID uuid ) {
1175                 PropertyFactory factory = getContext().getPropertyFactory();
1176                 properties.add(factory.create(ModeShapeLexicon.UUID, uuid));
1177                 return this;
1178             }
1179 
1180             public CreateAt<Graph> and( Property property ) {
1181                 properties.add(property);
1182                 return this;
1183             }
1184 
1185             public CreateAt<Graph> and( Iterable<Property> properties ) {
1186                 for (Property property : properties) {
1187                     this.properties.add(property);
1188                 }
1189                 return this;
1190             }
1191 
1192             public CreateAt<Graph> and( String name,
1193                                         Object... values ) {
1194                 ExecutionContext context = getContext();
1195                 PropertyFactory factory = context.getPropertyFactory();
1196                 NameFactory nameFactory = context.getValueFactories().getNameFactory();
1197                 properties.add(factory.create(nameFactory.create(name), values));
1198                 return this;
1199             }
1200 
1201             public CreateAt<Graph> and( Name name,
1202                                         Object... values ) {
1203                 ExecutionContext context = getContext();
1204                 PropertyFactory factory = context.getPropertyFactory();
1205                 properties.add(factory.create(name, values));
1206                 return this;
1207             }
1208 
1209             public CreateAt<Graph> and( Property property,
1210                                         Property... additionalProperties ) {
1211                 properties.add(property);
1212                 for (Property additionalProperty : additionalProperties) {
1213                     properties.add(additionalProperty);
1214                 }
1215                 return this;
1216             }
1217 
1218             public CreateAt<Graph> with( UUID uuid ) {
1219                 return and(uuid);
1220             }
1221 
1222             public CreateAt<Graph> with( Property property ) {
1223                 return and(property);
1224             }
1225 
1226             public CreateAt<Graph> with( Iterable<Property> properties ) {
1227                 return and(properties);
1228             }
1229 
1230             public CreateAt<Graph> with( Property property,
1231                                          Property... additionalProperties ) {
1232                 return and(property, additionalProperties);
1233             }
1234 
1235             public CreateAt<Graph> with( String name,
1236                                          Object... values ) {
1237                 return and(name, values);
1238             }
1239 
1240             public CreateAt<Graph> with( Name name,
1241                                          Object... values ) {
1242                 return and(name, values);
1243             }
1244 
1245             public Location getLocation() {
1246                 Location parentLoc = Location.create(parent);
1247                 CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
1248                 return request.getActualLocationOfNode();
1249             }
1250 
1251             public Node getNode() {
1252                 Location parentLoc = Location.create(parent);
1253                 CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
1254                 return getNodeAt(request.getActualLocationOfNode());
1255             }
1256 
1257             public Graph and() {
1258                 requests.createNode(Location.create(parent), workspaceName, childName, this.properties.iterator());
1259                 return Graph.this;
1260             }
1261         };
1262     }
1263 
1264     /**
1265      * Begin the request to create a node located at the supplied path.
1266      * <p>
1267      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1268      * is called.
1269      * </p>
1270      * 
1271      * @param atPath the path to the node that is to be created.
1272      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1273      *         node where the node is to be created
1274      */
1275     public Create<Graph> create( String atPath ) {
1276         return create(createPath(atPath));
1277     }
1278 
1279     /**
1280      * Begin the request to create a node located at the supplied path.
1281      * <p>
1282      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1283      * is called.
1284      * </p>
1285      * 
1286      * @param atPath the path to the node that is to be created.
1287      * @param property a property for the new node
1288      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1289      *         node where the node is to be created
1290      */
1291     public Create<Graph> create( String atPath,
1292                                  Property property ) {
1293         return create(createPath(atPath)).with(property);
1294     }
1295 
1296     /**
1297      * Begin the request to create a node located at the supplied path.
1298      * <p>
1299      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1300      * is called.
1301      * </p>
1302      * 
1303      * @param atPath the path to the node that is to be created.
1304      * @param firstProperty a property for the new node
1305      * @param additionalProperties additional properties for the new node
1306      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1307      *         node where the node is to be created
1308      */
1309     public Create<Graph> create( String atPath,
1310                                  Property firstProperty,
1311                                  Property... additionalProperties ) {
1312         return create(createPath(atPath)).with(firstProperty, additionalProperties);
1313     }
1314 
1315     /**
1316      * Begin the request to create a node located at the supplied path.
1317      * <p>
1318      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1319      * is called.
1320      * </p>
1321      * 
1322      * @param at the path to the node that is to be created.
1323      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1324      *         node where the node is to be created
1325      */
1326     public final Create<Graph> create( Path at ) {
1327         CheckArg.isNotNull(at, "at");
1328         Path parent = at.getParent();
1329         Name name = at.getLastSegment().getName();
1330         return create(Location.create(parent), name);
1331     }
1332 
1333     protected final CreateAction<Graph> create( Location parent,
1334                                                 Name child ) {
1335         return new CreateAction<Graph>(this, parent, getCurrentWorkspaceName(), child) {
1336             @Override
1337             protected Graph submit( Location parent,
1338                                     String workspaceName,
1339                                     Name childName,
1340                                     Collection<Property> properties,
1341                                     NodeConflictBehavior behavior ) {
1342                 requests.createNode(parent, workspaceName, childName, properties.iterator(), behavior);
1343                 return Graph.this;
1344             }
1345 
1346         };
1347     }
1348 
1349     /**
1350      * Begin the request to create a node located at the supplied path.
1351      * <p>
1352      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1353      * is called.
1354      * </p>
1355      * 
1356      * @param at the path to the node that is to be created.
1357      * @param properties the iterator over the properties for the new node
1358      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1359      *         node where the node is to be created
1360      */
1361     public Create<Graph> create( Path at,
1362                                  Iterable<Property> properties ) {
1363         Create<Graph> action = create(at);
1364         for (Property property : properties) {
1365             action.and(property);
1366         }
1367         return action;
1368     }
1369 
1370     /**
1371      * Begin the request to create a node located at the supplied path.
1372      * <p>
1373      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1374      * is called.
1375      * </p>
1376      * 
1377      * @param at the path to the node that is to be created.
1378      * @param property a property for the new node
1379      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1380      *         node where the node is to be created
1381      */
1382     public Create<Graph> create( Path at,
1383                                  Property property ) {
1384         return create(at).with(property);
1385     }
1386 
1387     /**
1388      * Begin the request to create a node located at the supplied path.
1389      * <p>
1390      * Like all other methods on the {@link Graph}, the request will be performed when the no-argument {@link Create#and()} method
1391      * is called.
1392      * </p>
1393      * 
1394      * @param at the path to the node that is to be created.
1395      * @param firstProperty a property for the new node
1396      * @param additionalProperties additional properties for the new node
1397      * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1398      *         node where the node is to be created
1399      */
1400     public Create<Graph> create( Path at,
1401                                  Property firstProperty,
1402                                  Property... additionalProperties ) {
1403         return create(at).with(firstProperty, additionalProperties);
1404     }
1405 
1406     /**
1407      * Begin the request to create a node under the existing parent node at the supplied location. Use this method if you are
1408      * creating a node when you have the {@link Location} of a parent from a previous request.
1409      * <p>
1410      * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>node(...)</code>
1411      * method is called on the returned object
1412      * </p>
1413      * 
1414      * @param parent the location of the parent
1415      * @return the object used to start creating a node
1416      */
1417     public CreateNode<Conjunction<Graph>> createUnder( final Location parent ) {
1418         final NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
1419         CheckArg.isNotNull(parent, "parent");
1420         return new CreateNode<Conjunction<Graph>>() {
1421             public Conjunction<Graph> node( String name,
1422                                             Property... properties ) {
1423                 Name child = nameFactory.create(name);
1424                 requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
1425                 return nextGraph;
1426             }
1427 
1428             public Conjunction<Graph> node( String name,
1429                                             Iterator<Property> properties ) {
1430                 Name child = nameFactory.create(name);
1431                 requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
1432                 return nextGraph;
1433             }
1434 
1435             public Conjunction<Graph> node( String name,
1436                                             Iterable<Property> properties ) {
1437                 Name child = nameFactory.create(name);
1438                 requests.createNode(parent, getCurrentWorkspaceName(), child, properties.iterator());
1439                 return nextGraph;
1440             }
1441         };
1442     }
1443 
1444     public AddValue<Graph> addValue( Object value ) {
1445         return new AddValueAction<Graph>(this, this.getCurrentWorkspaceName(), value) {
1446 
1447             @Override
1448             protected Graph submit( String workspaceName,
1449                                     Location on,
1450                                     Name property,
1451                                     List<Object> values ) {
1452                 requests.addValues(workspaceName, on, property, values);
1453                 return nextGraph.and();
1454             }
1455         };
1456     }
1457 
1458     public RemoveValue<Graph> removeValue( Object value ) {
1459         return new RemoveValueAction<Graph>(this, this.getCurrentWorkspaceName(), value) {
1460 
1461             @Override
1462             protected Graph submit( String workspaceName,
1463                                     Location on,
1464                                     Name property,
1465                                     List<Object> values ) {
1466                 requests.removeValues(workspaceName, on, property, values);
1467                 return nextGraph.and();
1468             }
1469         };
1470     }
1471 
1472     /**
1473      * Set the properties on a node.
1474      * 
1475      * @param properties the properties to set
1476      * @return the remove request object that should be used to specify the node on which the properties are to be set.
1477      */
1478     public On<Conjunction<Graph>> set( final Property... properties ) {
1479         return new On<Conjunction<Graph>>() {
1480             public Conjunction<Graph> on( Location location ) {
1481                 requests.setProperties(location, getCurrentWorkspaceName(), properties);
1482                 return nextGraph;
1483             }
1484 
1485             public Conjunction<Graph> on( String path ) {
1486                 return on(Location.create(createPath(path)));
1487             }
1488 
1489             public Conjunction<Graph> on( Path path ) {
1490                 return on(Location.create(path));
1491             }
1492 
1493             public Conjunction<Graph> on( Property idProperty ) {
1494                 return on(Location.create(idProperty));
1495             }
1496 
1497             public Conjunction<Graph> on( Property firstIdProperty,
1498                                           Property... additionalIdProperties ) {
1499                 return on(Location.create(firstIdProperty, additionalIdProperties));
1500             }
1501 
1502             public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1503                 return on(Location.create(idProperties));
1504             }
1505 
1506             public Conjunction<Graph> on( UUID uuid ) {
1507                 return on(Location.create(uuid));
1508             }
1509         };
1510     }
1511 
1512     /**
1513      * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
1514      * value(s) and the location of the node onto which the property should be set.
1515      * 
1516      * @param propertyName the property name
1517      * @return the interface used to specify the values
1518      */
1519     public SetValues<Conjunction<Graph>> set( String propertyName ) {
1520         Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
1521         return set(name);
1522     }
1523 
1524     /**
1525      * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
1526      * value(s) and the location of the node onto which the property should be set.
1527      * 
1528      * @param propertyName the property name
1529      * @return the interface used to specify the values
1530      */
1531     public SetValues<Conjunction<Graph>> set( final Name propertyName ) {
1532         return new SetValues<Conjunction<Graph>>() {
1533             public SetValuesTo<Conjunction<Graph>> on( final Location location ) {
1534                 return new SetValuesTo<Conjunction<Graph>>() {
1535                     public Conjunction<Graph> to( Node value ) {
1536                         Reference ref = (Reference)convertReferenceValue(value);
1537                         Property property = getContext().getPropertyFactory().create(propertyName, ref);
1538                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1539                         return nextGraph;
1540                     }
1541 
1542                     public Conjunction<Graph> to( Location value ) {
1543                         Reference ref = (Reference)convertReferenceValue(value);
1544                         Property property = getContext().getPropertyFactory().create(propertyName, ref);
1545                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1546                         return nextGraph;
1547                     }
1548 
1549                     protected Conjunction<Graph> toValue( Object value ) {
1550                         Property property = getContext().getPropertyFactory().create(propertyName, value);
1551                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1552                         return nextGraph;
1553                     }
1554 
1555                     public Conjunction<Graph> to( String value ) {
1556                         return toValue(value);
1557                     }
1558 
1559                     public Conjunction<Graph> to( int value ) {
1560                         return toValue(Integer.valueOf(value));
1561                     }
1562 
1563                     public Conjunction<Graph> to( long value ) {
1564                         return toValue(Long.valueOf(value));
1565                     }
1566 
1567                     public Conjunction<Graph> to( boolean value ) {
1568                         return toValue(Boolean.valueOf(value));
1569                     }
1570 
1571                     public Conjunction<Graph> to( float value ) {
1572                         return toValue(Float.valueOf(value));
1573                     }
1574 
1575                     public Conjunction<Graph> to( double value ) {
1576                         return toValue(Double.valueOf(value));
1577                     }
1578 
1579                     public Conjunction<Graph> to( BigDecimal value ) {
1580                         return toValue(value);
1581                     }
1582 
1583                     public Conjunction<Graph> to( Calendar value ) {
1584                         return toValue(value);
1585                     }
1586 
1587                     public Conjunction<Graph> to( Date value ) {
1588                         return toValue(value);
1589                     }
1590 
1591                     public Conjunction<Graph> to( DateTime value ) {
1592                         return toValue(value);
1593                     }
1594 
1595                     public Conjunction<Graph> to( Name value ) {
1596                         return toValue(value);
1597                     }
1598 
1599                     public Conjunction<Graph> to( Path value ) {
1600                         return toValue(value);
1601                     }
1602 
1603                     public Conjunction<Graph> to( Reference value ) {
1604                         return toValue(value);
1605                     }
1606 
1607                     public Conjunction<Graph> to( URI value ) {
1608                         return toValue(value);
1609                     }
1610 
1611                     public Conjunction<Graph> to( UUID value ) {
1612                         return toValue(value);
1613                     }
1614 
1615                     public Conjunction<Graph> to( Binary value ) {
1616                         return toValue(value);
1617                     }
1618 
1619                     public Conjunction<Graph> to( byte[] value ) {
1620                         return toValue(value);
1621                     }
1622 
1623                     public Conjunction<Graph> to( InputStream stream,
1624                                                   long approximateLength ) {
1625                         Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
1626                         return toValue(value);
1627                     }
1628 
1629                     public Conjunction<Graph> to( Reader reader,
1630                                                   long approximateLength ) {
1631                         Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
1632                         return toValue(value);
1633                     }
1634 
1635                     public Conjunction<Graph> to( Object value ) {
1636                         value = convertReferenceValue(value);
1637                         Property property = getContext().getPropertyFactory().create(propertyName, value);
1638                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1639                         return nextGraph;
1640                     }
1641 
1642                     public Conjunction<Graph> to( Object firstValue,
1643                                                   Object... otherValues ) {
1644                         firstValue = convertReferenceValue(firstValue);
1645                         for (int i = 0, len = otherValues.length; i != len; ++i) {
1646                             otherValues[i] = convertReferenceValue(otherValues[i]);
1647                         }
1648                         Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
1649                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1650                         return nextGraph;
1651                     }
1652 
1653                     public Conjunction<Graph> to( Object[] values ) {
1654                         for (int i = 0, len = values.length; i != len; ++i) {
1655                             values[i] = convertReferenceValue(values[i]);
1656                         }
1657                         Property property = getContext().getPropertyFactory().create(propertyName, values);
1658                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1659                         return nextGraph;
1660                     }
1661 
1662                     public Conjunction<Graph> to( Iterable<?> values ) {
1663                         List<Object> valueList = new LinkedList<Object>();
1664                         for (Object value : values) {
1665                             value = convertReferenceValue(value);
1666                             valueList.add(value);
1667                         }
1668                         Property property = getContext().getPropertyFactory().create(propertyName, valueList);
1669                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1670                         return nextGraph;
1671                     }
1672 
1673                     public Conjunction<Graph> to( Iterator<?> values ) {
1674                         List<Object> valueList = new LinkedList<Object>();
1675                         while (values.hasNext()) {
1676                             Object value = values.next();
1677                             valueList.add(value);
1678                         }
1679                         Property property = getContext().getPropertyFactory().create(propertyName, valueList);
1680                         requests.setProperty(location, getCurrentWorkspaceName(), property);
1681                         return nextGraph;
1682                     }
1683                 };
1684             }
1685 
1686             public SetValuesTo<Conjunction<Graph>> on( String path ) {
1687                 return on(Location.create(createPath(path)));
1688             }
1689 
1690             public SetValuesTo<Conjunction<Graph>> on( Path path ) {
1691                 return on(Location.create(path));
1692             }
1693 
1694             public SetValuesTo<Conjunction<Graph>> on( Property idProperty ) {
1695                 return on(Location.create(idProperty));
1696             }
1697 
1698             public SetValuesTo<Conjunction<Graph>> on( Property firstIdProperty,
1699                                                        Property... additionalIdProperties ) {
1700                 return on(Location.create(firstIdProperty, additionalIdProperties));
1701             }
1702 
1703             public SetValuesTo<Conjunction<Graph>> on( Iterable<Property> idProperties ) {
1704                 return on(Location.create(idProperties));
1705             }
1706 
1707             public SetValuesTo<Conjunction<Graph>> on( UUID uuid ) {
1708                 return on(Location.create(uuid));
1709             }
1710 
1711             public On<Conjunction<Graph>> to( Node node ) {
1712                 Reference value = (Reference)convertReferenceValue(node);
1713                 return set(getContext().getPropertyFactory().create(propertyName, value));
1714             }
1715 
1716             public On<Conjunction<Graph>> to( Location location ) {
1717                 Reference value = (Reference)convertReferenceValue(location);
1718                 return set(getContext().getPropertyFactory().create(propertyName, value));
1719             }
1720 
1721             protected On<Conjunction<Graph>> toValue( Object value ) {
1722                 return set(getContext().getPropertyFactory().create(propertyName, value));
1723             }
1724 
1725             public On<Conjunction<Graph>> to( String value ) {
1726                 return toValue(value);
1727             }
1728 
1729             public On<Conjunction<Graph>> to( int value ) {
1730                 return toValue(Integer.valueOf(value));
1731             }
1732 
1733             public On<Conjunction<Graph>> to( long value ) {
1734                 return toValue(Long.valueOf(value));
1735             }
1736 
1737             public On<Conjunction<Graph>> to( boolean value ) {
1738                 return toValue(Boolean.valueOf(value));
1739             }
1740 
1741             public On<Conjunction<Graph>> to( float value ) {
1742                 return toValue(Float.valueOf(value));
1743             }
1744 
1745             public On<Conjunction<Graph>> to( double value ) {
1746                 return toValue(Double.valueOf(value));
1747             }
1748 
1749             public On<Conjunction<Graph>> to( BigDecimal value ) {
1750                 return toValue(value);
1751             }
1752 
1753             public On<Conjunction<Graph>> to( Calendar value ) {
1754                 return toValue(value);
1755             }
1756 
1757             public On<Conjunction<Graph>> to( Date value ) {
1758                 return toValue(value);
1759             }
1760 
1761             public On<Conjunction<Graph>> to( DateTime value ) {
1762                 return toValue(value);
1763             }
1764 
1765             public On<Conjunction<Graph>> to( Name value ) {
1766                 return toValue(value);
1767             }
1768 
1769             public On<Conjunction<Graph>> to( Path value ) {
1770                 return toValue(value);
1771             }
1772 
1773             public On<Conjunction<Graph>> to( Reference value ) {
1774                 return toValue(value);
1775             }
1776 
1777             public On<Conjunction<Graph>> to( URI value ) {
1778                 return toValue(value);
1779             }
1780 
1781             public On<Conjunction<Graph>> to( UUID value ) {
1782                 return toValue(value);
1783             }
1784 
1785             public On<Conjunction<Graph>> to( Binary value ) {
1786                 return toValue(value);
1787             }
1788 
1789             public On<Conjunction<Graph>> to( byte[] value ) {
1790                 return toValue(value);
1791             }
1792 
1793             public On<Conjunction<Graph>> to( InputStream stream,
1794                                               long approximateLength ) {
1795                 Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
1796                 return toValue(value);
1797             }
1798 
1799             public On<Conjunction<Graph>> to( Reader reader,
1800                                               long approximateLength ) {
1801                 Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
1802                 return toValue(value);
1803             }
1804 
1805             public On<Conjunction<Graph>> to( Object value ) {
1806                 value = convertReferenceValue(value);
1807                 return set(getContext().getPropertyFactory().create(propertyName, value));
1808             }
1809 
1810             public On<Conjunction<Graph>> to( Object firstValue,
1811                                               Object... otherValues ) {
1812                 firstValue = convertReferenceValue(firstValue);
1813                 for (int i = 0, len = otherValues.length; i != len; ++i) {
1814                     otherValues[i] = convertReferenceValue(otherValues[i]);
1815                 }
1816                 return set(getContext().getPropertyFactory().create(propertyName, firstValue, otherValues));
1817             }
1818 
1819             public On<Conjunction<Graph>> to( Object[] values ) {
1820                 for (int i = 0, len = values.length; i != len; ++i) {
1821                     values[i] = convertReferenceValue(values[i]);
1822                 }
1823                 return set(getContext().getPropertyFactory().create(propertyName, values));
1824             }
1825 
1826             public On<Conjunction<Graph>> to( Iterable<?> values ) {
1827                 List<Object> valueList = new LinkedList<Object>();
1828                 for (Object value : values) {
1829                     value = convertReferenceValue(value);
1830                     valueList.add(value);
1831                 }
1832                 return set(getContext().getPropertyFactory().create(propertyName, valueList));
1833             }
1834 
1835             public On<Conjunction<Graph>> to( Iterator<?> values ) {
1836                 List<Object> valueList = new LinkedList<Object>();
1837                 while (values.hasNext()) {
1838                     Object value = values.next();
1839                     valueList.add(value);
1840                 }
1841                 return set(getContext().getPropertyFactory().create(propertyName, valueList));
1842             }
1843         };
1844     }
1845 
1846     /**
1847      * Remove properties from the node at the given location.
1848      * 
1849      * @param propertyNames the names of the properties to be removed
1850      * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1851      */
1852     public On<Conjunction<Graph>> remove( final Name... propertyNames ) {
1853         return new On<Conjunction<Graph>>() {
1854             public Conjunction<Graph> on( Location location ) {
1855                 requests.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
1856                 return nextGraph;
1857             }
1858 
1859             public Conjunction<Graph> on( String path ) {
1860                 return on(Location.create(createPath(path)));
1861             }
1862 
1863             public Conjunction<Graph> on( Path path ) {
1864                 return on(Location.create(path));
1865             }
1866 
1867             public Conjunction<Graph> on( Property idProperty ) {
1868                 return on(Location.create(idProperty));
1869             }
1870 
1871             public Conjunction<Graph> on( Property firstIdProperty,
1872                                           Property... additionalIdProperties ) {
1873                 return on(Location.create(firstIdProperty, additionalIdProperties));
1874             }
1875 
1876             public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1877                 return on(Location.create(idProperties));
1878             }
1879 
1880             public Conjunction<Graph> on( UUID uuid ) {
1881                 return on(Location.create(uuid));
1882             }
1883         };
1884     }
1885 
1886     /**
1887      * Remove properties from the node at the given location.
1888      * 
1889      * @param propertyNames the names of the properties to be removed
1890      * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1891      */
1892     public On<Conjunction<Graph>> remove( final String... propertyNames ) {
1893         NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
1894         int number = propertyNames.length;
1895         final Name[] names = new Name[number];
1896         for (int i = 0; i != number; ++i) {
1897             names[i] = nameFactory.create(propertyNames[i]);
1898         }
1899         return new On<Conjunction<Graph>>() {
1900             public Conjunction<Graph> on( Location location ) {
1901                 requests.removeProperties(location, getCurrentWorkspaceName(), names);
1902                 return nextGraph;
1903             }
1904 
1905             public Conjunction<Graph> on( String path ) {
1906                 return on(Location.create(createPath(path)));
1907             }
1908 
1909             public Conjunction<Graph> on( Path path ) {
1910                 return on(Location.create(path));
1911             }
1912 
1913             public Conjunction<Graph> on( Property idProperty ) {
1914                 return on(Location.create(idProperty));
1915             }
1916 
1917             public Conjunction<Graph> on( Property firstIdProperty,
1918                                           Property... additionalIdProperties ) {
1919                 return on(Location.create(firstIdProperty, additionalIdProperties));
1920             }
1921 
1922             public Conjunction<Graph> on( Iterable<Property> idProperties ) {
1923                 return on(Location.create(idProperties));
1924             }
1925 
1926             public Conjunction<Graph> on( UUID uuid ) {
1927                 return on(Location.create(uuid));
1928             }
1929         };
1930     }
1931 
1932     /**
1933      * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
1934      * object. Once the location is specified, the {@link Collection collection of properties} are read and then returned.
1935      * 
1936      * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
1937      */
1938     public On<Collection<Property>> getProperties() {
1939         return new On<Collection<Property>>() {
1940             public Collection<Property> on( Location location ) {
1941                 return requests.readAllProperties(location, getCurrentWorkspaceName()).getProperties();
1942             }
1943 
1944             public Collection<Property> on( String path ) {
1945                 return on(Location.create(createPath(path)));
1946             }
1947 
1948             public Collection<Property> on( Path path ) {
1949                 return on(Location.create(path));
1950             }
1951 
1952             public Collection<Property> on( Property idProperty ) {
1953                 return on(Location.create(idProperty));
1954             }
1955 
1956             public Collection<Property> on( Property firstIdProperty,
1957                                             Property... additionalIdProperties ) {
1958                 return on(Location.create(firstIdProperty, additionalIdProperties));
1959             }
1960 
1961             public Collection<Property> on( Iterable<Property> idProperties ) {
1962                 return on(Location.create(idProperties));
1963             }
1964 
1965             public Collection<Property> on( UUID uuid ) {
1966                 return on(Location.create(uuid));
1967             }
1968         };
1969     }
1970 
1971     /**
1972      * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
1973      * object. Once the location is specified, the {@link Map map of properties} are read and then returned.
1974      * 
1975      * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
1976      *         as a map keyed by their name
1977      */
1978     public On<Map<Name, Property>> getPropertiesByName() {
1979         return new On<Map<Name, Property>>() {
1980             public Map<Name, Property> on( Location location ) {
1981                 return requests.readAllProperties(location, getCurrentWorkspaceName()).getPropertiesByName();
1982             }
1983 
1984             public Map<Name, Property> on( String path ) {
1985                 return on(Location.create(createPath(path)));
1986             }
1987 
1988             public Map<Name, Property> on( Path path ) {
1989                 return on(Location.create(path));
1990             }
1991 
1992             public Map<Name, Property> on( Property idProperty ) {
1993                 return on(Location.create(idProperty));
1994             }
1995 
1996             public Map<Name, Property> on( Property firstIdProperty,
1997                                            Property... additionalIdProperties ) {
1998                 return on(Location.create(firstIdProperty, additionalIdProperties));
1999             }
2000 
2001             public Map<Name, Property> on( Iterable<Property> idProperties ) {
2002                 return on(Location.create(idProperties));
2003             }
2004 
2005             public Map<Name, Property> on( UUID uuid ) {
2006                 return on(Location.create(uuid));
2007             }
2008         };
2009     }
2010 
2011     /**
2012      * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
2013      * object. The returned object is used to supply the remaining information, including either the {@link Children#of(Location)
2014      * location of the parent}, or that a subset of the children should be retrieved {@link Children#inBlockOf(int) in a block}.
2015      * 
2016      * @return the object that is used to specify the remaining inputs for the request, and which will return the children
2017      */
2018     public Children<List<Location>> getChildren() {
2019         return new Children<List<Location>>() {
2020             public List<Location> of( String path ) {
2021                 return of(Location.create(createPath(path)));
2022             }
2023 
2024             public List<Location> of( Path path ) {
2025                 return of(Location.create(path));
2026             }
2027 
2028             public List<Location> of( Property idProperty ) {
2029                 return of(Location.create(idProperty));
2030             }
2031 
2032             public List<Location> of( Property firstIdProperty,
2033                                       Property... additionalIdProperties ) {
2034                 return of(Location.create(firstIdProperty, additionalIdProperties));
2035             }
2036 
2037             public List<Location> of( Iterable<Property> idProperties ) {
2038                 return of(Location.create(idProperties));
2039             }
2040 
2041             public List<Location> of( UUID uuid ) {
2042                 return of(Location.create(uuid));
2043             }
2044 
2045             public List<Location> of( Location at ) {
2046                 return requests.readAllChildren(at, getCurrentWorkspaceName()).getChildren();
2047             }
2048 
2049             public BlockOfChildren<List<Location>> inBlockOf( final int blockSize ) {
2050                 return new BlockOfChildren<List<Location>>() {
2051                     public Under<List<Location>> startingAt( final int startingIndex ) {
2052                         return new Under<List<Location>>() {
2053                             public List<Location> under( String path ) {
2054                                 return under(Location.create(createPath(path)));
2055                             }
2056 
2057                             public List<Location> under( Path path ) {
2058                                 return under(Location.create(path));
2059                             }
2060 
2061                             public List<Location> under( Property idProperty ) {
2062                                 return under(Location.create(idProperty));
2063                             }
2064 
2065                             public List<Location> under( Property firstIdProperty,
2066                                                          Property... additionalIdProperties ) {
2067                                 return under(Location.create(firstIdProperty, additionalIdProperties));
2068                             }
2069 
2070                             public List<Location> under( UUID uuid ) {
2071                                 return under(Location.create(uuid));
2072                             }
2073 
2074                             public List<Location> under( Location at ) {
2075                                 return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
2076                                                .getChildren();
2077                             }
2078                         };
2079                     }
2080 
2081                     public List<Location> startingAfter( final Location previousSibling ) {
2082                         return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
2083                                        .getChildren();
2084                     }
2085 
2086                     public List<Location> startingAfter( String pathOfPreviousSibling ) {
2087                         return startingAfter(Location.create(createPath(pathOfPreviousSibling)));
2088                     }
2089 
2090                     public List<Location> startingAfter( Path pathOfPreviousSibling ) {
2091                         return startingAfter(Location.create(pathOfPreviousSibling));
2092                     }
2093 
2094                     public List<Location> startingAfter( UUID uuidOfPreviousSibling ) {
2095                         return startingAfter(Location.create(uuidOfPreviousSibling));
2096                     }
2097 
2098                     public List<Location> startingAfter( Property idPropertyOfPreviousSibling ) {
2099                         return startingAfter(Location.create(idPropertyOfPreviousSibling));
2100                     }
2101 
2102                     public List<Location> startingAfter( Property firstIdProperyOfPreviousSibling,
2103                                                          Property... additionalIdPropertiesOfPreviousSibling ) {
2104                         return startingAfter(Location.create(firstIdProperyOfPreviousSibling,
2105                                                              additionalIdPropertiesOfPreviousSibling));
2106                     }
2107                 };
2108             }
2109         };
2110     }
2111 
2112     /**
2113      * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
2114      * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
2115      * 
2116      * @param name the name of the property that is to be read
2117      * @return the object that is used to specified the node whose property is to be read, and which will return the property
2118      */
2119     public On<Property> getProperty( final String name ) {
2120         Name nameObj = getContext().getValueFactories().getNameFactory().create(name);
2121         return getProperty(nameObj);
2122     }
2123 
2124     /**
2125      * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
2126      * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
2127      * 
2128      * @param name the name of the property that is to be read
2129      * @return the object that is used to specified the node whose property is to be read, and which will return the property
2130      */
2131     public OnMultiple<Property> getProperty( final Name name ) {
2132         CheckArg.isNotNull(name, "name");
2133         return new OnMultiple<Property>() {
2134             public Property on( String path ) {
2135                 return on(Location.create(createPath(path)));
2136             }
2137 
2138             public Property on( Path path ) {
2139                 return on(Location.create(path));
2140             }
2141 
2142             public Property on( Property idProperty ) {
2143                 return on(Location.create(idProperty));
2144             }
2145 
2146             public Property on( Property firstIdProperty,
2147                                 Property... additionalIdProperties ) {
2148                 return on(Location.create(firstIdProperty, additionalIdProperties));
2149             }
2150 
2151             public Property on( Iterable<Property> idProperties ) {
2152                 return on(Location.create(idProperties));
2153             }
2154 
2155             public Property on( UUID uuid ) {
2156                 return on(Location.create(uuid));
2157             }
2158 
2159             public Property on( Location at ) {
2160                 return requests.readProperty(at, getCurrentWorkspaceName(), name).getProperty();
2161             }
2162 
2163             public Map<Location, Property> on( Collection<Location> locations ) {
2164                 CheckArg.isNotNull(locations, "locations");
2165                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2166                 String workspace = getCurrentWorkspaceName();
2167                 for (Location location : locations) {
2168                     requests.add(new ReadPropertyRequest(location, workspace, name));
2169                 }
2170                 return execute(requests);
2171             }
2172 
2173             public Map<Location, Property> on( Location first,
2174                                                Location... additional ) {
2175                 CheckArg.isNotNull(first, "first");
2176                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2177                 String workspace = getCurrentWorkspaceName();
2178                 requests.add(new ReadPropertyRequest(first, workspace, name));
2179                 for (Location location : additional) {
2180                     requests.add(new ReadPropertyRequest(location, workspace, name));
2181                 }
2182                 return execute(requests);
2183             }
2184 
2185             public Map<Location, Property> on( String first,
2186                                                String... additional ) {
2187                 CheckArg.isNotNull(first, "first");
2188                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2189                 String workspace = getCurrentWorkspaceName();
2190                 requests.add(new ReadPropertyRequest(Location.create(createPath(first)), workspace, name));
2191                 for (String path : additional) {
2192                     requests.add(new ReadPropertyRequest(Location.create(createPath(path)), workspace, name));
2193                 }
2194                 return execute(requests);
2195             }
2196 
2197             public Map<Location, Property> on( Path first,
2198                                                Path... additional ) {
2199                 CheckArg.isNotNull(first, "first");
2200                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2201                 String workspace = getCurrentWorkspaceName();
2202                 requests.add(new ReadPropertyRequest(Location.create(first), workspace, name));
2203                 for (Path path : additional) {
2204                     requests.add(new ReadPropertyRequest(Location.create(path), workspace, name));
2205                 }
2206                 return execute(requests);
2207             }
2208 
2209             public Map<Location, Property> on( UUID first,
2210                                                UUID... additional ) {
2211                 CheckArg.isNotNull(first, "first");
2212                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2213                 String workspace = getCurrentWorkspaceName();
2214                 requests.add(new ReadPropertyRequest(Location.create(first), workspace, name));
2215                 for (UUID uuid : additional) {
2216                     requests.add(new ReadPropertyRequest(Location.create(uuid), workspace, name));
2217                 }
2218                 return execute(requests);
2219             }
2220 
2221             protected Map<Location, Property> execute( List<ReadPropertyRequest> requests ) {
2222                 // Create a composite request ...
2223                 Request composite = CompositeRequest.with(requests);
2224                 Graph.this.execute(composite);
2225                 Map<Location, Property> results = new HashMap<Location, Property>();
2226                 for (ReadPropertyRequest request : requests) {
2227                     Property property = request.getProperty();
2228                     Location location = request.getActualLocationOfNode();
2229                     results.put(location, property);
2230                 }
2231                 return results;
2232             }
2233         };
2234     }
2235 
2236     /**
2237      * Request that the properties with the given names be read on the node defined via the <code>on(...)</code> method on the
2238      * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
2239      * 
2240      * @param names the name of the property that are to be read
2241      * @return the object that is used to specified the node whose properties are to be read, and which will return the map of
2242      *         properties keyed by their name; never null
2243      */
2244     public OnMultiple<Map<Name, Property>> getProperties( final Name... names ) {
2245         return new OnMultiple<Map<Name, Property>>() {
2246             public Map<Name, Property> on( String path ) {
2247                 return on(Location.create(createPath(path)));
2248             }
2249 
2250             public Map<Name, Property> on( Path path ) {
2251                 return on(Location.create(path));
2252             }
2253 
2254             public Map<Name, Property> on( Property idProperty ) {
2255                 return on(Location.create(idProperty));
2256             }
2257 
2258             public Map<Name, Property> on( Property firstIdProperty,
2259                                            Property... additionalIdProperties ) {
2260                 return on(Location.create(firstIdProperty, additionalIdProperties));
2261             }
2262 
2263             public Map<Name, Property> on( Iterable<Property> idProperties ) {
2264                 return on(Location.create(idProperties));
2265             }
2266 
2267             public Map<Name, Property> on( UUID uuid ) {
2268                 return on(Location.create(uuid));
2269             }
2270 
2271             public Map<Name, Property> on( Location at ) {
2272                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2273                 String workspace = getCurrentWorkspaceName();
2274                 for (Name propertyName : names) {
2275                     requests.add(new ReadPropertyRequest(at, workspace, propertyName));
2276                 }
2277                 // Create a composite request ...
2278                 Request composite = CompositeRequest.with(requests);
2279                 Graph.this.execute(composite);
2280                 Map<Name, Property> results = new HashMap<Name, Property>();
2281                 for (ReadPropertyRequest request : requests) {
2282                     Property property = request.getProperty();
2283                     results.put(property.getName(), property);
2284                 }
2285                 return results;
2286             }
2287 
2288             public Map<Location, Map<Name, Property>> on( Collection<Location> locations ) {
2289                 CheckArg.isNotNull(locations, "locations");
2290                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2291                 String workspace = getCurrentWorkspaceName();
2292                 for (Location location : locations) {
2293                     if (location == null) continue;
2294                     for (Name propertyName : names) {
2295                         if (propertyName == null) continue;
2296                         requests.add(new ReadPropertyRequest(location, workspace, propertyName));
2297                     }
2298                 }
2299                 return execute(requests);
2300             }
2301 
2302             /**
2303              * {@inheritDoc}
2304              * 
2305              * @see org.modeshape.graph.Graph.OnMultiple#on(org.modeshape.graph.Location, org.modeshape.graph.Location[])
2306              */
2307             public Map<Location, Map<Name, Property>> on( Location first,
2308                                                           Location... additional ) {
2309                 CheckArg.isNotNull(first, "first");
2310                 final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
2311                 String workspace = getCurrentWorkspaceName();
2312                 for (Location location : additional) {
2313                     if (location == null) continue;
2314                     for (Name propertyName : names) {
2315                         if (propertyName == null) continue;
2316                         requests.add(new ReadPropertyRequest(first, workspace, propertyName));
2317                         requests.add(new ReadPropertyRequest(location, workspace, propertyName));
2318                     }
2319                 }
2320                 return execute(requests);
2321             }
2322 
2323             /**
2324              * {@inheritDoc}
2325              * 
2326              * @see org.modeshape.graph.Graph.OnMultiple#on(org.modeshape.graph.property.Path,
2327              *      org.modeshape.graph.property.Path[])
2328              */
2329             public Map<Location, Map<Name, Property>> on( Path first,
2330                                                           Path... additional ) {
2331                 CheckArg.isNotNull(first, "first");
2332                 List<Location> locations = new LinkedList<Location>();
2333                 locations.add(Location.create(first));
2334                 for (Path path : additional) {
2335                     if (path != null) locations.add(Location.create(path));
2336                 }
2337                 return on(locations);
2338             }
2339 
2340             /**
2341              * {@inheritDoc}
2342              * 
2343              * @see org.modeshape.graph.Graph.OnMultiple#on(java.lang.String, java.lang.String[])
2344              */
2345             public Map<Location, Map<Name, Property>> on( String first,
2346                                                           String... additional ) {
2347                 CheckArg.isNotNull(first, "first");
2348                 List<Location> locations = new LinkedList<Location>();
2349                 locations.add(Location.create(createPath(first)));
2350                 for (String path : additional) {
2351                     if (path != null) locations.add(Location.create(createPath(path)));
2352                 }
2353                 return on(locations);
2354             }
2355 
2356             /**
2357              * {@inheritDoc}
2358              * 
2359              * @see org.modeshape.graph.Graph.OnMultiple#on(java.util.UUID, java.util.UUID[])
2360              */
2361             public Map<Location, Map<Name, Property>> on( UUID first,
2362                                                           UUID... additional ) {
2363                 CheckArg.isNotNull(first, "first");
2364                 List<Location> locations = new LinkedList<Location>();
2365                 locations.add(Location.create(first));
2366                 for (UUID uuid : additional) {
2367                     if (uuid != null) locations.add(Location.create(uuid));
2368                 }
2369                 return on(locations);
2370             }
2371 
2372             protected Map<Location, Map<Name, Property>> execute( List<ReadPropertyRequest> requests ) {
2373                 // Create a composite request ...
2374                 Request composite = CompositeRequest.with(requests);
2375                 Graph.this.execute(composite);
2376                 Map<Location, Map<Name, Property>> results = new HashMap<Location, Map<Name, Property>>();
2377                 for (ReadPropertyRequest request : requests) {
2378                     Property property = request.getProperty();
2379 
2380                     // property was requested but doesn't exist
2381                     if (property == null) continue;
2382 
2383                     Location location = request.getActualLocationOfNode();
2384                     Map<Name, Property> properties = results.get(location);
2385                     if (properties == null) {
2386                         properties = new HashMap<Name, Property>();
2387                         results.put(location, properties);
2388                     }
2389                     properties.put(property.getName(), property);
2390                 }
2391                 return results;
2392             }
2393         };
2394     }
2395 
2396     /**
2397      * Request to read the node with the supplied UUID.
2398      * 
2399      * @param uuid the UUID of the node that is to be read
2400      * @return the node that is read from the repository
2401      */
2402     public Node getNodeAt( UUID uuid ) {
2403         return getNodeAt(Location.create(uuid));
2404     }
2405 
2406     /**
2407      * Request to read the node at the supplied location.
2408      * 
2409      * @param location the location of the node that is to be read
2410      * @return the node that is read from the repository
2411      */
2412     public Node getNodeAt( Location location ) {
2413         return new GraphNode(requests.readNode(location, getCurrentWorkspaceName()));
2414     }
2415 
2416     /**
2417      * Request to read the node at the supplied path.
2418      * 
2419      * @param path the path of the node that is to be read
2420      * @return the node that is read from the repository
2421      */
2422     public Node getNodeAt( String path ) {
2423         return getNodeAt(Location.create(createPath(path)));
2424     }
2425 
2426     /**
2427      * Request to read the node at the supplied path.
2428      * 
2429      * @param path the path of the node that is to be read
2430      * @return the node that is read from the repository
2431      */
2432     public Node getNodeAt( Path path ) {
2433         return getNodeAt(Location.create(path));
2434     }
2435 
2436     /**
2437      * Request to read the node with the supplied unique identifier property.
2438      * 
2439      * @param idProperty the identification property that is unique to the node that is to be read
2440      * @return the node that is read from the repository
2441      */
2442     public Node getNodeAt( Property idProperty ) {
2443         return getNodeAt(Location.create(idProperty));
2444     }
2445 
2446     /**
2447      * Request to read the node with the supplied unique identifier properties.
2448      * 
2449      * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
2450      * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be read
2451      * @return the node that is read from the repository
2452      */
2453     public Node getNodeAt( Property firstIdProperty,
2454                            Property... additionalIdProperties ) {
2455         return getNodeAt(Location.create(firstIdProperty, additionalIdProperties));
2456     }
2457 
2458     /**
2459      * Request to read the node with the supplied unique identifier properties.
2460      * 
2461      * @param idProperties the identification properties that uniquely identify the node that is to be read
2462      * @return the node that is read from the repository
2463      */
2464     public Node getNodeAt( Iterable<Property> idProperties ) {
2465         return getNodeAt(Location.create(idProperties));
2466     }
2467 
2468     /**
2469      * Request to read the node given by the supplied reference value.
2470      * 
2471      * @param reference the reference property value that is to be resolved into a node
2472      * @return the node that is read from the repository
2473      * @throws ValueFormatException if the supplied reference could not be converted to an identifier property value
2474      */
2475     public Node resolve( Reference reference ) {
2476         CheckArg.isNotNull(reference, "reference");
2477         UUID uuid = getContext().getValueFactories().getUuidFactory().create(reference);
2478         return getNodeAt(uuid);
2479     }
2480 
2481     /**
2482      * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code> in
2483      * the resulting {@link At} object. All properties and children of every node in the subgraph will be read and returned in the
2484      * {@link Subgraph} object returned from the <code>at(...)</code> methods.
2485      * 
2486      * @param depth the maximum depth of the subgraph that should be read
2487      * @return the component that should be used to specify the location of the node that is the top of the subgraph, and which
2488      *         will return the {@link Subgraph} containing the results
2489      */
2490     public At<Subgraph> getSubgraphOfDepth( final int depth ) {
2491         return new At<Subgraph>() {
2492             public Subgraph at( Location location ) {
2493                 return new SubgraphResults(requests.readBranch(location, getCurrentWorkspaceName(), depth));
2494             }
2495 
2496             public Subgraph at( String path ) {
2497                 return at(Location.create(createPath(path)));
2498             }
2499 
2500             public Subgraph at( Path path ) {
2501                 return at(Location.create(path));
2502             }
2503 
2504             public Subgraph at( UUID uuid ) {
2505                 return at(Location.create(uuid));
2506             }
2507 
2508             public Subgraph at( Property idProperty ) {
2509                 return at(Location.create(idProperty));
2510             }
2511 
2512             public Subgraph at( Property firstIdProperty,
2513                                 Property... additionalIdProperties ) {
2514                 return at(Location.create(firstIdProperty, additionalIdProperties));
2515             }
2516 
2517             public Subgraph at( Iterable<Property> idProperties ) {
2518                 return at(Location.create(idProperties));
2519             }
2520         };
2521     }
2522 
2523     /**
2524      * Search the current workspace using the supplied full-text search expression.
2525      * 
2526      * @param fullTextSearchExpression the full-text search expression
2527      * @param maxResults the maximum number of results that are to be returned; always positive
2528      * @param offset the number of initial results to skip, or 0 if the first results are to be returned
2529      * @return the results of the search; never null
2530      * @throws IllegalArgumentException if the expression is null
2531      */
2532     public QueryResults search( final String fullTextSearchExpression,
2533                                 int maxResults,
2534                                 int offset ) {
2535         FullTextSearchRequest request = requests.search(getCurrentWorkspaceName(), fullTextSearchExpression, maxResults, offset);
2536         QueryResults results = new org.modeshape.graph.query.process.QueryResults(request.getResultColumns(),
2537                                                                                   request.getStatistics(), request.getTuples());
2538         return results;
2539     }
2540 
2541     /**
2542      * Query the current workspace using the supplied {@link Schemata}.
2543      * 
2544      * @param query the query that is to be executed against the current workspace
2545      * @param schemata the schemata defining the structure of the tables that are being queried
2546      * @return the interface used to continue specifying the options for the query and to obtain the results
2547      * @throws IllegalArgumentException if the query or schemata references are null
2548      */
2549     public BuildQuery query( final QueryCommand query,
2550                              final Schemata schemata ) {
2551         CheckArg.isNotNull(query, "query");
2552         CheckArg.isNotNull(schemata, "schemata");
2553         return new BuildQuery() {
2554             private PlanHints hints;
2555             private Problems problems;
2556             private Map<String, Object> variables;
2557 
2558             /**
2559              * {@inheritDoc}
2560              * 
2561              * @see org.modeshape.graph.Graph.BuildQuery#using(java.util.Map)
2562              */
2563             public BuildQuery using( Map<String, Object> variables ) {
2564                 CheckArg.isNotNull(variables, "variables");
2565                 if (this.variables == null) this.variables = new HashMap<String, Object>();
2566                 this.variables.putAll(variables);
2567                 return this;
2568             }
2569 
2570             /**
2571              * {@inheritDoc}
2572              * 
2573              * @see org.modeshape.graph.Graph.BuildQuery#using(java.lang.String, java.lang.Object)
2574              */
2575             public BuildQuery using( String variableName,
2576                                      Object variableValue ) {
2577                 CheckArg.isNotNull(variableName, "variableName");
2578                 if (this.variables == null) this.variables = new HashMap<String, Object>();
2579                 this.variables.put(variableName, variableValue);
2580                 return this;
2581             }
2582 
2583             /**
2584              * {@inheritDoc}
2585              * 
2586              * @see org.modeshape.graph.Graph.BuildQuery#using(org.modeshape.graph.query.plan.PlanHints)
2587              */
2588             public BuildQuery using( PlanHints hints ) {
2589                 this.hints = hints;
2590                 return this;
2591             }
2592 
2593             /**
2594              * {@inheritDoc}
2595              * 
2596              * @see org.modeshape.graph.Graph.BuildQuery#execute()
2597              */
2598             public QueryResults execute() {
2599                 Batch batch = batch();
2600                 TypeSystem typeSystem = getContext().getValueFactories().getTypeSystem();
2601                 QueryContext context = new GraphQueryContext(schemata, typeSystem, hints, problems, variables, batch);
2602                 QueryEngine engine = getQueryEngine();
2603                 return engine.execute(context, query);
2604             }
2605         };
2606     }
2607 
2608     protected QueryEngine getQueryEngine() {
2609         if (queryEngine == null) {
2610             queryEngine = getQueryEngine(new RuleBasedOptimizer());
2611         }
2612         return queryEngine;
2613     }
2614 
2615     protected QueryEngine getQueryEngine( Optimizer optimizer ) {
2616         Planner planner = new CanonicalPlanner();
2617         Processor processor = new QueryProcessor() {
2618             /**
2619              * {@inheritDoc}
2620              * 
2621              * @see org.modeshape.graph.query.process.QueryProcessor#createAccessComponent(org.modeshape.graph.query.model.QueryCommand,
2622              *      org.modeshape.graph.query.QueryContext, org.modeshape.graph.query.plan.PlanNode,
2623              *      org.modeshape.graph.query.QueryResults.Columns, org.modeshape.graph.query.process.SelectComponent.Analyzer)
2624              */
2625             @Override
2626             protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
2627                                                                  QueryContext context,
2628                                                                  PlanNode accessNode,
2629                                                                  Columns resultColumns,
2630                                                                  Analyzer analyzer ) {
2631                 return new AccessQueryProcessor(getSourceName(), getCurrentWorkspaceName(), context, resultColumns, accessNode);
2632             }
2633 
2634             /**
2635              * {@inheritDoc}
2636              * 
2637              * @see org.modeshape.graph.query.process.QueryProcessor#preExecute(QueryContext)
2638              */
2639             @Override
2640             protected void preExecute( QueryContext context ) {
2641                 // Submit the batch before the processing the query. No need to hold onto the batch results,
2642                 // because each ProcessingComponent holds onto its AccessQueryRequest ...
2643                 ((GraphQueryContext)context).getBatch().execute();
2644             }
2645         };
2646         return new QueryEngine(planner, optimizer, processor);
2647     }
2648 
2649     protected class GraphQueryContext extends QueryContext {
2650         private final Batch batch;
2651 
2652         protected GraphQueryContext( Schemata schemata,
2653                                      TypeSystem typeSystem,
2654                                      PlanHints hints,
2655                                      Problems problems,
2656                                      Map<String, Object> variables,
2657                                      Batch batch ) {
2658             super(schemata, typeSystem, hints, problems, variables);
2659             this.batch = batch;
2660             assert this.batch != null;
2661         }
2662 
2663         /**
2664          * Get the {@link Batch} that is being used to execute these queries
2665          * 
2666          * @return the batch; never null
2667          */
2668         public Batch getBatch() {
2669             return batch;
2670         }
2671     }
2672 
2673     protected static class AccessQueryProcessor extends AbstractAccessComponent {
2674         private final AccessQueryRequest accessRequest;
2675         private final String graphSourceName;
2676 
2677         protected AccessQueryProcessor( String graphSourceName,
2678                                         String workspaceName,
2679                                         QueryContext context,
2680                                         Columns columns,
2681                                         PlanNode accessNode ) {
2682             super(context, columns, accessNode);
2683             this.graphSourceName = graphSourceName;
2684             accessRequest = new AccessQueryRequest(workspaceName, sourceName, getColumns(), andedConstraints, limit,
2685                                                    context.getSchemata(), context.getVariables());
2686             ((GraphQueryContext)context).getBatch().requestQueue.submit(accessRequest);
2687         }
2688 
2689         /**
2690          * Get the access query request.
2691          * 
2692          * @return the access query request; never null
2693          */
2694         public AccessQueryRequest getAccessRequest() {
2695             return accessRequest;
2696         }
2697 
2698         /**
2699          * {@inheritDoc}
2700          * 
2701          * @see org.modeshape.graph.query.process.ProcessingComponent#execute()
2702          */
2703         @Override
2704         public List<Object[]> execute() {
2705             if (accessRequest.getError() != null) {
2706                 I18n msg = GraphI18n.errorWhilePerformingQuery;
2707                 getContext().getProblems().addError(accessRequest.getError(),
2708                                                     msg,
2709                                                     accessNode.getString(),
2710                                                     accessRequest.workspace(),
2711                                                     graphSourceName,
2712                                                     accessRequest.getError().getLocalizedMessage());
2713                 return emptyTuples();
2714             }
2715             return accessRequest.getTuples();
2716         }
2717 
2718     }
2719 
2720     /**
2721      * Import the content from the provided stream of XML data, specifying via the returned {@link ImportInto object} where the
2722      * content is to be imported.
2723      * 
2724      * @param stream the open stream of XML data that the importer can read the content that is to be imported
2725      * @return the object that should be used to specify into which the content is to be imported
2726      * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
2727      */
2728     public ImportInto<Conjunction<Graph>> importXmlFrom( final InputStream stream ) {
2729         CheckArg.isNotNull(stream, "stream");
2730 
2731         return new ImportInto<Conjunction<Graph>>() {
2732             private boolean skipRootElement = false;
2733             private Name nameAttribute = JcrLexicon.NAME;
2734             private Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
2735 
2736             public ImportInto<Conjunction<Graph>> skippingRootElement( boolean skipRootElement ) {
2737                 this.skipRootElement = skipRootElement;
2738                 return this;
2739             }
2740 
2741             public ImportInto<Conjunction<Graph>> usingAttributeForName( String nameAttribute ) {
2742                 if (nameAttribute != null) this.nameAttribute = createName(nameAttribute);
2743                 return this;
2744             }
2745 
2746             public ImportInto<Conjunction<Graph>> usingAttributeForName( Name nameAttribute ) {
2747                 if (nameAttribute != null) this.nameAttribute = nameAttribute;
2748                 return this;
2749             }
2750 
2751             public ImportInto<Conjunction<Graph>> usingAttributeForType( String typeAttribute ) {
2752                 if (typeAttribute != null) this.typeAttribute = createName(typeAttribute);
2753                 return this;
2754             }
2755 
2756             public ImportInto<Conjunction<Graph>> usingAttributeForType( Name typeAttribute ) {
2757                 if (typeAttribute != null) this.typeAttribute = typeAttribute;
2758                 return this;
2759             }
2760 
2761             public Conjunction<Graph> into( String path ) throws IOException, SAXException {
2762                 return into(Location.create(createPath(path)));
2763             }
2764 
2765             public Conjunction<Graph> into( Path path ) throws IOException, SAXException {
2766                 return into(Location.create(path));
2767             }
2768 
2769             public Conjunction<Graph> into( Property idProperty ) throws IOException, SAXException {
2770                 return into(Location.create(idProperty));
2771             }
2772 
2773             public Conjunction<Graph> into( Property firstIdProperty,
2774                                             Property... additionalIdProperties ) throws IOException, SAXException {
2775                 return into(Location.create(firstIdProperty, additionalIdProperties));
2776             }
2777 
2778             public Conjunction<Graph> into( Iterable<Property> idProperties ) throws IOException, SAXException {
2779                 return into(Location.create(idProperties));
2780             }
2781 
2782             public Conjunction<Graph> into( UUID uuid ) throws IOException, SAXException {
2783                 return into(Location.create(uuid));
2784             }
2785 
2786             public Conjunction<Graph> into( Location at ) throws IOException, SAXException {
2787                 final Name nameAttribute = this.nameAttribute;
2788                 final Name typeAttribute = this.typeAttribute;
2789                 GraphImporter importer = new GraphImporter(Graph.this) {
2790                     @Override
2791                     protected Name getNameAttribute() {
2792                         return nameAttribute;
2793                     }
2794 
2795                     @Override
2796                     protected Name getTypeAttribute() {
2797                         return typeAttribute;
2798                     }
2799                 };
2800                 importer.importXml(stream, at, skipRootElement).execute(); // 'importXml' creates and uses a new batch
2801                 return Graph.this.nextGraph;
2802             }
2803         };
2804     }
2805 
2806     /**
2807      * Import the content from the XML file at the supplied URI, specifying via the returned {@link ImportInto object} where the
2808      * content is to be imported.
2809      * 
2810      * @param uri the URI where the importer can read the content that is to be imported
2811      * @return the object that should be used to specify into which the content is to be imported
2812      * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
2813      */
2814     public ImportInto<Conjunction<Graph>> importXmlFrom( final URI uri ) {
2815         return new ImportInto<Conjunction<Graph>>() {
2816             private boolean skipRootElement = false;
2817             private Name nameAttribute = JcrLexicon.NAME;
2818             private Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
2819 
2820             public ImportInto<Conjunction<Graph>> skippingRootElement( boolean skipRootElement ) {
2821                 this.skipRootElement = skipRootElement;
2822                 return this;
2823             }
2824 
2825             public ImportInto<Conjunction<Graph>> usingAttributeForName( String nameAttribute ) {
2826                 if (nameAttribute != null) this.nameAttribute = createName(nameAttribute);
2827                 return this;
2828             }
2829 
2830             public ImportInto<Conjunction<Graph>> usingAttributeForName( Name nameAttribute ) {
2831                 if (nameAttribute != null) this.nameAttribute = nameAttribute;
2832                 return this;
2833             }
2834 
2835             public ImportInto<Conjunction<Graph>> usingAttributeForType( String typeAttribute ) {
2836                 if (typeAttribute != null) this.typeAttribute = createName(typeAttribute);
2837                 return this;
2838             }
2839 
2840             public ImportInto<Conjunction<Graph>> usingAttributeForType( Name typeAttribute ) {
2841                 if (typeAttribute != null) this.typeAttribute = typeAttribute;
2842                 return this;
2843             }
2844 
2845             public Conjunction<Graph> into( String path ) throws IOException, SAXException {
2846                 return into(Location.create(createPath(path)));
2847             }
2848 
2849             public Conjunction<Graph> into( Path path ) throws IOException, SAXException {
2850                 return into(Location.create(path));
2851             }
2852 
2853             public Conjunction<Graph> into( Property idProperty ) throws IOException, SAXException {
2854                 return into(Location.create(idProperty));
2855             }
2856 
2857             public Conjunction<Graph> into( Property firstIdProperty,
2858                                             Property... additionalIdProperties ) throws IOException, SAXException {
2859                 return into(Location.create(firstIdProperty, additionalIdProperties));
2860             }
2861 
2862             public Conjunction<Graph> into( Iterable<Property> idProperties ) throws IOException, SAXException {
2863                 return into(Location.create(idProperties));
2864             }
2865 
2866             public Conjunction<Graph> into( UUID uuid ) throws IOException, SAXException {
2867                 return into(Location.create(uuid));
2868             }
2869 
2870             public Conjunction<Graph> into( Location at ) throws IOException, SAXException {
2871                 final Name nameAttribute = this.nameAttribute;
2872                 final Name typeAttribute = this.typeAttribute;
2873                 GraphImporter importer = new GraphImporter(Graph.this) {
2874                     @Override
2875                     protected Name getNameAttribute() {
2876                         return nameAttribute;
2877                     }
2878 
2879                     @Override
2880                     protected Name getTypeAttribute() {
2881                         return typeAttribute;
2882                     }
2883                 };
2884                 importer.importXml(uri, at, skipRootElement).execute(); // 'importXml' creates and uses a new batch
2885                 return Graph.this.nextGraph;
2886             }
2887         };
2888     }
2889 
2890     /**
2891      * Import the content from the XML file at the supplied file location, specifying via the returned {@link ImportInto object}
2892      * where the content is to be imported.
2893      * 
2894      * @param pathToFile the path to the XML file that should be imported.
2895      * @return the object that should be used to specify into which the content is to be imported
2896      * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
2897      */
2898     public ImportInto<Conjunction<Graph>> importXmlFrom( String pathToFile ) {
2899         CheckArg.isNotEmpty(pathToFile, "pathToFile");
2900         // Try the file name ...
2901         File file = new File(pathToFile);
2902         if (file.exists() && file.canRead()) {
2903             return importXmlFrom(new File(pathToFile).toURI());
2904         }
2905         // See if there is a resource on the classpath ...
2906         ClassLoader classLoader = getClass().getClassLoader();
2907         InputStream stream = classLoader.getResourceAsStream(pathToFile);
2908         if (stream != null) {
2909             try {
2910                 stream.close();
2911                 return importXmlFrom(pathToFile, classLoader);
2912             } catch (IOException e) {
2913                 // shouldn't happen, but continue anyway ...
2914             }
2915         }
2916         // Try a URL ...
2917         try {
2918             return importXmlFrom(new URI(pathToFile));
2919         } catch (URISyntaxException e) {
2920             // Must not be a URI ...
2921         }
2922         // Try the resource on the classpath anyway ...
2923         return importXmlFrom(pathToFile, getClass().getClassLoader());
2924     }
2925 
2926     /**
2927      * Import the content from the XML file at the supplied file location, specifying via the returned {@link ImportInto object}
2928      * where the content is to be imported.
2929      * 
2930      * @param resourceName the name of the resource file on the classpath
2931      * @param classLoader the classloader that should be used to load the resource; may be null if the class' current classloader
2932      *        should be used
2933      * @return the object that should be used to specify into which the content is to be imported
2934      * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
2935      */
2936     public ImportInto<Conjunction<Graph>> importXmlFrom( String resourceName,
2937                                                          ClassLoader classLoader ) {
2938         CheckArg.isNotEmpty(resourceName, "resourceName");
2939         // Try the resource on the classpath ...
2940         if (classLoader == null) classLoader = getClass().getClassLoader();
2941         InputStream stream = null;
2942         RuntimeException error = null;
2943         try {
2944             stream = getClass().getResourceAsStream(resourceName);
2945             return importXmlFrom(stream);
2946         } catch (RuntimeException e) {
2947             error = e;
2948             throw e;
2949         } finally {
2950             if (stream != null) {
2951                 try {
2952                     stream.close();
2953                 } catch (Throwable e2) {
2954                     if (error == null) {
2955                         throw new RuntimeException(e2);
2956                     }
2957                 }
2958             }
2959         }
2960     }
2961 
2962     /**
2963      * Import the content from the XML file at the supplied file, specifying via the returned {@link ImportInto object} where the
2964      * content is to be imported.
2965      * 
2966      * @param file the XML file that should be imported.
2967      * @return the object that should be used to specify into which the content is to be imported
2968      * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
2969      */
2970     public ImportInto<Conjunction<Graph>> importXmlFrom( File file ) {
2971         CheckArg.isNotNull(file, "file");
2972         return importXmlFrom(file.toURI());
2973     }
2974 
2975     /**
2976      * Merge the content in the supplied graph with the content in this graph, ensuring that all of the content in the supplied
2977      * graph exists in this graph. Note that any extra content is this graph but not in the supplied graph will not be affected.
2978      * 
2979      * @param otherContent the content that should exist within this graph or, if it does not exist, added to this graph
2980      * @return the interface for additional requests or actions
2981      */
2982     public Conjunction<Graph> merge( Graph otherContent ) {
2983         GraphMerger initializer = new GraphMerger(otherContent);
2984         Batch batch = batch();
2985         initializer.merge(this, batch);
2986         batch.execute();
2987         return new Conjunction<Graph>() {
2988             /**
2989              * {@inheritDoc}
2990              * 
2991              * @see org.modeshape.graph.Graph.Conjunction#and()
2992              */
2993             public Graph and() {
2994                 return Graph.this;
2995             }
2996         };
2997     }
2998 
2999     protected Path createPath( String path ) {
3000         return getContext().getValueFactories().getPathFactory().create(path);
3001     }
3002 
3003     protected Name createName( String name ) {
3004         return getContext().getValueFactories().getNameFactory().create(name);
3005     }
3006 
3007     protected List<Segment> getSegments( List<Location> locations ) {
3008         List<Segment> segments = new ArrayList<Segment>(locations.size());
3009         for (Location location : locations) {
3010             segments.add(location.getPath().getLastSegment());
3011         }
3012         return segments;
3013     }
3014 
3015     /**
3016      * Begin a batch of requests to perform various operations. Use this approach when multiple operations are to be built and
3017      * then executed with one submission to the underlying {@link #getSourceName() repository source}. The {@link Results results}
3018      * are not available until the {@link Batch#execute()} method is invoked.
3019      * 
3020      * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
3021      * @see Batch#execute()
3022      * @see Results
3023      */
3024     public Batch batch() {
3025         return new Batch(new BatchRequestBuilder());
3026     }
3027 
3028     /**
3029      * Begin a batch of requests to perform various operations, but specify the queue where all accumulated requests should be
3030      * placed. Use this approach when multiple operations are to be built and then executed with one submission to the underlying
3031      * {@link #getSourceName() repository source}. The {@link Results results} are not available until the {@link Batch#execute()}
3032      * method is invoked.
3033      * 
3034      * @param builder the request builder that should be used; may not be null
3035      * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
3036      * @see Batch#execute()
3037      * @see Results
3038      */
3039     public Batch batch( BatchRequestBuilder builder ) {
3040         CheckArg.isNotNull(builder, "builder");
3041         return new Batch(builder);
3042     }
3043 
3044     /**
3045      * Interface for creating multiple requests to perform various operations. Note that all the requests are accumulated until
3046      * the {@link #execute()} method is called. The results of all the operations are then available in the {@link Results} object
3047      * returned by the {@link #execute()}.
3048      */
3049     @Immutable
3050     public final class Batch implements Executable<Node> {
3051         protected final BatchRequestBuilder requestQueue;
3052         protected final BatchConjunction nextRequests;
3053         protected final String workspaceName;
3054         protected boolean executed = false;
3055 
3056         /*package*/Batch( BatchRequestBuilder builder ) {
3057             assert builder != null;
3058             this.requestQueue = builder;
3059             this.workspaceName = Graph.this.getCurrentWorkspaceName();
3060             this.nextRequests = new BatchConjunction() {
3061                 public Batch and() {
3062                     return Batch.this;
3063                 }
3064 
3065                 public Results execute() {
3066                     return Batch.this.execute();
3067                 }
3068             };
3069         }
3070 
3071         /**
3072          * Return whether this batch has been {@link #execute() executed}.
3073          * 
3074          * @return true if this batch has already been executed, or false otherwise
3075          */
3076         public boolean hasExecuted() {
3077             return executed;
3078         }
3079 
3080         /**
3081          * Determine whether this batch needs to be executed (there are requests and the batch has not been executed yet).
3082          * 
3083          * @return true if there are some requests in this batch that need to be executed, or false execution is not required
3084          */
3085         public boolean isExecuteRequired() {
3086             return !executed && requestQueue.hasRequests();
3087         }
3088 
3089         /**
3090          * Obtain the graph that this batch uses.
3091          * 
3092          * @return the graph; never null
3093          */
3094         public Graph getGraph() {
3095             return Graph.this;
3096         }
3097 
3098         /**
3099          * Get the name of the workspace that this batch is using. This is always constant throughout the lifetime of the batch.
3100          * 
3101          * @return the name of the workspace; never null
3102          */
3103         public String getCurrentWorkspaceName() {
3104             return this.workspaceName;
3105         }
3106 
3107         protected final void assertNotExecuted() {
3108             if (executed) {
3109                 throw new IllegalStateException(GraphI18n.unableToAddMoreRequestsToAlreadyExecutedBatch.text());
3110             }
3111         }
3112 
3113         /**
3114          * Begin the request to move the specified node into a parent node at a different location, which is specified via the
3115          * <code>into(...)</code> method on the returned {@link Move} object.
3116          * <p>
3117          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3118          * called.
3119          * </p>
3120          * 
3121          * @param from the node that is to be moved.
3122          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3123          *         to be moved
3124          */
3125         public Move<BatchConjunction> move( Node from ) {
3126             return move(from.getLocation());
3127         }
3128 
3129         /**
3130          * Begin the request to move a node at the specified location into a parent node at a different location, which is
3131          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3132          * <p>
3133          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3134          * called.
3135          * </p>
3136          * 
3137          * @param from the location of the node that is to be moved.
3138          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3139          *         to be moved
3140          */
3141         public final Move<BatchConjunction> move( Location from ) {
3142             assertNotExecuted();
3143             return new MoveAction<BatchConjunction>(this.nextRequests, from) {
3144                 @Override
3145                 protected BatchConjunction submit( Locations from,
3146                                                    Location into,
3147                                                    Location before,
3148                                                    Name newName ) {
3149                     String workspaceName = getCurrentWorkspaceName();
3150                     do {
3151                         requestQueue.moveBranch(from.getLocation(), into, before, workspaceName, newName);
3152                     } while ((from = from.next()) != null);
3153                     return and();
3154                 }
3155             };
3156         }
3157 
3158         /**
3159          * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
3160          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3161          * <p>
3162          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3163          * called.
3164          * </p>
3165          * 
3166          * @param fromPath the path to the node that is to be moved.
3167          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3168          *         to be moved
3169          */
3170         public Move<BatchConjunction> move( String fromPath ) {
3171             return move(Location.create(createPath(fromPath)));
3172         }
3173 
3174         /**
3175          * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
3176          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3177          * <p>
3178          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3179          * called.
3180          * </p>
3181          * 
3182          * @param from the path to the node that is to be moved.
3183          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3184          *         to be moved
3185          */
3186         public Move<BatchConjunction> move( Path from ) {
3187             return move(Location.create(from));
3188         }
3189 
3190         /**
3191          * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which
3192          * is specified via the <code>into(...)</code> method on the returned {@link Move} object.
3193          * <p>
3194          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3195          * called.
3196          * </p>
3197          * 
3198          * @param from the UUID of the node that is to be moved.
3199          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3200          *         to be moved
3201          */
3202         public Move<BatchConjunction> move( UUID from ) {
3203             return move(Location.create(from));
3204         }
3205 
3206         /**
3207          * Begin the request to move a node with the specified unique identification property into a parent node at a different
3208          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3209          * identification property should uniquely identify a single node.
3210          * <p>
3211          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3212          * called.
3213          * </p>
3214          * 
3215          * @param idProperty the unique identification property of the node that is to be moved.
3216          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3217          *         to be moved
3218          */
3219         public Move<BatchConjunction> move( Property idProperty ) {
3220             return move(Location.create(idProperty));
3221         }
3222 
3223         /**
3224          * Begin the request to move a node with the specified identification properties into a parent node at a different
3225          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3226          * identification properties should uniquely identify a single node.
3227          * <p>
3228          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3229          * called.
3230          * </p>
3231          * 
3232          * @param firstIdProperty the first identification property of the node that is to be moved
3233          * @param additionalIdProperties the remaining identification properties of the node that is to be moved
3234          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3235          *         to be moved
3236          */
3237         public Move<BatchConjunction> move( Property firstIdProperty,
3238                                             Property... additionalIdProperties ) {
3239             return move(Location.create(firstIdProperty, additionalIdProperties));
3240         }
3241 
3242         /**
3243          * Begin the request to move a node with the specified identification properties into a parent node at a different
3244          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3245          * identification properties should uniquely identify a single node.
3246          * <p>
3247          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3248          * called.
3249          * </p>
3250          * 
3251          * @param idProperties the identification properties of the node that is to be moved
3252          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3253          *         to be moved
3254          */
3255         public Move<BatchConjunction> move( Iterable<Property> idProperties ) {
3256             return move(Location.create(idProperties));
3257         }
3258 
3259         /**
3260          * Request to lock the specified node. This request is submitted to the repository immediately.
3261          * 
3262          * @param at the node that is to be locked
3263          * @return an object that allows the scope of the lock to be defined
3264          */
3265         public LockScope<LockTimeout<BatchConjunction>> lock( Node at ) {
3266             return lock(at.getLocation());
3267         }
3268 
3269         /**
3270          * Request to lock the node at the given path. This request is submitted to the repository immediately.
3271          * 
3272          * @param atPath the path of the node that is to be locked
3273          * @return an object that allows the scope of the lock to be defined
3274          */
3275         public LockScope<LockTimeout<BatchConjunction>> lock( String atPath ) {
3276             return lock(Location.create(createPath(atPath)));
3277         }
3278 
3279         /**
3280          * Request to lock the node at the given path. This request is submitted to the repository immediately.
3281          * 
3282          * @param at the path of the node that is to be locked
3283          * @return an object that allows the scope of the lock to be defined
3284          */
3285         public LockScope<LockTimeout<BatchConjunction>> lock( Path at ) {
3286             return lock(Location.create(at));
3287         }
3288 
3289         /**
3290          * Request to lock the node with the given UUID. This request is submitted to the repository immediately.
3291          * 
3292          * @param at the UUID of the node that is to be locked
3293          * @return an object that allows the scope of the lock to be defined
3294          */
3295         public LockScope<LockTimeout<BatchConjunction>> lock( UUID at ) {
3296             return lock(Location.create(at));
3297         }
3298 
3299         /**
3300          * Request to lock the node with the given unique identification property. This request is submitted to the repository
3301          * immediately.
3302          * 
3303          * @param idProperty the unique identifying property of the node that is to be locked
3304          * @return an object that allows the scope of the lock to be defined
3305          */
3306         public LockScope<LockTimeout<BatchConjunction>> lock( Property idProperty ) {
3307             return lock(Location.create(idProperty));
3308         }
3309 
3310         /**
3311          * Request to lock the node with the given identification properties. The identification properties should uniquely
3312          * identify a single node. This request is submitted to the repository immediately.
3313          * 
3314          * @param firstIdProperty the first identification property of the node that is to be copied
3315          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3316          * @return an object that allows the scope of the lock to be defined
3317          */
3318         public LockScope<LockTimeout<BatchConjunction>> lock( Property firstIdProperty,
3319                                                               Property... additionalIdProperties ) {
3320             return lock(Location.create(firstIdProperty, additionalIdProperties));
3321         }
3322 
3323         /**
3324          * Request to lock the node at the given location. This request is submitted to the repository immediately.
3325          * 
3326          * @param at the location of the node that is to be locked
3327          * @return an object that allows the scope of the lock to be defined
3328          */
3329         public LockScope<LockTimeout<BatchConjunction>> lock( Location at ) {
3330             return new LockAction<BatchConjunction>(this.nextRequests, at) {
3331                 @Override
3332                 protected BatchConjunction submit( Location target,
3333                                                    org.modeshape.graph.request.LockBranchRequest.LockScope lockScope,
3334                                                    long lockTimeoutInMillis ) {
3335                     String workspaceName = getCurrentWorkspaceName();
3336                     requests.lockBranch(workspaceName, target, lockScope, lockTimeoutInMillis);
3337                     return and();
3338                 }
3339             };
3340         }
3341 
3342         /**
3343          * Request to unlock the specified node. This request is submitted to the repository immediately.
3344          * 
3345          * @param at the node that is to be unlocked
3346          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3347          */
3348         public BatchConjunction unlock( Node at ) {
3349             return unlock(at.getLocation());
3350         }
3351 
3352         /**
3353          * Request to unlock the node at the given path. This request is submitted to the repository immediately.
3354          * 
3355          * @param atPath the path of the node that is to be unlocked
3356          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3357          */
3358         public BatchConjunction unlock( String atPath ) {
3359             return unlock(Location.create(createPath(atPath)));
3360         }
3361 
3362         /**
3363          * Request to unlock the node at the given path. This request is submitted to the repository immediately.
3364          * 
3365          * @param at the path of the node that is to be unlocked
3366          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3367          */
3368         public BatchConjunction unlock( Path at ) {
3369             return unlock(Location.create(at));
3370         }
3371 
3372         /**
3373          * Request to unlock the node with the given UUID. This request is submitted to the repository immediately.
3374          * 
3375          * @param at the UUID of the node that is to be unlocked
3376          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3377          */
3378         public BatchConjunction unlock( UUID at ) {
3379             return unlock(Location.create(at));
3380         }
3381 
3382         /**
3383          * Request to unlock the node with the given unique identification property. This request is submitted to the repository
3384          * immediately.
3385          * 
3386          * @param idProperty the unique identifying property of the node that is to be unlocked
3387          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3388          */
3389         public BatchConjunction unlock( Property idProperty ) {
3390             return unlock(Location.create(idProperty));
3391         }
3392 
3393         /**
3394          * Request to unlock the node with the given identification properties. The identification properties should uniquely
3395          * identify a single node. This request is submitted to the repository immediately.
3396          * 
3397          * @param firstIdProperty the first identification property of the node that is to be copied
3398          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3399          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3400          */
3401         public BatchConjunction unlock( Property firstIdProperty,
3402                                         Property... additionalIdProperties ) {
3403             return unlock(Location.create(firstIdProperty, additionalIdProperties));
3404         }
3405 
3406         /**
3407          * Request to unlock the node at the given location. This request is submitted to the repository immediately.
3408          * 
3409          * @param at the location of the node that is to be unlocked
3410          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3411          */
3412         public BatchConjunction unlock( Location at ) {
3413             requests.unlockBranch(workspaceName, at);
3414             return this.nextRequests;
3415         }
3416 
3417         /**
3418          * Begin the request to clone the specified node into a parent node at a different location, which is specified via the
3419          * <code>into(...)</code> method on the returned {@link Clone} object.
3420          * <p>
3421          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3422          * called.
3423          * </p>
3424          * 
3425          * @param from the node that is to be copied.
3426          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3427          *         is to be copied
3428          */
3429         public Clone<BatchConjunction> clone( Node from ) {
3430             return clone(from.getLocation());
3431         }
3432 
3433         /**
3434          * Begin the request to clone a node at the specified location into a parent node at a different location, which is
3435          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3436          * <p>
3437          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3438          * called.
3439          * </p>
3440          * 
3441          * @param from the location of the node that is to be copied.
3442          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3443          *         is to be copied
3444          */
3445         public Clone<BatchConjunction> clone( Location from ) {
3446             assertNotExecuted();
3447             return new CloneAction<BatchConjunction>(this.nextRequests, from) {
3448                 @Override
3449                 protected BatchConjunction submit( String fromWorkspaceName,
3450                                                    Location from,
3451                                                    String intoWorkspaceName,
3452                                                    Location into,
3453                                                    Name desiredName,
3454                                                    Segment desiredSegment,
3455                                                    boolean removeExisting ) {
3456                     requestQueue.cloneBranch(from,
3457                                              fromWorkspaceName,
3458                                              into,
3459                                              intoWorkspaceName,
3460                                              desiredName,
3461                                              desiredSegment,
3462                                              removeExisting);
3463                     return and();
3464                 }
3465             };
3466         }
3467 
3468         /**
3469          * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
3470          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3471          * <p>
3472          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3473          * called.
3474          * </p>
3475          * 
3476          * @param fromPath the path to the node that is to be copied.
3477          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3478          *         is to be copied
3479          */
3480         public Clone<BatchConjunction> clone( String fromPath ) {
3481             return clone(Location.create(createPath(fromPath)));
3482         }
3483 
3484         /**
3485          * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
3486          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3487          * <p>
3488          * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the
3489          * <code>into(...)</code> method is called.
3490          * </p>
3491          * 
3492          * @param from the path to the node that is to be copied.
3493          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3494          *         is to be copied
3495          */
3496         public Clone<BatchConjunction> clone( Path from ) {
3497             return clone(Location.create(from));
3498         }
3499 
3500         /**
3501          * Begin the request to clone a node with the specified unique identifier into a parent node at a different location,
3502          * which is specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3503          * <p>
3504          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3505          * called.
3506          * </p>
3507          * 
3508          * @param from the UUID of the node that is to be copied.
3509          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3510          *         is to be copied
3511          */
3512         public Clone<BatchConjunction> clone( UUID from ) {
3513             return clone(Location.create(from));
3514         }
3515 
3516         /**
3517          * Begin the request to clone a node with the specified unique identification property into a parent node at a different
3518          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3519          * identification property should uniquely identify a single node.
3520          * <p>
3521          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3522          * called.
3523          * </p>
3524          * 
3525          * @param idProperty the unique identification property of the node that is to be copied.
3526          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3527          *         is to be copied
3528          */
3529         public Clone<BatchConjunction> clone( Property idProperty ) {
3530             return clone(Location.create(idProperty));
3531         }
3532 
3533         /**
3534          * Begin the request to clone a node with the specified identification properties into a parent node at a different
3535          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3536          * identification properties should uniquely identify a single node.
3537          * <p>
3538          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3539          * called.
3540          * </p>
3541          * 
3542          * @param firstIdProperty the first identification property of the node that is to be copied
3543          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3544          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3545          *         is to be copied
3546          */
3547         public Clone<BatchConjunction> clone( Property firstIdProperty,
3548                                               Property... additionalIdProperties ) {
3549             return clone(Location.create(firstIdProperty, additionalIdProperties));
3550         }
3551 
3552         /**
3553          * Begin the request to clone a node with the specified identification properties into a parent node at a different
3554          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3555          * identification properties should uniquely identify a single node.
3556          * <p>
3557          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3558          * called.
3559          * </p>
3560          * 
3561          * @param idProperties the identification properties of the node that is to be copied
3562          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3563          *         is to be copied
3564          */
3565         public Clone<BatchConjunction> clone( Iterable<Property> idProperties ) {
3566             return clone(Location.create(idProperties));
3567         }
3568 
3569         /**
3570          * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
3571          * <code>into(...)</code> method on the returned {@link Copy} object.
3572          * <p>
3573          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3574          * called.
3575          * </p>
3576          * 
3577          * @param from the node that is to be copied.
3578          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3579          *         is to be copied
3580          */
3581         public Copy<BatchConjunction> copy( Node from ) {
3582             return copy(from.getLocation());
3583         }
3584 
3585         /**
3586          * Begin the request to copy a node at the specified location into a parent node at a different location, which is
3587          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3588          * <p>
3589          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3590          * called.
3591          * </p>
3592          * 
3593          * @param from the location of the node that is to be copied.
3594          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3595          *         is to be copied
3596          */
3597         public Copy<BatchConjunction> copy( Location from ) {
3598             assertNotExecuted();
3599             return new CopyAction<BatchConjunction>(this.nextRequests, from) {
3600                 @Override
3601                 protected BatchConjunction submit( String fromWorkspaceName,
3602                                                    Locations from,
3603                                                    Location into,
3604                                                    Name copyName ) {
3605 
3606                     String intoWorkspaceName = getCurrentWorkspaceName();
3607                     if (fromWorkspaceName == null) fromWorkspaceName = intoWorkspaceName;
3608                     do {
3609                         requestQueue.copyBranch(from.getLocation(), fromWorkspaceName, into, intoWorkspaceName, copyName, null);
3610                     } while ((from = from.next()) != null);
3611                     return and();
3612                 }
3613             };
3614         }
3615 
3616         /**
3617          * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
3618          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3619          * <p>
3620          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3621          * called.
3622          * </p>
3623          * 
3624          * @param fromPath the path to the node that is to be copied.
3625          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3626          *         is to be copied
3627          */
3628         public Copy<BatchConjunction> copy( String fromPath ) {
3629             return copy(Location.create(createPath(fromPath)));
3630         }
3631 
3632         /**
3633          * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
3634          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3635          * <p>
3636          * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the
3637          * <code>into(...)</code> method is called.
3638          * </p>
3639          * 
3640          * @param from the path to the node that is to be copied.
3641          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3642          *         is to be copied
3643          */
3644         public Copy<BatchConjunction> copy( Path from ) {
3645             return copy(Location.create(from));
3646         }
3647 
3648         /**
3649          * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which
3650          * is specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3651          * <p>
3652          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3653          * called.
3654          * </p>
3655          * 
3656          * @param from the UUID of the node that is to be copied.
3657          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3658          *         is to be copied
3659          */
3660         public Copy<BatchConjunction> copy( UUID from ) {
3661             return copy(Location.create(from));
3662         }
3663 
3664         /**
3665          * Begin the request to copy a node with the specified unique identification property into a parent node at a different
3666          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3667          * identification property should uniquely identify a single node.
3668          * <p>
3669          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3670          * called.
3671          * </p>
3672          * 
3673          * @param idProperty the unique identification property of the node that is to be copied.
3674          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3675          *         is to be copied
3676          */
3677         public Copy<BatchConjunction> copy( Property idProperty ) {
3678             return copy(Location.create(idProperty));
3679         }
3680 
3681         /**
3682          * Begin the request to copy a node with the specified identification properties into a parent node at a different
3683          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3684          * identification properties should uniquely identify a single node.
3685          * <p>
3686          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3687          * called.
3688          * </p>
3689          * 
3690          * @param firstIdProperty the first identification property of the node that is to be copied
3691          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3692          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3693          *         is to be copied
3694          */
3695         public Copy<BatchConjunction> copy( Property firstIdProperty,
3696                                             Property... additionalIdProperties ) {
3697             return copy(Location.create(firstIdProperty, additionalIdProperties));
3698         }
3699 
3700         /**
3701          * Begin the request to copy a node with the specified identification properties into a parent node at a different
3702          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3703          * identification properties should uniquely identify a single node.
3704          * <p>
3705          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3706          * called.
3707          * </p>
3708          * 
3709          * @param idProperties the identification properties of the node that is to be copied
3710          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3711          *         is to be copied
3712          */
3713         public Copy<BatchConjunction> copy( Iterable<Property> idProperties ) {
3714             return copy(Location.create(idProperties));
3715         }
3716 
3717         /**
3718          * Request to delete the specified node.
3719          * <p>
3720          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3721          * called.
3722          * </p>
3723          * 
3724          * @param at the node that is to be deleted
3725          * @return an object that may be used to start another request
3726          */
3727         public BatchConjunction delete( Node at ) {
3728             return delete(at.getLocation());
3729         }
3730 
3731         /**
3732          * Request to delete the node at the given location.
3733          * <p>
3734          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3735          * called.
3736          * </p>
3737          * 
3738          * @param at the location of the node that is to be deleted
3739          * @return an object that may be used to start another request
3740          */
3741         public BatchConjunction delete( Location at ) {
3742             assertNotExecuted();
3743             this.requestQueue.deleteBranch(at, getCurrentWorkspaceName());
3744             return nextRequests;
3745         }
3746 
3747         /**
3748          * Request to delete the node at the given path.
3749          * <p>
3750          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3751          * called.
3752          * </p>
3753          * 
3754          * @param atPath the path of the node that is to be deleted
3755          * @return an object that may be used to start another request
3756          */
3757         public BatchConjunction delete( String atPath ) {
3758             return delete(Location.create(createPath(atPath)));
3759         }
3760 
3761         /**
3762          * Request to delete the node at the given path.
3763          * <p>
3764          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3765          * called.
3766          * </p>
3767          * 
3768          * @param at the path of the node that is to be deleted
3769          * @return an object that may be used to start another request
3770          */
3771         public BatchConjunction delete( Path at ) {
3772             return delete(Location.create(at));
3773         }
3774 
3775         /**
3776          * Request to delete the node with the given UUID.
3777          * <p>
3778          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3779          * called.
3780          * </p>
3781          * 
3782          * @param at the UUID of the node that is to be deleted
3783          * @return an object that may be used to start another request
3784          */
3785         public BatchConjunction delete( UUID at ) {
3786             return delete(Location.create(at));
3787         }
3788 
3789         /**
3790          * Request to delete the node with the given unique identification property.
3791          * <p>
3792          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3793          * called.
3794          * </p>
3795          * 
3796          * @param idProperty the unique identifying property of the node that is to be deleted
3797          * @return an object that may be used to start another request
3798          */
3799         public BatchConjunction delete( Property idProperty ) {
3800             return delete(Location.create(idProperty));
3801         }
3802 
3803         /**
3804          * Request to delete the node with the given identification properties. The identification properties should uniquely
3805          * identify a single node.
3806          * <p>
3807          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3808          * called.
3809          * </p>
3810          * 
3811          * @param firstIdProperty the first identification property of the node that is to be copied
3812          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3813          * @return an object that may be used to start another request
3814          */
3815         public BatchConjunction delete( Property firstIdProperty,
3816                                         Property... additionalIdProperties ) {
3817             return delete(Location.create(firstIdProperty, additionalIdProperties));
3818         }
3819 
3820         /**
3821          * Request to delete the node with the given identification properties. The identification properties should uniquely
3822          * identify a single node.
3823          * <p>
3824          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3825          * called.
3826          * </p>
3827          * 
3828          * @param idProperties the identification property of the node that is to be copied
3829          * @return an object that may be used to start another request
3830          */
3831         public BatchConjunction delete( Iterable<Property> idProperties ) {
3832             return delete(Location.create(idProperties));
3833         }
3834 
3835         /**
3836          * Begin the request to create a node located at the supplied path.
3837          * <p>
3838          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3839          * called.
3840          * </p>
3841          * 
3842          * @param atPath the path to the node that is to be created.
3843          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3844          *         node where the node is to be created
3845          */
3846         public Create<Batch> create( String atPath ) {
3847             return create(createPath(atPath));
3848         }
3849 
3850         /**
3851          * Begin the request to create a node located at the supplied path.
3852          * <p>
3853          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3854          * called.
3855          * </p>
3856          * 
3857          * @param atPath the path to the node that is to be created.
3858          * @param property a property for the new node
3859          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3860          *         node where the node is to be created
3861          */
3862         public Create<Batch> create( String atPath,
3863                                      Property property ) {
3864             return create(createPath(atPath)).with(property);
3865         }
3866 
3867         /**
3868          * Begin the request to create a node located at the supplied path.
3869          * <p>
3870          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3871          * called.
3872          * </p>
3873          * 
3874          * @param atPath the path to the node that is to be created.
3875          * @param firstProperty a property for the new node
3876          * @param additionalProperties additional properties for the new node
3877          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3878          *         node where the node is to be created
3879          */
3880         public Create<Batch> create( String atPath,
3881                                      Property firstProperty,
3882                                      Property... additionalProperties ) {
3883             return create(createPath(atPath)).with(firstProperty, additionalProperties);
3884         }
3885 
3886         /**
3887          * Begin the request to create a node located at the supplied path.
3888          * <p>
3889          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3890          * called.
3891          * </p>
3892          * 
3893          * @param at the path to the node that is to be created.
3894          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3895          *         node where the node is to be created
3896          */
3897         public final Create<Batch> create( Path at ) {
3898             assertNotExecuted();
3899             CheckArg.isNotNull(at, "at");
3900             Path parent = at.getParent();
3901             Name name = at.getLastSegment().getName();
3902             return create(Location.create(parent), name);
3903         }
3904 
3905         protected final CreateAction<Batch> create( Location parent,
3906                                                     Name child ) {
3907             return new CreateAction<Batch>(this, parent, getCurrentWorkspaceName(), child) {
3908                 @Override
3909                 protected Batch submit( Location parent,
3910                                         String workspaceName,
3911                                         Name childName,
3912                                         Collection<Property> properties,
3913                                         NodeConflictBehavior behavior ) {
3914                     requestQueue.createNode(parent, workspaceName, childName, properties.iterator(), behavior);
3915                     return Batch.this;
3916                 }
3917             };
3918         }
3919 
3920         /**
3921          * Begin the request to create a node located at the supplied path.
3922          * <p>
3923          * Like all other methods on the {@link Batch}, the request will be performed when the {@link Batch#execute()} method is
3924          * called.
3925          * </p>
3926          * 
3927          * @param at the path to the node that is to be created.
3928          * @param properties the iterator over the properties for the new node
3929          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3930          *         node where the node is to be created
3931          */
3932         public Create<Batch> create( Path at,
3933                                      Iterable<Property> properties ) {
3934             Create<Batch> action = create(at);
3935             for (Property property : properties) {
3936                 action.and(property);
3937             }
3938             return action;
3939         }
3940 
3941         /**
3942          * Begin the request to create a node located at the supplied path.
3943          * <p>
3944          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3945          * called.
3946          * </p>
3947          * 
3948          * @param at the path to the node that is to be created.
3949          * @param property a property for the new node
3950          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3951          *         node where the node is to be created
3952          */
3953         public Create<Batch> create( Path at,
3954                                      Property property ) {
3955             return create(at).with(property);
3956         }
3957 
3958         /**
3959          * Begin the request to create a node located at the supplied path.
3960          * <p>
3961          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3962          * called.
3963          * </p>
3964          * 
3965          * @param at the path to the node that is to be created.
3966          * @param firstProperty a property for the new node
3967          * @param additionalProperties additional properties for the new node
3968          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3969          *         node where the node is to be created
3970          */
3971         public Create<Batch> create( Path at,
3972                                      Property firstProperty,
3973                                      Property... additionalProperties ) {
3974             return create(at).with(firstProperty, additionalProperties);
3975         }
3976 
3977         /**
3978          * Begin the request to create a node under the existing parent node at the supplied location. This request is submitted
3979          * to the repository after the returned components are completed.
3980          * 
3981          * @param parent the location of the parent
3982          * @return the object used to start creating a node
3983          */
3984         public CreateNodeNamed<Batch> createUnder( Location parent ) {
3985             CheckArg.isNotNull(parent, "parent");
3986             return new CreateNodeNamedAction<Batch>(this, parent) {
3987                 @Override
3988                 protected CreateAction<Batch> createWith( Batch batch,
3989                                                           Location parent,
3990                                                           Name childName ) {
3991                     return Batch.this.create(parent, childName);
3992                 }
3993             };
3994         }
3995 
3996         public AddValue<Batch> addValue( Object value ) {
3997             return new AddValueAction<Batch>(this, this.getCurrentWorkspaceName(), value) {
3998 
3999                 @Override
4000                 protected Batch submit( String workspaceName,
4001                                         Location on,
4002                                         Name property,
4003                                         List<Object> values ) {
4004                     requests.addValues(workspaceName, on, property, values);
4005                     return nextRequests.and();
4006                 }
4007             };
4008         }
4009 
4010         public RemoveValue<Batch> removeValue( Object value ) {
4011             return new RemoveValueAction<Batch>(this, this.getCurrentWorkspaceName(), value) {
4012 
4013                 @Override
4014                 protected Batch submit( String workspaceName,
4015                                         Location on,
4016                                         Name property,
4017                                         List<Object> values ) {
4018                     requests.removeValues(workspaceName, on, property, values);
4019                     return nextRequests.and();
4020                 }
4021             };
4022         }
4023 
4024         /**
4025          * Set the properties on a node.
4026          * 
4027          * @param properties the properties to set
4028          * @return the interface that should be used to specify the node on which the properties are to be set.
4029          */
4030         public On<BatchConjunction> set( final Property... properties ) {
4031             return new On<BatchConjunction>() {
4032                 public BatchConjunction on( Location location ) {
4033                     requestQueue.setProperties(location, getCurrentWorkspaceName(), properties);
4034                     return nextRequests;
4035                 }
4036 
4037                 public BatchConjunction on( String path ) {
4038                     return on(Location.create(createPath(path)));
4039                 }
4040 
4041                 public BatchConjunction on( Path path ) {
4042                     return on(Location.create(path));
4043                 }
4044 
4045                 public BatchConjunction on( Property idProperty ) {
4046                     return on(Location.create(idProperty));
4047                 }
4048 
4049                 public BatchConjunction on( Property firstIdProperty,
4050                                             Property... additionalIdProperties ) {
4051                     return on(Location.create(firstIdProperty, additionalIdProperties));
4052                 }
4053 
4054                 public BatchConjunction on( Iterable<Property> idProperties ) {
4055                     return on(Location.create(idProperties));
4056                 }
4057 
4058                 public BatchConjunction on( UUID uuid ) {
4059                     return on(Location.create(uuid));
4060                 }
4061             };
4062         }
4063 
4064         /**
4065          * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
4066          * value(s) and the location of the node onto which the property should be set.
4067          * 
4068          * @param propertyName the property name
4069          * @return the interface used to specify the values
4070          */
4071         public SetValues<BatchConjunction> set( String propertyName ) {
4072             Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
4073             return set(name);
4074         }
4075 
4076         /**
4077          * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
4078          * value(s) and the location of the node onto which the property should be set.
4079          * 
4080          * @param propertyName the property name
4081          * @return the interface used to specify the values
4082          */
4083         public SetValues<BatchConjunction> set( final Name propertyName ) {
4084             return new SetValues<BatchConjunction>() {
4085                 public SetValuesTo<BatchConjunction> on( final Location location ) {
4086                     return new SetValuesTo<BatchConjunction>() {
4087                         public BatchConjunction to( Node value ) {
4088                             return to(value.getLocation());
4089                         }
4090 
4091                         public BatchConjunction to( Location value ) {
4092                             Reference ref = (Reference)convertReferenceValue(value);
4093                             Property property = getContext().getPropertyFactory().create(propertyName, ref);
4094                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4095                             return nextRequests;
4096                         }
4097 
4098                         protected BatchConjunction toValue( Object value ) {
4099                             Property property = getContext().getPropertyFactory().create(propertyName, value);
4100                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4101                             return nextRequests;
4102                         }
4103 
4104                         public BatchConjunction to( String value ) {
4105                             return toValue(value);
4106                         }
4107 
4108                         public BatchConjunction to( int value ) {
4109                             return toValue(Integer.valueOf(value));
4110                         }
4111 
4112                         public BatchConjunction to( long value ) {
4113                             return toValue(Long.valueOf(value));
4114                         }
4115 
4116                         public BatchConjunction to( boolean value ) {
4117                             return toValue(Boolean.valueOf(value));
4118                         }
4119 
4120                         public BatchConjunction to( float value ) {
4121                             return toValue(Float.valueOf(value));
4122                         }
4123 
4124                         public BatchConjunction to( double value ) {
4125                             return toValue(Double.valueOf(value));
4126                         }
4127 
4128                         public BatchConjunction to( BigDecimal value ) {
4129                             return toValue(value);
4130                         }
4131 
4132                         public BatchConjunction to( Calendar value ) {
4133                             return toValue(value);
4134                         }
4135 
4136                         public BatchConjunction to( Date value ) {
4137                             return toValue(value);
4138                         }
4139 
4140                         public BatchConjunction to( DateTime value ) {
4141                             return toValue(value);
4142                         }
4143 
4144                         public BatchConjunction to( Name value ) {
4145                             return toValue(value);
4146                         }
4147 
4148                         public BatchConjunction to( Path value ) {
4149                             return toValue(value);
4150                         }
4151 
4152                         public BatchConjunction to( Reference value ) {
4153                             return toValue(value);
4154                         }
4155 
4156                         public BatchConjunction to( URI value ) {
4157                             return toValue(value);
4158                         }
4159 
4160                         public BatchConjunction to( UUID value ) {
4161                             return toValue(value);
4162                         }
4163 
4164                         public BatchConjunction to( Binary value ) {
4165                             return toValue(value);
4166                         }
4167 
4168                         public BatchConjunction to( byte[] value ) {
4169                             return toValue(value);
4170                         }
4171 
4172                         public BatchConjunction to( InputStream stream,
4173                                                     long approximateLength ) {
4174                             Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
4175                             return toValue(value);
4176                         }
4177 
4178                         public BatchConjunction to( Reader reader,
4179                                                     long approximateLength ) {
4180                             Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
4181                             return toValue(value);
4182                         }
4183 
4184                         public BatchConjunction to( Object value ) {
4185                             value = convertReferenceValue(value);
4186                             Property property = getContext().getPropertyFactory().create(propertyName, value);
4187                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4188                             return nextRequests;
4189                         }
4190 
4191                         public BatchConjunction to( Object firstValue,
4192                                                     Object... otherValues ) {
4193                             firstValue = convertReferenceValue(firstValue);
4194                             for (int i = 0, len = otherValues.length; i != len; ++i) {
4195                                 otherValues[i] = convertReferenceValue(otherValues[i]);
4196                             }
4197                             Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
4198                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4199                             return nextRequests;
4200                         }
4201 
4202                         public BatchConjunction to( Object[] values ) {
4203                             for (int i = 0; i != values.length; ++i) {
4204                                 values[i] = convertReferenceValue(values[i]);
4205                             }
4206                             Property property = getContext().getPropertyFactory().create(propertyName, values);
4207                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4208                             return nextRequests;
4209                         }
4210 
4211                         public BatchConjunction to( Iterable<?> values ) {
4212                             List<Object> valueList = new LinkedList<Object>();
4213                             for (Object value : values) {
4214                                 value = convertReferenceValue(value);
4215                                 valueList.add(value);
4216                             }
4217                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
4218                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4219                             return nextRequests;
4220                         }
4221 
4222                         public BatchConjunction to( Iterator<?> values ) {
4223                             List<Object> valueList = new LinkedList<Object>();
4224                             while (values.hasNext()) {
4225                                 Object value = values.next();
4226                                 valueList.add(value);
4227                             }
4228                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
4229                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4230                             return nextRequests;
4231                         }
4232 
4233                     };
4234                 }
4235 
4236                 public SetValuesTo<BatchConjunction> on( String path ) {
4237                     return on(Location.create(createPath(path)));
4238                 }
4239 
4240                 public SetValuesTo<BatchConjunction> on( Path path ) {
4241                     return on(Location.create(path));
4242                 }
4243 
4244                 public SetValuesTo<BatchConjunction> on( Property idProperty ) {
4245                     return on(Location.create(idProperty));
4246                 }
4247 
4248                 public SetValuesTo<BatchConjunction> on( Property firstIdProperty,
4249                                                          Property... additionalIdProperties ) {
4250                     return on(Location.create(firstIdProperty, additionalIdProperties));
4251                 }
4252 
4253                 public SetValuesTo<BatchConjunction> on( Iterable<Property> idProperties ) {
4254                     return on(Location.create(idProperties));
4255                 }
4256 
4257                 public SetValuesTo<BatchConjunction> on( UUID uuid ) {
4258                     return on(Location.create(uuid));
4259                 }
4260 
4261                 public On<BatchConjunction> to( Node value ) {
4262                     Object reference = convertReferenceValue(value);
4263                     return set(getContext().getPropertyFactory().create(propertyName, reference));
4264                 }
4265 
4266                 public On<BatchConjunction> to( Location value ) {
4267                     Object reference = convertReferenceValue(value);
4268                     return set(getContext().getPropertyFactory().create(propertyName, reference));
4269                 }
4270 
4271                 protected On<BatchConjunction> toValue( Object value ) {
4272                     return set(getContext().getPropertyFactory().create(propertyName, value));
4273                 }
4274 
4275                 public On<BatchConjunction> to( String value ) {
4276                     return toValue(value);
4277                 }
4278 
4279                 public On<BatchConjunction> to( int value ) {
4280                     return toValue(Integer.valueOf(value));
4281                 }
4282 
4283                 public On<BatchConjunction> to( long value ) {
4284                     return toValue(Long.valueOf(value));
4285                 }
4286 
4287                 public On<BatchConjunction> to( boolean value ) {
4288                     return toValue(Boolean.valueOf(value));
4289                 }
4290 
4291                 public On<BatchConjunction> to( float value ) {
4292                     return toValue(Float.valueOf(value));
4293                 }
4294 
4295                 public On<BatchConjunction> to( double value ) {
4296                     return toValue(Double.valueOf(value));
4297                 }
4298 
4299                 public On<BatchConjunction> to( BigDecimal value ) {
4300                     return toValue(value);
4301                 }
4302 
4303                 public On<BatchConjunction> to( Calendar value ) {
4304                     return toValue(value);
4305                 }
4306 
4307                 public On<BatchConjunction> to( Date value ) {
4308                     return toValue(value);
4309                 }
4310 
4311                 public On<BatchConjunction> to( DateTime value ) {
4312                     return toValue(value);
4313                 }
4314 
4315                 public On<BatchConjunction> to( Name value ) {
4316                     return toValue(value);
4317                 }
4318 
4319                 public On<BatchConjunction> to( Path value ) {
4320                     return toValue(value);
4321                 }
4322 
4323                 public On<BatchConjunction> to( Reference value ) {
4324                     return toValue(value);
4325                 }
4326 
4327                 public On<BatchConjunction> to( URI value ) {
4328                     return toValue(value);
4329                 }
4330 
4331                 public On<BatchConjunction> to( UUID value ) {
4332                     return toValue(value);
4333                 }
4334 
4335                 public On<BatchConjunction> to( Binary value ) {
4336                     return toValue(value);
4337                 }
4338 
4339                 public On<BatchConjunction> to( byte[] value ) {
4340                     return toValue(value);
4341                 }
4342 
4343                 public On<BatchConjunction> to( InputStream stream,
4344                                                 long approximateLength ) {
4345                     Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
4346                     return toValue(value);
4347                 }
4348 
4349                 public On<BatchConjunction> to( Reader reader,
4350                                                 long approximateLength ) {
4351                     Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
4352                     return toValue(value);
4353                 }
4354 
4355                 public On<BatchConjunction> to( Object value ) {
4356                     value = convertReferenceValue(value);
4357                     return set(getContext().getPropertyFactory().create(propertyName, value));
4358                 }
4359 
4360                 public On<BatchConjunction> to( Object firstValue,
4361                                                 Object... otherValues ) {
4362                     Object[] values = new Object[otherValues.length + 1];
4363                     values[0] = convertReferenceValue(firstValue);
4364                     for (int i = 0, len = otherValues.length; i != len; ++i) {
4365                         values[i + 1] = convertReferenceValue(otherValues[i]);
4366                     }
4367                     return set(getContext().getPropertyFactory().create(propertyName, values));
4368                 }
4369 
4370                 public On<BatchConjunction> to( Object[] values ) {
4371                     for (int i = 0, len = values.length; i != len; ++i) {
4372                         values[i] = convertReferenceValue(values[i]);
4373                     }
4374                     return set(getContext().getPropertyFactory().create(propertyName, values));
4375                 }
4376 
4377                 public On<BatchConjunction> to( Iterable<?> values ) {
4378                     List<Object> valueList = new LinkedList<Object>();
4379                     for (Object value : values) {
4380                         value = convertReferenceValue(value);
4381                         valueList.add(value);
4382                     }
4383                     return set(getContext().getPropertyFactory().create(propertyName, valueList));
4384                 }
4385 
4386                 public On<BatchConjunction> to( Iterator<?> values ) {
4387                     List<Object> valueList = new LinkedList<Object>();
4388                     while (values.hasNext()) {
4389                         Object value = values.next();
4390                         valueList.add(value);
4391                     }
4392                     return set(getContext().getPropertyFactory().create(propertyName, valueList));
4393                 }
4394             };
4395         }
4396 
4397         /**
4398          * Remove properties from the node at the given location.
4399          * 
4400          * @param propertyNames the names of the properties to be removed
4401          * @return the remove request object that should be used to specify the node from which the properties are to be removed.
4402          */
4403         public On<BatchConjunction> remove( final Name... propertyNames ) {
4404             return new On<BatchConjunction>() {
4405                 public BatchConjunction on( Location location ) {
4406                     requestQueue.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
4407                     return nextRequests;
4408                 }
4409 
4410                 public BatchConjunction on( String path ) {
4411                     return on(Location.create(createPath(path)));
4412                 }
4413 
4414                 public BatchConjunction on( Path path ) {
4415                     return on(Location.create(path));
4416                 }
4417 
4418                 public BatchConjunction on( Property idProperty ) {
4419                     return on(Location.create(idProperty));
4420                 }
4421 
4422                 public BatchConjunction on( Property firstIdProperty,
4423                                             Property... additionalIdProperties ) {
4424                     return on(Location.create(firstIdProperty, additionalIdProperties));
4425                 }
4426 
4427                 public BatchConjunction on( Iterable<Property> idProperties ) {
4428                     return on(Location.create(idProperties));
4429                 }
4430 
4431                 public BatchConjunction on( UUID uuid ) {
4432                     return on(Location.create(uuid));
4433                 }
4434             };
4435         }
4436 
4437         /**
4438          * Remove properties from the node at the given location.
4439          * 
4440          * @param propertyNames the names of the properties to be removed
4441          * @return the remove request object that should be used to specify the node from which the properties are to be removed.
4442          */
4443         public On<BatchConjunction> remove( String... propertyNames ) {
4444             NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
4445             int number = propertyNames.length;
4446             final Name[] names = new Name[number];
4447             for (int i = 0; i != number; ++i) {
4448                 names[i] = nameFactory.create(propertyNames[i]);
4449             }
4450             return new On<BatchConjunction>() {
4451                 public BatchConjunction on( Location location ) {
4452                     requestQueue.removeProperties(location, getCurrentWorkspaceName(), names);
4453                     return nextRequests;
4454                 }
4455 
4456                 public BatchConjunction on( String path ) {
4457                     return on(Location.create(createPath(path)));
4458                 }
4459 
4460                 public BatchConjunction on( Path path ) {
4461                     return on(Location.create(path));
4462                 }
4463 
4464                 public BatchConjunction on( Property idProperty ) {
4465                     return on(Location.create(idProperty));
4466                 }
4467 
4468                 public BatchConjunction on( Property firstIdProperty,
4469                                             Property... additionalIdProperties ) {
4470                     return on(Location.create(firstIdProperty, additionalIdProperties));
4471                 }
4472 
4473                 public BatchConjunction on( Iterable<Property> idProperties ) {
4474                     return on(Location.create(idProperties));
4475                 }
4476 
4477                 public BatchConjunction on( UUID uuid ) {
4478                     return on(Location.create(uuid));
4479                 }
4480             };
4481         }
4482 
4483         /**
4484          * Request to read the node with the supplied UUID.
4485          * <p>
4486          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4487          * called.
4488          * </p>
4489          * 
4490          * @param uuid the UUID of the node that is to be read
4491          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4492          */
4493         public BatchConjunction read( UUID uuid ) {
4494             return read(Location.create(uuid));
4495         }
4496 
4497         /**
4498          * Request to read the node at the supplied location.
4499          * <p>
4500          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4501          * called.
4502          * </p>
4503          * 
4504          * @param location the location of the node that is to be read
4505          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4506          */
4507         public BatchConjunction read( Location location ) {
4508             assertNotExecuted();
4509             requestQueue.readNode(location, getCurrentWorkspaceName());
4510             return nextRequests;
4511         }
4512 
4513         /**
4514          * Request to read the node at the supplied path.
4515          * <p>
4516          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4517          * called.
4518          * </p>
4519          * 
4520          * @param path the path of the node that is to be read
4521          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4522          */
4523         public BatchConjunction read( String path ) {
4524             return read(Location.create(createPath(path)));
4525         }
4526 
4527         /**
4528          * Request to read the node at the supplied path.
4529          * <p>
4530          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4531          * called.
4532          * </p>
4533          * 
4534          * @param path the path of the node that is to be read
4535          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4536          */
4537         public BatchConjunction read( Path path ) {
4538             return read(Location.create(path));
4539         }
4540 
4541         /**
4542          * Request to read the node with the supplied unique identifier property.
4543          * <p>
4544          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4545          * called.
4546          * </p>
4547          * 
4548          * @param idProperty the identification property that is unique to the node that is to be read
4549          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4550          */
4551         public BatchConjunction read( Property idProperty ) {
4552             return read(Location.create(idProperty));
4553         }
4554 
4555         /**
4556          * Request to read the node with the supplied unique identifier properties.
4557          * <p>
4558          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4559          * called.
4560          * </p>
4561          * 
4562          * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
4563          * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be
4564          *        read
4565          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4566          */
4567         public BatchConjunction read( Property firstIdProperty,
4568                                       Property... additionalIdProperties ) {
4569             return read(Location.create(firstIdProperty, additionalIdProperties));
4570         }
4571 
4572         /**
4573          * Request to read the node with the supplied unique identifier properties.
4574          * <p>
4575          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4576          * called.
4577          * </p>
4578          * 
4579          * @param idProperties the identification properties that uniquely identify the node that is to be read
4580          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4581          */
4582         public BatchConjunction read( Iterable<Property> idProperties ) {
4583             return read(Location.create(idProperties));
4584         }
4585 
4586         /**
4587          * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
4588          * returned {@link On} object.
4589          * <p>
4590          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4591          * called.
4592          * </p>
4593          * 
4594          * @param propertyName the name of the property that is to be read
4595          * @return the object that is used to specified the node whose property is to be read
4596          */
4597         public On<BatchConjunction> readProperty( String propertyName ) {
4598             assertNotExecuted();
4599             Name name = Graph.this.getContext().getValueFactories().getNameFactory().create(propertyName);
4600             return readProperty(name);
4601         }
4602 
4603         /**
4604          * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
4605          * returned {@link On} object.
4606          * <p>
4607          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4608          * called.
4609          * </p>
4610          * 
4611          * @param name the name of the property that is to be read
4612          * @return the object that is used to specified the node whose property is to be read
4613          */
4614         public On<BatchConjunction> readProperty( final Name name ) {
4615             assertNotExecuted();
4616             return new On<BatchConjunction>() {
4617                 public BatchConjunction on( String path ) {
4618                     return on(Location.create(createPath(path)));
4619                 }
4620 
4621                 public BatchConjunction on( Path path ) {
4622                     return on(Location.create(path));
4623                 }
4624 
4625                 public BatchConjunction on( Property idProperty ) {
4626                     return on(Location.create(idProperty));
4627                 }
4628 
4629                 public BatchConjunction on( Property firstIdProperty,
4630                                             Property... additionalIdProperties ) {
4631                     return on(Location.create(firstIdProperty, additionalIdProperties));
4632                 }
4633 
4634                 public BatchConjunction on( Iterable<Property> idProperties ) {
4635                     return on(Location.create(idProperties));
4636                 }
4637 
4638                 public BatchConjunction on( UUID uuid ) {
4639                     return on(Location.create(uuid));
4640                 }
4641 
4642                 public BatchConjunction on( Location at ) {
4643                     requestQueue.readProperty(at, getCurrentWorkspaceName(), name);
4644                     return Batch.this.nextRequests;
4645                 }
4646             };
4647         }
4648 
4649         /**
4650          * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
4651          * object.
4652          * <p>
4653          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4654          * called.
4655          * </p>
4656          * 
4657          * @return the object that is used to specified the node whose properties are to be read,
4658          */
4659         public On<BatchConjunction> readProperties() {
4660             assertNotExecuted();
4661             return new On<BatchConjunction>() {
4662                 public BatchConjunction on( Location location ) {
4663                     requestQueue.readAllProperties(location, getCurrentWorkspaceName());
4664                     return Batch.this.nextRequests;
4665                 }
4666 
4667                 public BatchConjunction on( String path ) {
4668                     return on(Location.create(createPath(path)));
4669                 }
4670 
4671                 public BatchConjunction on( Path path ) {
4672                     return on(Location.create(path));
4673                 }
4674 
4675                 public BatchConjunction on( Property idProperty ) {
4676                     return on(Location.create(idProperty));
4677                 }
4678 
4679                 public BatchConjunction on( Property firstIdProperty,
4680                                             Property... additionalIdProperties ) {
4681                     return on(Location.create(firstIdProperty, additionalIdProperties));
4682                 }
4683 
4684                 public BatchConjunction on( Iterable<Property> idProperties ) {
4685                     return on(Location.create(idProperties));
4686                 }
4687 
4688                 public BatchConjunction on( UUID uuid ) {
4689                     return on(Location.create(uuid));
4690                 }
4691             };
4692         }
4693 
4694         /**
4695          * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
4696          * object.
4697          * <p>
4698          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4699          * called.
4700          * </p>
4701          * 
4702          * @return the object that is used to specified the node whose children are to be read
4703          */
4704         public Of<BatchConjunction> readChildren() {
4705             assertNotExecuted();
4706             return new Of<BatchConjunction>() {
4707                 public BatchConjunction of( String path ) {
4708                     return of(Location.create(createPath(path)));
4709                 }
4710 
4711                 public BatchConjunction of( Path path ) {
4712                     return of(Location.create(path));
4713                 }
4714 
4715                 public BatchConjunction of( Property idProperty ) {
4716                     return of(Location.create(idProperty));
4717                 }
4718 
4719                 public BatchConjunction of( Property firstIdProperty,
4720                                             Property... additionalIdProperties ) {
4721                     return of(Location.create(firstIdProperty, additionalIdProperties));
4722                 }
4723 
4724                 public BatchConjunction of( Iterable<Property> idProperties ) {
4725                     return of(Location.create(idProperties));
4726                 }
4727 
4728                 public BatchConjunction of( UUID uuid ) {
4729                     return of(Location.create(uuid));
4730                 }
4731 
4732                 public BatchConjunction of( Location at ) {
4733                     requestQueue.readAllChildren(at, getCurrentWorkspaceName());
4734                     return Batch.this.nextRequests;
4735                 }
4736             };
4737         }
4738 
4739         /**
4740          * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code>
4741          * in the resulting {@link At} object.
4742          * <p>
4743          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4744          * called.
4745          * </p>
4746          * 
4747          * @param depth the maximum depth of the subgraph that should be read
4748          * @return the component that should be used to specify the location of the node that is the top of the subgraph
4749          */
4750         public At<BatchConjunction> readSubgraphOfDepth( final int depth ) {
4751             assertNotExecuted();
4752             return new At<BatchConjunction>() {
4753                 public BatchConjunction at( Location location ) {
4754                     requestQueue.readBranch(location, getCurrentWorkspaceName(), depth);
4755                     return Batch.this.nextRequests;
4756                 }
4757 
4758                 public BatchConjunction at( String path ) {
4759                     return at(Location.create(createPath(path)));
4760                 }
4761 
4762                 public BatchConjunction at( Path path ) {
4763                     return at(Location.create(path));
4764                 }
4765 
4766                 public BatchConjunction at( UUID uuid ) {
4767                     return at(Location.create(uuid));
4768                 }
4769 
4770                 public BatchConjunction at( Property idProperty ) {
4771                     return at(Location.create(idProperty));
4772                 }
4773 
4774                 public BatchConjunction at( Property firstIdProperty,
4775                                             Property... additionalIdProperties ) {
4776                     return at(Location.create(firstIdProperty, additionalIdProperties));
4777                 }
4778 
4779                 public BatchConjunction at( Iterable<Property> idProperties ) {
4780                     return at(Location.create(idProperties));
4781                 }
4782             };
4783         }
4784 
4785         /**
4786          * Ensure that this graph contains the content in the supplied graph. Any changes are recorded as operations on this
4787          * batch, and require {@link #execute() execution}.
4788          * 
4789          * @param otherContent the content that should exist within this graph or, if it does not exist, added to this graph
4790          * @return the interface for additional requests or actions
4791          */
4792         public Conjunction<Batch> merge( Graph otherContent ) {
4793             GraphMerger initializer = new GraphMerger(otherContent);
4794             initializer.merge(this.getGraph(), this);
4795             return new Conjunction<Batch>() {
4796                 /**
4797                  * {@inheritDoc}
4798                  * 
4799                  * @see org.modeshape.graph.Graph.Conjunction#and()
4800                  */
4801                 public Batch and() {
4802                     return Batch.this;
4803                 }
4804             };
4805         }
4806 
4807         /**
4808          * {@inheritDoc}
4809          * 
4810          * @see org.modeshape.graph.Graph.Executable#execute()
4811          */
4812         public Results execute() {
4813             executed = true;
4814             Request request = requestQueue.pop();
4815             if (request == null) {
4816                 return new BatchResults();
4817             }
4818             Graph.this.execute(request);
4819             if (RequestType.COMPOSITE == request.getType()) {
4820                 CompositeRequest composite = (CompositeRequest)request;
4821                 return new BatchResults(composite.getRequests());
4822             }
4823             return new BatchResults(request);
4824         }
4825 
4826         /**
4827          * {@inheritDoc}
4828          * 
4829          * @see java.lang.Object#toString()
4830          */
4831         @Override
4832         public String toString() {
4833             StringBuilder sb = new StringBuilder();
4834             sb.append("Pending requests:\n");
4835             sb.append(requestQueue.toString());
4836             return sb.toString();
4837         }
4838     }
4839 
4840     /**
4841      * Utility method for checking a property value. If the value is a {@link Node} or {@link Location}, a {@link Reference} value
4842      * is created (if the node/location has a UUID); otherwise, the value is returned as is.
4843      * 
4844      * @param value the property value
4845      * @return the property value, which may be a {@link Reference} if the input value is a Node or Location
4846      */
4847     protected Object convertReferenceValue( Object value ) {
4848         if (value instanceof Node) {
4849             Node node = (Node)value;
4850             UUID uuid = node.getLocation().getUuid();
4851             if (uuid == null) {
4852                 // Look for a property ...
4853                 Property uuidProperty = node.getProperty(ModeShapeLexicon.UUID);
4854                 if (uuidProperty != null) {
4855                     uuid = getContext().getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
4856                 } else {
4857                     uuidProperty = node.getProperty(JcrLexicon.UUID);
4858                     if (uuidProperty != null) {
4859                         uuid = getContext().getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
4860                     }
4861                 }
4862             }
4863             if (uuid == null) {
4864                 String nodeString = node.getLocation().getString(getContext().getNamespaceRegistry());
4865                 String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
4866                 throw new IllegalArgumentException(msg);
4867             }
4868             return getContext().getValueFactories().getReferenceFactory().create(uuid);
4869         }
4870         if (value instanceof Location) {
4871             Location location = (Location)value;
4872             UUID uuid = location.getUuid();
4873             if (uuid == null) {
4874                 String nodeString = location.getString(getContext().getNamespaceRegistry());
4875                 String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
4876                 throw new IllegalArgumentException(msg);
4877             }
4878             return getContext().getValueFactories().getReferenceFactory().create(uuid);
4879         }
4880         return value;
4881     }
4882 
4883     protected static DateTime computeExpirationTime( CacheableRequest request ) {
4884         CachePolicy policy = request.getCachePolicy();
4885         return policy == null ? null : request.getTimeLoaded().plus(policy.getTimeToLive(), TimeUnit.MILLISECONDS);
4886     }
4887 
4888     /**
4889      * The interface used to complete a query submission.
4890      */
4891     public interface BuildQuery {
4892         /**
4893          * Use the supplied hints when executing the query.
4894          * 
4895          * @param hints the hints
4896          * @return this same interface for method chaining purposes; never null
4897          * @throws IllegalArgumentException if the hints reference is null
4898          */
4899         BuildQuery using( PlanHints hints );
4900 
4901         /**
4902          * Use the supplied variables when executing the query.
4903          * 
4904          * @param variables the variables
4905          * @return this same interface for method chaining purposes; never null
4906          * @throws IllegalArgumentException if the variables reference is null
4907          */
4908         BuildQuery using( Map<String, Object> variables );
4909 
4910         /**
4911          * Use the supplied value for the given variable name when executing the query.
4912          * 
4913          * @param variableName the variable value
4914          * @param value the value to replace the variable during execution
4915          * @return this same interface for method chaining purposes; never null
4916          * @throws IllegalArgumentException if the variable name is null
4917          */
4918         BuildQuery using( String variableName,
4919                           Object value );
4920 
4921         /**
4922          * Execute the query and get the results.
4923          * 
4924          * @return the query results
4925          */
4926         QueryResults execute();
4927     }
4928 
4929     /**
4930      * The interface used to specify the name of a new workspace.
4931      */
4932     public interface NameWorkspace {
4933 
4934         /**
4935          * Specify the name of the new workspace that is to be created.
4936          * 
4937          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4938          * @return the workspace; never null
4939          * @throws IllegalArgumentException if the name of the new workspace is null
4940          * @throws InvalidWorkspaceException if there is already an existing workspace with the supplied name
4941          */
4942         Workspace named( String workspaceName );
4943 
4944         /**
4945          * Specify the name of the new workspace that is to be created. If a workspace with the supplied name already exists, the
4946          * new workspace name will be adjusted so that it is unique.
4947          * 
4948          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4949          * @return the workspace; never null
4950          * @throws IllegalArgumentException if the name of the new workspace is null
4951          */
4952         Workspace namedSomethingLike( String workspaceName );
4953     }
4954 
4955     /**
4956      * The interface used to create a new workspace.
4957      */
4958     public interface CreateWorkspace extends NameWorkspace {
4959         /**
4960          * Specify that the new workspace should be initialized as a clone of another existing workspace.
4961          * 
4962          * @param originalWorkspaceName the name of the existing workspace that will be cloned to create the new workspace;
4963          * @return the interface that should be used to set the name of the new workspace; never null
4964          * @throws IllegalArgumentException if the name of the original workspace is null
4965          * @throws InvalidWorkspaceException if there is no such workspace with the supplied name
4966          */
4967         NameWorkspace clonedFrom( String originalWorkspaceName );
4968     }
4969 
4970     /**
4971      * The interface used to destroy a workspace.
4972      */
4973     public interface DestroyWorkspace {
4974         /**
4975          * Specify the name of the new workspace that is to be destroyed.
4976          * 
4977          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4978          * @return true if the workspace was destroyed, or false otherwise
4979          * @throws IllegalArgumentException if the name of the workspace is null
4980          * @throws InvalidWorkspaceException if there is no existing workspace with the supplied name
4981          */
4982         boolean named( String workspaceName );
4983     }
4984 
4985     /**
4986      * A interface used to execute the accumulated {@link Batch requests}.
4987      * 
4988      * @param <NodeType> the type of node that is returned
4989      */
4990     public interface Executable<NodeType extends Node> {
4991         /**
4992          * Stop accumulating the requests, submit them to the repository source, and return the results.
4993          * 
4994          * @return the results containing the requested information from the repository.
4995          * @throws PathNotFoundException if a request used a node that did not exist
4996          * @throws InvalidRequestException if a request was not valid
4997          * @throws InvalidWorkspaceException if the workspace used in a request was not valid
4998          * @throws UnsupportedRequestException if a request was not supported by the source
4999          * @throws RepositorySourceException if an error occurs during execution
5000          * @throws RuntimeException if a runtime error occurs during execution
5001          */
5002         Results execute();
5003     }
5004 
5005     /**
5006      * A interface that can be used to finish the current request and start another.
5007      * 
5008      * @param <Next> the interface that will be used to start another request
5009      */
5010     public interface Conjunction<Next> {
5011         /**
5012          * Finish the request and prepare to start another.
5013          * 
5014          * @return the interface that can be used to start another request; never null
5015          */
5016         Next and();
5017     }
5018 
5019     /**
5020      * A component that defines the location into which a node should be copied or moved.
5021      * 
5022      * @param <Next> The interface that is to be returned when this request is completed
5023      */
5024     public interface Into<Next> {
5025         /**
5026          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5027          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5028          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5029          * {@link To#to(Location)} instead.
5030          * 
5031          * @param parentLocation the location of the new parent
5032          * @return the interface for additional requests or actions
5033          * @see To#to(Location)
5034          */
5035         Next into( Location parentLocation );
5036 
5037         /**
5038          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5039          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5040          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5041          * {@link To#to(String)} instead.
5042          * 
5043          * @param parentPath the path of the new parent
5044          * @return the interface for additional requests or actions
5045          * @see To#to(String)
5046          */
5047         Next into( String parentPath );
5048 
5049         /**
5050          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5051          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5052          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5053          * {@link To#to(Path)} instead.
5054          * 
5055          * @param parentPath the path of the new parent
5056          * @return the interface for additional requests or actions
5057          * @see To#to(Path)
5058          */
5059         Next into( Path parentPath );
5060 
5061         /**
5062          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5063          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5064          * same-name-sibling index).
5065          * 
5066          * @param parentUuid the UUID of the new parent
5067          * @return the interface for additional requests or actions
5068          */
5069         Next into( UUID parentUuid );
5070 
5071         /**
5072          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5073          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5074          * same-name-sibling index).
5075          * 
5076          * @param parentIdProperty the property that uniquely identifies the new parent
5077          * @return the interface for additional requests or actions
5078          */
5079         Next into( Property parentIdProperty );
5080 
5081         /**
5082          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5083          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5084          * same-name-sibling index).
5085          * 
5086          * @param firstParentIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies
5087          *        the new parent
5088          * @param additionalParentIdProperties the additional properties that, with the <code>additionalIdProperties</code>,
5089          *        uniquely identifies the new parent
5090          * @return the interface for additional requests or actions
5091          */
5092         Next into( Property firstParentIdProperty,
5093                    Property... additionalParentIdProperties );
5094     }
5095 
5096     /**
5097      * A component that defines the location before which a node should be copied or moved. This is similar to an {@link Into},
5098      * but it allows for placing a node at a particular location within the new destination, rather than always placing the moved
5099      * or copied node as the last child of the new parent.
5100      * 
5101      * @param <Next> The interface that is to be returned when this request is completed
5102      */
5103     public interface Before<Next> {
5104         /**
5105          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5106          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5107          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5108          * {@link To#to(Location)} instead.
5109          * 
5110          * @param parentLocation the location of the new parent
5111          * @return the interface for additional requests or actions
5112          * @see To#to(Location)
5113          */
5114         Next before( Location parentLocation );
5115 
5116         /**
5117          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5118          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5119          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5120          * {@link To#to(String)} instead.
5121          * 
5122          * @param parentPath the path of the new parent
5123          * @return the interface for additional requests or actions
5124          * @see To#to(String)
5125          */
5126         Next before( String parentPath );
5127 
5128         /**
5129          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5130          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5131          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5132          * {@link To#to(Path)} instead.
5133          * 
5134          * @param parentPath the path of the new parent
5135          * @return the interface for additional requests or actions
5136          * @see To#to(Path)
5137          */
5138         Next before( Path parentPath );
5139 
5140         /**
5141          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5142          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5143          * same-name-sibling index).
5144          * 
5145          * @param parentUuid the UUID of the new parent
5146          * @return the interface for additional requests or actions
5147          */
5148         Next before( UUID parentUuid );
5149 
5150         /**
5151          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5152          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5153          * same-name-sibling index).
5154          * 
5155          * @param parentIdProperty the property that uniquely identifies the new parent
5156          * @return the interface for additional requests or actions
5157          */
5158         Next before( Property parentIdProperty );
5159 
5160         /**
5161          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5162          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5163          * same-name-sibling index).
5164          * 
5165          * @param firstParentIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies
5166          *        the new parent
5167          * @param additionalParentIdProperties the additional properties that, with the <code>additionalIdProperties</code>,
5168          *        uniquely identifies the new parent
5169          * @return the interface for additional requests or actions
5170          */
5171         Next before( Property firstParentIdProperty,
5172                      Property... additionalParentIdProperties );
5173     }
5174 
5175     /**
5176      * A component that defines the name of a property to which a value should be added.
5177      * 
5178      * @param <Next> The interface that is to be returned when this request is completed
5179      */
5180     public interface ToName<Next> {
5181 
5182         public Next to( String name );
5183 
5184         public Next to( Name name );
5185 
5186     }
5187 
5188     /**
5189      * A component that defines the name of a property from which a value should be removed.
5190      * 
5191      * @param <Next> The interface that is to be returned when this request is completed
5192      */
5193     public interface FromName<Next> {
5194 
5195         public Next from( String name );
5196 
5197         public Next from( Name name );
5198 
5199     }
5200 
5201     /**
5202      * A component that defines the location to which a node should be copied or moved.
5203      * 
5204      * @param <Next> The interface that is to be returned when this request is completed
5205      */
5206     public interface To<Next> {
5207         /**
5208          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5209          * {@link Into#into(Location)}, which specifies the location of the parent and which assumes the new node should have the
5210          * same name as the original, this method allows the caller to specify a new name for the new node.
5211          * 
5212          * @param desiredLocation the desired location for the new node, which must have a {@link Location#getPath() path}
5213          * @return the interface for additional requests or actions
5214          * @see Into#into(Location)
5215          */
5216         Next to( Location desiredLocation );
5217 
5218         /**
5219          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5220          * {@link Into#into(String)}, which specifies the location of the parent and which assumes the new node should have the
5221          * same name as the original, this method allows the caller to specify a new name for the new node.
5222          * 
5223          * @param desiredPath the path for the new node
5224          * @return the interface for additional requests or actions
5225          * @see Into#into(String)
5226          */
5227         Next to( String desiredPath );
5228 
5229         /**
5230          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5231          * {@link Into#into(Path)} , which specifies the location of the parent and which assumes the new node should have the
5232          * same name as the original, this method allows the caller to specify a new name for the new node.
5233          * 
5234          * @param desiredPath the path for the new node
5235          * @return the interface for additional requests or actions
5236          * @see Into#into(Path)
5237          */
5238         Next to( Path desiredPath );
5239     }
5240 
5241     /**
5242      * A component that defines a new child name for a node.
5243      * 
5244      * @param <Next> The interface that is to be returned when this request is completed
5245      */
5246     public interface AsChild<Next> {
5247         /**
5248          * Finish the request by specifying the exact segment for the new child node. If no segment already exists, this request
5249          * should fail.
5250          * 
5251          * @param newSegment the new name
5252          * @return the interface for additional requests or actions
5253          */
5254         Next as( Path.Segment newSegment );
5255 
5256         /**
5257          * Finish the request by specifying the name of the new child node. This method indicates that the child should be added
5258          * as a new node with the given name at the end of the parents children
5259          * 
5260          * @param newName the new name
5261          * @return the interface for additional requests or actions
5262          */
5263         Next as( Name newName );
5264 
5265         /**
5266          * Finish the request by specifying the name of the new child node. This method indicates that the child should be added
5267          * as a new node with the given name at the end of the parents children
5268          * 
5269          * @param newName the new name
5270          * @return the interface for additional requests or actions
5271          */
5272         Next as( String newName );
5273     }
5274 
5275     /**
5276      * A component that defines a new name for a node.
5277      * 
5278      * @param <Next> The interface that is to be returned when this request is completed
5279      */
5280     public interface AsName<Next> {
5281         /**
5282          * Finish the request by specifying the new name.
5283          * 
5284          * @param newName the new name
5285          * @return the interface for additional requests or actions
5286          */
5287         Next as( String newName );
5288 
5289         /**
5290          * Finish the request by specifying the new name.
5291          * 
5292          * @param newName the new name
5293          * @return the interface for additional requests or actions
5294          */
5295         Next as( Name newName );
5296     }
5297 
5298     /**
5299      * A interface that is used to add more locations that are to be copied/moved.
5300      * 
5301      * @param <Next> The interface that is to be returned when this request is completed
5302      */
5303     public interface And<Next> {
5304         /**
5305          * Specify that another node should also be copied or moved.
5306          * 
5307          * @param from the location of the node to be copied or moved
5308          * @return the interface for finishing the request
5309          */
5310         Next and( Location from );
5311 
5312         /**
5313          * Specify that another node should also be copied or moved.
5314          * 
5315          * @param fromPath the path of the node to be copied or moved
5316          * @return the interface for finishing the request
5317          */
5318         Next and( String fromPath );
5319 
5320         /**
5321          * Specify that another node should also be copied or moved.
5322          * 
5323          * @param from the path of the node to be copied or moved
5324          * @return the interface for finishing the request
5325          */
5326         Next and( Path from );
5327 
5328         /**
5329          * Specify that another node should also be copied or moved.
5330          * 
5331          * @param from the UUID of the node to be copied or moved
5332          * @return the interface for finishing the request
5333          */
5334         Next and( UUID from );
5335 
5336         /**
5337          * Specify that another node should also be copied or moved.
5338          * 
5339          * @param idProperty the property that uniquely identifies the node to be copied or moved
5340          * @return the interface for finishing the request
5341          */
5342         Next and( Property idProperty );
5343 
5344         /**
5345          * Specify that another node should also be copied or moved.
5346          * 
5347          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5348          *        node to be copied or moved
5349          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5350          *        identifies the node to be copied or moved
5351          * @return the interface for finishing the request
5352          */
5353         Next and( Property firstIdProperty,
5354                   Property... additionalIdProperties );
5355 
5356         /**
5357          * Specify that another node should also be copied or moved.
5358          * 
5359          * @param idProperties the properties that uniquely identifies the node to be copied or moved
5360          * @return the interface for finishing the request
5361          */
5362         Next and( Iterable<Property> idProperties );
5363     }
5364 
5365     /**
5366      * The interface for defining additional nodes to be moved and the parent into which the node(s) are to be moved.
5367      * 
5368      * @param <Next> The interface that is to be returned when this request is completed
5369      */
5370     public interface Move<Next> extends AsName<Into<Next>>, Into<Next>, Before<Next>, And<Move<Next>> {
5371     }
5372 
5373     /**
5374      * The interface for defining additional nodes to be copied and the locations where the copy is to be placed. The
5375      * <code>to(...)</code> methods allow you to specify the location of the copy, including the name for the node that results
5376      * from the copy. Alternatively, you can use the <code>into(...)</code> methods to specify the parent location where the copy
5377      * is to be placed, which will assume the new copy will have the same name as the original.
5378      * 
5379      * @param <Next> The interface that is to be returned when this request is completed
5380      */
5381     public interface Copy<Next> extends FromWorkspace<CopyTarget<Next>>, CopyTarget<Next>, And<Copy<Next>> {
5382     }
5383 
5384     public interface CopyTarget<Next> extends To<Next>, Into<Next> {
5385     }
5386 
5387     /**
5388      * The interface for defining a branch of nodes to be cloned and the location where the clone is to be placed. Cloning a
5389      * branch differs from copying a branch in that:
5390      * <ol>
5391      * <li>Nodes can be copied within the same workspace or to another workspace; cloned nodes must be copied from one workspace
5392      * into another.</li>
5393      * <li>Copied nodes always get new UUIDs; cloned nodes always maintain their UUIDs and hence must define the behavior that
5394      * occurs if a node with the same UUID already exists in the new workspace.</li>
5395      * <li>Nodes can be copied to a specific name under a specific parent, but can only be added as a new child node at the end of
5396      * the new parent's children; nodes can be cloned to an exact location among the parent's children, replacing the existing
5397      * node at that location.</li>
5398      * </ol>
5399      * 
5400      * @param <Next>
5401      */
5402     public interface Clone<Next> extends FromWorkspace<AsChild<Into<WithUuids<Next>>>> {
5403 
5404     }
5405 
5406     /**
5407      * The interface for specifying that a node should come from a workspace other than the current workspace.
5408      * 
5409      * @param <Next> The interface that is to be returned when this request is completed
5410      */
5411     public interface FromWorkspace<Next> {
5412         Next fromWorkspace( String workspaceName );
5413     }
5414 
5415     /**
5416      * The interface for specifying how UUID conflicts should be handled.
5417      * 
5418      * @param <Next> The interface that is to be returned when this request is completed
5419      */
5420     public interface WithUuids<Next> {
5421         Next failingIfAnyUuidsMatch();
5422 
5423         Next replacingExistingNodesWithSameUuids();
5424     }
5425 
5426     /**
5427      * Interface for specifying whether a lock should be deep in scope
5428      * 
5429      * @param <Next> The interface that is to be returned when this create request is completed
5430      */
5431     public interface LockScope<Next> {
5432         Next andItsDescendants();
5433 
5434         Next only();
5435     }
5436 
5437     /**
5438      * Interface for specifying whether the maximum length of the lock
5439      * 
5440      * @param <Next> The interface that is to be returned when this create request is completed
5441      */
5442     public interface LockTimeout<Next> {
5443         Next withDefaultTimeout();
5444 
5445         Next withTimeoutOf( long milliseconds );
5446     }
5447 
5448     @NotThreadSafe
5449     protected abstract class LockAction<T> extends AbstractAction<T> implements LockScope<LockTimeout<T>> {
5450         private final Location target;
5451 
5452         /*package*/LockAction( T afterConjunction,
5453                                 Location target ) {
5454             super(afterConjunction);
5455             this.target = target;
5456         }
5457 
5458         protected abstract T submit( Location lock,
5459                                      org.modeshape.graph.request.LockBranchRequest.LockScope lockScope,
5460                                      long lockTimeoutInMillis );
5461 
5462         @SuppressWarnings( "synthetic-access" )
5463         public LockTimeout<T> andItsDescendants() {
5464             return new LockTimeout<T>() {
5465 
5466                 public T withDefaultTimeout() {
5467                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS, 0);
5468                 }
5469 
5470                 public T withTimeoutOf( long milliseconds ) {
5471                     return submit(target,
5472                                   org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS,
5473                                   milliseconds);
5474                 }
5475 
5476             };
5477         }
5478 
5479         @SuppressWarnings( "synthetic-access" )
5480         public LockTimeout<T> only() {
5481             return new LockTimeout<T>() {
5482 
5483                 public T withDefaultTimeout() {
5484                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_ONLY, 0);
5485                 }
5486 
5487                 public T withTimeoutOf( long milliseconds ) {
5488                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_ONLY, milliseconds);
5489                 }
5490 
5491             };
5492         }
5493 
5494     }
5495 
5496     /**
5497      * The interface for defining additional properties on a new node.
5498      * 
5499      * @param <Next> The interface that is to be returned when this create request is completed
5500      */
5501     public interface Create<Next> extends Conjunction<Next> {
5502         /**
5503          * Create the node only if there is no existing node with the same {@link Path.Segment#getName() name} (ignoring
5504          * {@link Path.Segment#getIndex() same-name-sibling indexes}).
5505          * 
5506          * @return this interface for continued specification of the request
5507          */
5508         Create<Next> ifAbsent();
5509 
5510         /**
5511          * Create the node if it does not exist, or update any existing node that has the same {@link Path.Segment#getName() name}
5512          * (ignoring {@link Path.Segment#getIndex() same-name-sibling indexes}).
5513          * 
5514          * @return this interface for continued specification of the request
5515          */
5516         Create<Next> orUpdate();
5517 
5518         /**
5519          * Create the node if it does not exist, or replace any existing node that has the same {@link Path.Segment#getName()
5520          * name} (ignoring {@link Path.Segment#getIndex() same-name-sibling indexes}).
5521          * 
5522          * @return this interface for continued specification of the request
5523          */
5524         Create<Next> orReplace();
5525 
5526         /**
5527          * Create the node if it does not exist by appending or adjusting the {@link Path.Segment#getIndex() same-name-sibling
5528          * index}). This is the default behavior.
5529          * 
5530          * @return this interface for continued specification of the request
5531          */
5532         Create<Next> byAppending();
5533 
5534         /**
5535          * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
5536          * 
5537          * @param uuid the UUID
5538          * @return this same interface so additional properties may be added
5539          */
5540         Create<Next> with( UUID uuid );
5541 
5542         /**
5543          * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
5544          * 
5545          * @param property the property
5546          * @return this same interface so additional properties may be added
5547          */
5548         Create<Next> with( Property property );
5549 
5550         /**
5551          * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
5552          * 
5553          * @param properties the properties that should be added
5554          * @return this same interface so additional properties may be added
5555          */
5556         Create<Next> with( Iterable<Property> properties );
5557 
5558         /**
5559          * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
5560          * 
5561          * @param propertyName the name of the property
5562          * @param values the property values
5563          * @return this same interface so additional properties may be added
5564          */
5565         Create<Next> with( String propertyName,
5566                            Object... values );
5567 
5568         /**
5569          * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
5570          * 
5571          * @param propertyName the name of the property
5572          * @param values the property values
5573          * @return this same interface so additional properties may be added
5574          */
5575         Create<Next> with( Name propertyName,
5576                            Object... values );
5577 
5578         /**
5579          * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
5580          * 
5581          * @param firstProperty the first property
5582          * @param additionalProperties the additional property
5583          * @return this same interface so additional properties may be added
5584          */
5585         Create<Next> with( Property firstProperty,
5586                            Property... additionalProperties );
5587 
5588         /**
5589          * Specify the UUID that should the new node should have.
5590          * 
5591          * @param uuid the UUID
5592          * @return this same interface so additional properties may be added
5593          */
5594         Create<Next> and( UUID uuid );
5595 
5596         /**
5597          * Specify a property that should the new node should have.
5598          * 
5599          * @param property the property
5600          * @return this same interface so additional properties may be added
5601          */
5602         Create<Next> and( Property property );
5603 
5604         /**
5605          * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
5606          * the properties in the supplied {@link Iterable}.
5607          * 
5608          * @param properties the properties that should be added
5609          * @return this same interface so additional properties may be added
5610          */
5611         Create<Next> and( Iterable<Property> properties );
5612 
5613         /**
5614          * Specify a property that should the new node should have.
5615          * 
5616          * @param propertyName the name of the property
5617          * @param values the property values
5618          * @return this same interface so additional properties may be added
5619          */
5620         Create<Next> and( String propertyName,
5621                           Object... values );
5622 
5623         /**
5624          * Specify a property that should the new node should have.
5625          * 
5626          * @param propertyName the name of the property
5627          * @param values the property values
5628          * @return this same interface so additional properties may be added
5629          */
5630         Create<Next> and( Name propertyName,
5631                           Object... values );
5632 
5633         /**
5634          * Specify properties that should the new node should have.
5635          * 
5636          * @param firstProperty the first property
5637          * @param additionalProperties the additional property
5638          * @return this same interface so additional properties may be added
5639          */
5640         Create<Next> and( Property firstProperty,
5641                           Property... additionalProperties );
5642     }
5643 
5644     /**
5645      * The interface for defining additional properties on a new node.
5646      * 
5647      * @param <Next> The interface that is to be returned when this create request is completed
5648      */
5649     public interface CreateAt<Next> extends Conjunction<Next> {
5650         /**
5651          * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
5652          * 
5653          * @param uuid the UUID
5654          * @return this same interface so additional properties may be added
5655          */
5656         CreateAt<Next> with( UUID uuid );
5657 
5658         /**
5659          * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
5660          * 
5661          * @param property the property
5662          * @return this same interface so additional properties may be added
5663          */
5664         CreateAt<Next> with( Property property );
5665 
5666         /**
5667          * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
5668          * 
5669          * @param properties the properties that should be added
5670          * @return this same interface so additional properties may be added
5671          */
5672         CreateAt<Next> with( Iterable<Property> properties );
5673 
5674         /**
5675          * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
5676          * 
5677          * @param propertyName the name of the property
5678          * @param values the property values
5679          * @return this same interface so additional properties may be added
5680          */
5681         CreateAt<Next> with( String propertyName,
5682                              Object... values );
5683 
5684         /**
5685          * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
5686          * 
5687          * @param propertyName the name of the property
5688          * @param values the property values
5689          * @return this same interface so additional properties may be added
5690          */
5691         CreateAt<Next> with( Name propertyName,
5692                              Object... values );
5693 
5694         /**
5695          * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
5696          * 
5697          * @param firstProperty the first property
5698          * @param additionalProperties the additional property
5699          * @return this same interface so additional properties may be added
5700          */
5701         CreateAt<Next> with( Property firstProperty,
5702                              Property... additionalProperties );
5703 
5704         /**
5705          * Specify the UUID that should the new node should have.
5706          * 
5707          * @param uuid the UUID
5708          * @return this same interface so additional properties may be added
5709          */
5710         CreateAt<Next> and( UUID uuid );
5711 
5712         /**
5713          * Specify a property that should the new node should have.
5714          * 
5715          * @param property the property
5716          * @return this same interface so additional properties may be added
5717          */
5718         CreateAt<Next> and( Property property );
5719 
5720         /**
5721          * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
5722          * the properties in the supplied {@link Iterable}.
5723          * 
5724          * @param properties the properties that should be added
5725          * @return this same interface so additional properties may be added
5726          */
5727         CreateAt<Next> and( Iterable<Property> properties );
5728 
5729         /**
5730          * Specify a property that should the new node should have.
5731          * 
5732          * @param propertyName the name of the property
5733          * @param values the property values
5734          * @return this same interface so additional properties may be added
5735          */
5736         CreateAt<Next> and( String propertyName,
5737                             Object... values );
5738 
5739         /**
5740          * Specify a property that should the new node should have.
5741          * 
5742          * @param propertyName the name of the property
5743          * @param values the property values
5744          * @return this same interface so additional properties may be added
5745          */
5746         CreateAt<Next> and( Name propertyName,
5747                             Object... values );
5748 
5749         /**
5750          * Specify properties that should the new node should have.
5751          * 
5752          * @param firstProperty the first property
5753          * @param additionalProperties the additional property
5754          * @return this same interface so additional properties may be added
5755          */
5756         CreateAt<Next> and( Property firstProperty,
5757                             Property... additionalProperties );
5758 
5759         /**
5760          * Complete this request, submit it, and return the actual location of the created node.
5761          * 
5762          * @return the actual location of the just-created node; never null
5763          */
5764         Location getLocation();
5765 
5766         /**
5767          * Complete this request, submit it, and return the actual node that was created.
5768          * 
5769          * @return the actual node that was just created; never null
5770          */
5771         Node getNode();
5772     }
5773 
5774     /**
5775      * The interface for defining the node upon which a request operates.
5776      * 
5777      * @param <Next> The interface that is to be returned when the request is completed
5778      */
5779     public interface On<Next> {
5780         /**
5781          * Specify the location of the node upon which the request is to operate.
5782          * 
5783          * @param to the location of the new parent
5784          * @return the interface for additional requests or actions
5785          */
5786         Next on( Location to );
5787 
5788         /**
5789          * Specify the path of the node upon which the request is to operate.
5790          * 
5791          * @param toPath the path of the new parent
5792          * @return the interface for additional requests or actions
5793          */
5794         Next on( String toPath );
5795 
5796         /**
5797          * Specify the path of the node upon which the request is to operate.
5798          * 
5799          * @param to the path of the new parent
5800          * @return the interface for additional requests or actions
5801          */
5802         Next on( Path to );
5803 
5804         /**
5805          * Specify the UUID of the node upon which the request is to operate.
5806          * 
5807          * @param to the UUID of the new parent
5808          * @return the interface for additional requests or actions
5809          */
5810         Next on( UUID to );
5811 
5812         /**
5813          * Specify the unique identification property that identifies the node upon which the request is to operate.
5814          * 
5815          * @param idProperty the property that uniquely identifies the new parent
5816          * @return the interface for additional requests or actions
5817          */
5818         Next on( Property idProperty );
5819 
5820         /**
5821          * Specify the unique identification properties that identify the node upon which the request is to operate.
5822          * 
5823          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5824          *        new parent
5825          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5826          *        identifies the new parent
5827          * @return the interface for additional requests or actions
5828          */
5829         Next on( Property firstIdProperty,
5830                  Property... additionalIdProperties );
5831 
5832         /**
5833          * Specify the unique identification properties that identify the node upon which the request is to operate.
5834          * 
5835          * @param idProperties the properties that uniquely identifies the new parent
5836          * @return the interface for additional requests or actions
5837          */
5838         Next on( Iterable<Property> idProperties );
5839     }
5840 
5841     /**
5842      * The interface for defining the node upon which a request operates, including a method that accepts multiple locations.
5843      * 
5844      * @param <Next> The interface that is to be returned when the request is completed
5845      */
5846     public interface OnMultiple<Next> extends On<Next> {
5847         /**
5848          * Specify the location of each node upon which the requests are to operate.
5849          * 
5850          * @param to the locations
5851          * @return the interface for additional requests or actions
5852          */
5853         Map<Location, Next> on( Collection<Location> to );
5854 
5855         /**
5856          * Specify the location of each node upon which the requests are to operate.
5857          * 
5858          * @param firstTo the first location
5859          * @param additional the additional location
5860          * @return the interface for additional requests or actions
5861          */
5862         Map<Location, Next> on( Location firstTo,
5863                                 Location... additional );
5864 
5865         /**
5866          * Specify the path of each node upon which the requests are to operate.
5867          * 
5868          * @param firstPath the first path
5869          * @param additional the additional path
5870          * @return the interface for additional requests or actions
5871          */
5872         Map<Location, Next> on( String firstPath,
5873                                 String... additional );
5874 
5875         /**
5876          * Specify the path of each node upon which the requests are to operate.
5877          * 
5878          * @param firstPath the first path
5879          * @param additional the additional path
5880          * @return the interface for additional requests or actions
5881          */
5882         Map<Location, Next> on( Path firstPath,
5883                                 Path... additional );
5884 
5885         /**
5886          * Specify the UUID of each node upon which the requests are to operate.
5887          * 
5888          * @param firstPath the first UUID of the node
5889          * @param additional the additional UUIDs
5890          * @return the interface for additional requests or actions
5891          */
5892         Map<Location, Next> on( UUID firstPath,
5893                                 UUID... additional );
5894     }
5895 
5896     /**
5897      * The interface for defining the node upon which a request operates.
5898      * 
5899      * @param <Next> The interface that is to be returned when the request is completed
5900      */
5901     public interface Of<Next> {
5902         /**
5903          * Specify the location of the node upon which the request is to operate.
5904          * 
5905          * @param to the location of the new parent
5906          * @return the interface for additional requests or actions
5907          */
5908         Next of( Location to );
5909 
5910         /**
5911          * Specify the path of the node upon which the request is to operate.
5912          * 
5913          * @param toPath the path of the new parent
5914          * @return the interface for additional requests or actions
5915          */
5916         Next of( String toPath );
5917 
5918         /**
5919          * Specify the path of the node upon which the request is to operate.
5920          * 
5921          * @param to the path of the new parent
5922          * @return the interface for additional requests or actions
5923          */
5924         Next of( Path to );
5925 
5926         /**
5927          * Specify the UUID of the node upon which the request is to operate.
5928          * 
5929          * @param to the UUID of the new parent
5930          * @return the interface for additional requests or actions
5931          */
5932         Next of( UUID to );
5933 
5934         /**
5935          * Specify the unique identification property that identifies the node upon which the request is to operate.
5936          * 
5937          * @param idProperty the property that uniquely identifies the new parent
5938          * @return the interface for additional requests or actions
5939          */
5940         Next of( Property idProperty );
5941 
5942         /**
5943          * Specify the unique identification properties that identify the node upon which the request is to operate.
5944          * 
5945          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5946          *        new parent
5947          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5948          *        identifies the new parent
5949          * @return the interface for additional requests or actions
5950          */
5951         Next of( Property firstIdProperty,
5952                  Property... additionalIdProperties );
5953 
5954         /**
5955          * Specify the unique identification properties that identify the node upon which the request is to operate.
5956          * 
5957          * @param idProperties the properties that uniquely identifies the new parent
5958          * @return the interface for additional requests or actions
5959          */
5960         Next of( Iterable<Property> idProperties );
5961     }
5962 
5963     /**
5964      * The interface for defining the node upon which which a request operates.
5965      * 
5966      * @param <Next> The interface that is to be returned when the request is completed
5967      */
5968     public interface At<Next> {
5969         /**
5970          * Specify the location of the node upon which the request is to operate.
5971          * 
5972          * @param to the location of the new parent
5973          * @return the interface for additional requests or actions
5974          */
5975         Next at( Location to );
5976 
5977         /**
5978          * Specify the path of the node upon which the request is to operate.
5979          * 
5980          * @param toPath the path of the new parent
5981          * @return the interface for additional requests or actions
5982          */
5983         Next at( String toPath );
5984 
5985         /**
5986          * Specify the path of the node upon which the request is to operate.
5987          * 
5988          * @param to the path of the new parent
5989          * @return the interface for additional requests or actions
5990          */
5991         Next at( Path to );
5992 
5993         /**
5994          * Specify the UUID of the node upon which the request is to operate.
5995          * 
5996          * @param to the UUID of the new parent
5997          * @return the interface for additional requests or actions
5998          */
5999         Next at( UUID to );
6000 
6001         /**
6002          * Specify the unique identification property that identifies the node upon which the request is to operate.
6003          * 
6004          * @param idProperty the property that uniquely identifies the new parent
6005          * @return the interface for additional requests or actions
6006          */
6007         Next at( Property idProperty );
6008 
6009         /**
6010          * Specify the unique identification properties that identify the node upon which the request is to operate.
6011          * 
6012          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
6013          *        new parent
6014          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
6015          *        identifies the new parent
6016          * @return the interface for additional requests or actions
6017          */
6018         Next at( Property firstIdProperty,
6019                  Property... additionalIdProperties );
6020 
6021         /**
6022          * Specify the unique identification properties that identify the node upon which the request is to operate.
6023          * 
6024          * @param idProperties the properties that uniquely identifies the new parent
6025          * @return the interface for additional requests or actions
6026          */
6027         Next at( Iterable<Property> idProperties );
6028     }
6029 
6030     /**
6031      * A component used to supply the details for getting children of another node. If all of the children are to be obtained,
6032      * then the parent can be specified using one of the <code>of(...)</code> methods on this component. If, however, only some of
6033      * the nodes are to be returned (e.g., a "block" of children), then specify the {@link #inBlockOf(int) block size} followed by
6034      * the {@link BlockOfChildren block size and parent}.
6035      * 
6036      * @param <Next>
6037      */
6038     public interface Children<Next> extends Of<Next> {
6039         /**
6040          * Specify that a block of children are to be retreived, and in particular the number of children that are to be returned.
6041          * 
6042          * @param blockSize the number of children that are to be retrieved in the block; must be positive
6043          * @return the interface used to specify the starting point for the block and the parent
6044          */
6045         BlockOfChildren<Next> inBlockOf( int blockSize );
6046     }
6047 
6048     /**
6049      * A component used to specify a block of children starting either {@link #startingAt(int) at a particular index} or
6050      * {@link #startingAfter(Location) after a previous sibling}.
6051      * 
6052      * @param <Next>
6053      */
6054     public interface BlockOfChildren<Next> {
6055         /**
6056          * Specify the block of children is to start at the supplied index.
6057          * 
6058          * @param startingIndex the zero-based index of the first child to be returned in the block
6059          * @return interface used to specify the parent of the children; never null
6060          */
6061         Under<Next> startingAt( int startingIndex );
6062 
6063         /**
6064          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6065          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6066          * block.
6067          * 
6068          * @param previousSibling the location of the sibling node that is before the first node in the block
6069          * @return the children; never null
6070          */
6071         Next startingAfter( Location previousSibling );
6072 
6073         /**
6074          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6075          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6076          * block.
6077          * 
6078          * @param pathToPreviousSiblingName the path of the sibling node that is before the first node in the block
6079          * @return the children; never null
6080          */
6081         Next startingAfter( String pathToPreviousSiblingName );
6082 
6083         /**
6084          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6085          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6086          * block.
6087          * 
6088          * @param previousSibling the path of the sibling node that is before the first node in the block
6089          * @return the children; never null
6090          */
6091         Next startingAfter( Path previousSibling );
6092 
6093         /**
6094          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6095          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6096          * block.
6097          * 
6098          * @param previousSiblingUuid the UUID of the sibling node that is before the first node in the block
6099          * @return the children; never null
6100          */
6101         Next startingAfter( UUID previousSiblingUuid );
6102 
6103         /**
6104          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6105          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6106          * block.
6107          * 
6108          * @param idPropertyOfPreviousSibling the property that uniquely identifies the previous sibling
6109          * @return the children; never null
6110          */
6111         Next startingAfter( Property idPropertyOfPreviousSibling );
6112 
6113         /**
6114          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6115          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6116          * block.
6117          * 
6118          * @param firstIdPropertyOfPreviousSibling the first property that, with the <code>additionalIdProperties</code>, uniquely
6119          *        identifies the previous sibling
6120          * @param additionalIdPropertiesOfPreviousSibling the additional properties that, with the
6121          *        <code>additionalIdProperties</code>, uniquely identifies the previous sibling
6122          * @return the children; never null
6123          */
6124         Next startingAfter( Property firstIdPropertyOfPreviousSibling,
6125                             Property... additionalIdPropertiesOfPreviousSibling );
6126     }
6127 
6128     /**
6129      * The interface for defining the node under which which a request operates.
6130      * 
6131      * @param <Next> The interface that is to be returned when the request is completed
6132      */
6133     public interface Under<Next> {
6134         /**
6135          * Specify the location of the node under which the request is to operate.
6136          * 
6137          * @param to the location of the new parent
6138          * @return the interface for additional requests or actions
6139          */
6140         Next under( Location to );
6141 
6142         /**
6143          * Specify the path of the node under which the request is to operate.
6144          * 
6145          * @param toPath the path of the new parent
6146          * @return the interface for additional requests or actions
6147          */
6148         Next under( String toPath );
6149 
6150         /**
6151          * Specify the path of the node under which the request is to operate.
6152          * 
6153          * @param to the path of the new parent
6154          * @return the interface for additional requests or actions
6155          */
6156         Next under( Path to );
6157 
6158         /**
6159          * Specify the UUID of the node under which the request is to operate.
6160          * 
6161          * @param to the UUID of the new parent
6162          * @return the interface for additional requests or actions
6163          */
6164         Next under( UUID to );
6165 
6166         /**
6167          * Specify the unique identification property that identifies the node under which the request is to operate.
6168          * 
6169          * @param idProperty the property that uniquely identifies the new parent
6170          * @return the interface for additional requests or actions
6171          */
6172         Next under( Property idProperty );
6173 
6174         /**
6175          * Specify the unique identification properties that identify the node under which the request is to operate.
6176          * 
6177          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
6178          *        new parent
6179          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
6180          *        identifies the new parent
6181          * @return the interface for additional requests or actions
6182          */
6183         Next under( Property firstIdProperty,
6184                     Property... additionalIdProperties );
6185     }
6186 
6187     /**
6188      * The interface for defining the node on which an {@link Graph#addValue(Object)} operation applies and what additional values
6189      * (if any) should be added.
6190      * 
6191      * @param <Next> The interface that is to be returned when the request is completed
6192      */
6193     public interface AddValue<Next> extends ToName<On<Next>> {
6194         /**
6195          * Specifies an additional value to be added
6196          * 
6197          * @param value the value to be added
6198          * @return an object that allows additional values to be specified for removal or for their location to be specified
6199          */
6200         AddValue<Next> andValue( Object value );
6201 
6202     }
6203 
6204     /**
6205      * The interface for defining the node on which an {@link Graph#removeValue(Object)} operation applies and what additional
6206      * values (if any) should be removed.
6207      * 
6208      * @param <Next> The interface that is to be returned when the request is completed
6209      */
6210     public interface RemoveValue<Next> extends FromName<On<Next>> {
6211 
6212         /**
6213          * Specifies an additional value to be removed
6214          * 
6215          * @param value the value to be removed
6216          * @return an object that allows additional values to be specified for removal or for their location to be specified
6217          */
6218         RemoveValue<Next> andValue( Object value );
6219 
6220     }
6221 
6222     public abstract class AddValueAction<T> extends AbstractAction<T> implements AddValue<T> {
6223 
6224         protected final String workspaceName;
6225         protected final List<Object> values = new LinkedList<Object>();
6226 
6227         protected AddValueAction( T afterConjunction,
6228                                   String workspaceName,
6229                                   Object firstValue ) {
6230             super(afterConjunction);
6231 
6232             this.workspaceName = workspaceName;
6233             this.values.add(firstValue);
6234         }
6235 
6236         public AddValue<T> andValue( Object nextValue ) {
6237             this.values.add(nextValue);
6238             return this;
6239         }
6240 
6241         public On<T> to( String name ) {
6242             return to(createName(name));
6243         }
6244 
6245         public On<T> to( final Name name ) {
6246             return new On<T>() {
6247 
6248                 public T on( Iterable<Property> idProperties ) {
6249                     return on(Location.create(idProperties));
6250                 }
6251 
6252                 public T on( Location to ) {
6253                     return submit(workspaceName, to, name, values);
6254                 }
6255 
6256                 public T on( Path to ) {
6257                     return on(Location.create(to));
6258                 }
6259 
6260                 public T on( Property firstIdProperty,
6261                              Property... additionalIdProperties ) {
6262                     return on(Location.create(firstIdProperty, additionalIdProperties));
6263                 }
6264 
6265                 public T on( Property idProperty ) {
6266                     return on(Location.create(idProperty));
6267                 }
6268 
6269                 public T on( String toPath ) {
6270                     return on(Location.create(createPath(toPath)));
6271                 }
6272 
6273                 public T on( UUID to ) {
6274                     return on(Location.create(to));
6275                 }
6276             };
6277         }
6278 
6279         protected abstract T submit( String workspaceName,
6280                                      Location on,
6281                                      Name property,
6282                                      List<Object> values );
6283 
6284     }
6285 
6286     public abstract class RemoveValueAction<T> extends AbstractAction<T> implements RemoveValue<T> {
6287 
6288         protected final String workspaceName;
6289         protected final List<Object> values = new LinkedList<Object>();
6290 
6291         protected RemoveValueAction( T afterConjunction,
6292                                      String workspaceName,
6293                                      Object firstValue ) {
6294             super(afterConjunction);
6295 
6296             this.workspaceName = workspaceName;
6297             this.values.add(firstValue);
6298         }
6299 
6300         public RemoveValue<T> andValue( Object nextValue ) {
6301             this.values.add(nextValue);
6302             return this;
6303         }
6304 
6305         public On<T> from( String name ) {
6306             return from(createName(name));
6307         }
6308 
6309         public On<T> from( final Name name ) {
6310             return new On<T>() {
6311 
6312                 public T on( Iterable<Property> idProperties ) {
6313                     return on(Location.create(idProperties));
6314                 }
6315 
6316                 public T on( Location to ) {
6317                     return submit(workspaceName, to, name, values);
6318                 }
6319 
6320                 public T on( Path to ) {
6321                     return on(Location.create(to));
6322                 }
6323 
6324                 public T on( Property firstIdProperty,
6325                              Property... additionalIdProperties ) {
6326                     return on(Location.create(firstIdProperty, additionalIdProperties));
6327                 }
6328 
6329                 public T on( Property idProperty ) {
6330                     return on(Location.create(idProperty));
6331                 }
6332 
6333                 public T on( String toPath ) {
6334                     return on(Location.create(createPath(toPath)));
6335                 }
6336 
6337                 public T on( UUID to ) {
6338                     return on(Location.create(to));
6339                 }
6340             };
6341         }
6342 
6343         protected abstract T submit( String workspaceName,
6344                                      Location on,
6345                                      Name property,
6346                                      List<Object> values );
6347 
6348     }
6349 
6350     /**
6351      * A component used to set the values on a property.
6352      * 
6353      * @param <Next> the next command
6354      */
6355     public interface SetValues<Next> extends On<SetValuesTo<Next>>, SetValuesTo<On<Next>> {
6356     }
6357 
6358     /**
6359      * A component used to set the values on a property.
6360      * 
6361      * @param <Next>
6362      */
6363     public interface SetValuesTo<Next> {
6364 
6365         /**
6366          * 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
6367          * {@link Location#getUuid() UUID}.
6368          * 
6369          * @param node the node to which a reference should be set
6370          * @return the interface for additional requests or actions
6371          * @throws IllegalArgumentException if the value is a Node that has no {@link Location#getUuid() UUID}
6372          */
6373         Next to( Node node );
6374 
6375         /**
6376          * Set the property value to be a reference to the given location. Note that it is an error if the Location does not have
6377          * a {@link Location#getUuid() UUID}.
6378          * 
6379          * @param location the location to which a reference should be set
6380          * @return the interface for additional requests or actions
6381          * @throws IllegalArgumentException if the value is a Location that has no {@link Location#getUuid() UUID}
6382          */
6383         Next to( Location location );
6384 
6385         /**
6386          * Set the property value to the given string.
6387          * 
6388          * @param value the property value
6389          * @return the interface for additional requests or actions
6390          */
6391         Next to( String value );
6392 
6393         /**
6394          * Set the property value to the given integer value.
6395          * 
6396          * @param value the property value
6397          * @return the interface for additional requests or actions
6398          */
6399         Next to( int value );
6400 
6401         /**
6402          * Set the property value to the given long value.
6403          * 
6404          * @param value the property value
6405          * @return the interface for additional requests or actions
6406          */
6407         Next to( long value );
6408 
6409         /**
6410          * Set the property value to the given boolean value.
6411          * 
6412          * @param value the property value
6413          * @return the interface for additional requests or actions
6414          */
6415         Next to( boolean value );
6416 
6417         /**
6418          * Set the property value to the given float value.
6419          * 
6420          * @param value the property value
6421          * @return the interface for additional requests or actions
6422          */
6423         Next to( float value );
6424 
6425         /**
6426          * Set the property value to the given double value.
6427          * 
6428          * @param value the property value
6429          * @return the interface for additional requests or actions
6430          */
6431         Next to( double value );
6432 
6433         /**
6434          * Set the property value to the given decimal value.
6435          * 
6436          * @param value the property value
6437          * @return the interface for additional requests or actions
6438          */
6439         Next to( BigDecimal value );
6440 
6441         /**
6442          * Set the property value to the date given by the supplied calendar.
6443          * 
6444          * @param value the property value
6445          * @return the interface for additional requests or actions
6446          */
6447         Next to( Calendar value );
6448 
6449         /**
6450          * Set the property value to the given date.
6451          * 
6452          * @param value the property value
6453          * @return the interface for additional requests or actions
6454          */
6455         Next to( Date value );
6456 
6457         /**
6458          * Set the property value to the given date-time instant.
6459          * 
6460          * @param value the property value
6461          * @return the interface for additional requests or actions
6462          */
6463         Next to( DateTime value );
6464 
6465         /**
6466          * Set the property value to the given Name.
6467          * 
6468          * @param value the property value
6469          * @return the interface for additional requests or actions
6470          */
6471         Next to( Name value );
6472 
6473         /**
6474          * Set the property value to the given Path.
6475          * 
6476          * @param value the property value
6477          * @return the interface for additional requests or actions
6478          */
6479         Next to( Path value );
6480 
6481         /**
6482          * Set the property value to the given Reference. See also {@link #to(Node)}.
6483          * 
6484          * @param value the property value
6485          * @return the interface for additional requests or actions
6486          */
6487         Next to( Reference value );
6488 
6489         /**
6490          * Set the property value to the given URI.
6491          * 
6492          * @param value the property value
6493          * @return the interface for additional requests or actions
6494          */
6495         Next to( URI value );
6496 
6497         /**
6498          * Set the property value to the given UUID.
6499          * 
6500          * @param value the property value
6501          * @return the interface for additional requests or actions
6502          */
6503         Next to( UUID value );
6504 
6505         /**
6506          * Set the property value to the given binary value.
6507          * 
6508          * @param value the property value
6509          * @return the interface for additional requests or actions
6510          */
6511         Next to( Binary value );
6512 
6513         /**
6514          * Set the property value to the given byte array.
6515          * 
6516          * @param value the property value
6517          * @return the interface for additional requests or actions
6518          */
6519         Next to( byte[] value );
6520 
6521         /**
6522          * Set the property value to the given string.
6523          * 
6524          * @param stream the stream containing the content to be used for the property value
6525          * @param approximateLength the approximate length of the content (in bytes)
6526          * @return the interface for additional requests or actions
6527          */
6528         Next to( InputStream stream,
6529                  long approximateLength );
6530 
6531         /**
6532          * Set the property value to the given string.
6533          * 
6534          * @param reader the reader containing the content to be used for the property value
6535          * @param approximateLength the approximate length of the content (in bytes)
6536          * @return the interface for additional requests or actions
6537          */
6538         Next to( Reader reader,
6539                  long approximateLength );
6540 
6541         /**
6542          * Set the property value to the given object. The supplied <code>value</code> should be a valid property value, or a
6543          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6544          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6545          * 
6546          * @param value the property value
6547          * @return the interface for additional requests or actions
6548          * @throws IllegalArgumentException if the value is a Node or Location that has no {@link Location#getUuid() UUID}
6549          */
6550         Next to( Object value );
6551 
6552         /**
6553          * Set the property values to the given object. Each of the supplied <code>values</code> should be a valid property value,
6554          * or a {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note
6555          * that it is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6556          * 
6557          * @param values the property values
6558          * @return the interface for additional requests or actions
6559          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6560          *         UUID}
6561          */
6562         Next to( Object[] values );
6563 
6564         /**
6565          * Set the property value to the given objects. Each of the supplied values should be a valid property value, or a
6566          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6567          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6568          * 
6569          * @param firstValue the first property value
6570          * @param otherValues the remaining property values
6571          * @return the interface for additional requests or actions
6572          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6573          *         UUID}
6574          */
6575         Next to( Object firstValue,
6576                  Object... otherValues );
6577 
6578         /**
6579          * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
6580          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6581          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6582          * 
6583          * @param values the container for the property values
6584          * @return the interface for additional requests or actions
6585          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6586          *         UUID}
6587          */
6588         Next to( Iterable<?> values );
6589 
6590         /**
6591          * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
6592          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6593          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6594          * 
6595          * @param values the iterator over the property values
6596          * @return the interface for additional requests or actions
6597          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6598          *         UUID}
6599          */
6600         Next to( Iterator<?> values );
6601     }
6602 
6603     /**
6604      * A component that defines a node that is to be created.
6605      * 
6606      * @param <Next> The interface that is to be returned to complete the create request
6607      */
6608     public interface CreateNode<Next> {
6609         /**
6610          * Specify the name of the node that is to be created.
6611          * 
6612          * @param nodeName the name of the new node
6613          * @param properties the properties for the new node
6614          * @return the next component for making additional requests.
6615          */
6616         Next node( String nodeName,
6617                    Property... properties );
6618 
6619         /**
6620          * Specify the name of the node that is to be created.
6621          * 
6622          * @param nodeName the name of the new node
6623          * @param properties the properties for the new node
6624          * @return the next component for making additional requests.
6625          */
6626         Next node( String nodeName,
6627                    Iterator<Property> properties );
6628 
6629         /**
6630          * Specify the name of the node that is to be created.
6631          * 
6632          * @param nodeName the name of the new node
6633          * @param properties the properties for the new node
6634          * @return the next component for making additional requests.
6635          */
6636         Next node( String nodeName,
6637                    Iterable<Property> properties );
6638     }
6639 
6640     /**
6641      * A component that defines a node that is to be created.
6642      * 
6643      * @param <Next> The interface that is to be returned to complete the create request
6644      */
6645     public interface CreateNodeNamed<Next> {
6646         /**
6647          * Specify the name of the node that is to be created.
6648          * 
6649          * @param nodeName the name of the new node
6650          * @return the interface used to complete the request
6651          */
6652         Create<Next> nodeNamed( String nodeName );
6653 
6654         /**
6655          * Specify the name of the node that is to be created.
6656          * 
6657          * @param nodeName the name of the new node
6658          * @return the interface used to complete the request
6659          */
6660         Create<Next> nodeNamed( Name nodeName );
6661     }
6662 
6663     /**
6664      * A component that defines the location into which a node should be copied or moved.
6665      * 
6666      * @param <Next> The interface that is to be returned when this request is completed
6667      */
6668     public interface ImportInto<Next> {
6669 
6670         /**
6671          * Specify the name of the XML attribute that should be used as the node name. If this is not specified (or the attribute
6672          * name is null) the importer will look for the "jcr:name" attribute.
6673          * 
6674          * @param nameAttribute the name of the XML attribute containing the name for each node
6675          * @return the interface used to specify the location where the content should be placed
6676          */
6677         ImportInto<Next> usingAttributeForName( String nameAttribute );
6678 
6679         /**
6680          * Specify the name of the XML attribute that should be used as the node name. If this is not specified (or the attribute
6681          * name is null) the importer will look for the "jcr:name" attribute.
6682          * 
6683          * @param nameAttribute the name of the XML attribute containing the name for each node
6684          * @return the interface used to specify the location where the content should be placed
6685          */
6686         ImportInto<Next> usingAttributeForName( Name nameAttribute );
6687 
6688         /**
6689          * Specify the name of the XML attribute that should be used as the node's type. If this is not specified (or the
6690          * attribute name is null) the importer will look for the "jcr:primaryType" attribute.
6691          * 
6692          * @param typeAttribute the name of the XML attribute containing the type for each node
6693          * @return the interface used to specify the location where the content should be placed
6694          */
6695         ImportInto<Next> usingAttributeForType( String typeAttribute );
6696 
6697         /**
6698          * Specify the name of the XML attribute that should be used as the node's type. If this is not specified (or the
6699          * attribute name is null) the importer will look for the "jcr:primaryType" attribute.
6700          * 
6701          * @param typeAttribute the name of the XML attribute containing the type for each node
6702          * @return the interface used to specify the location where the content should be placed
6703          */
6704         ImportInto<Next> usingAttributeForType( Name typeAttribute );
6705 
6706         /**
6707          * Specify whether the root element in the XML document should be skipped (that is, not be represented by a node). By
6708          * default, the root element is not skipped.
6709          * 
6710          * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
6711          * @return the interface used to specify the location where the content should be placed
6712          */
6713         ImportInto<Next> skippingRootElement( boolean skip );
6714 
6715         /**
6716          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6717          * 
6718          * @param to the location of the new parent
6719          * @return the interface for additional requests or actions
6720          * @throws IOException if there is a problem reading the content being imported
6721          * @throws SAXException if there is a problem with the SAX Parser
6722          */
6723         Next into( Location to ) throws IOException, SAXException;
6724 
6725         /**
6726          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6727          * 
6728          * @param toPath the path of the new parent
6729          * @return the interface for additional requests or actions
6730          * @throws IOException if there is a problem reading the content being imported
6731          * @throws SAXException if there is a problem with the SAX Parser
6732          */
6733         Next into( String toPath ) throws IOException, SAXException;
6734 
6735         /**
6736          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6737          * 
6738          * @param to the path of the new parent
6739          * @return the interface for additional requests or actions
6740          * @throws IOException if there is a problem reading the content being imported
6741          * @throws SAXException if there is a problem with the SAX Parser
6742          */
6743         Next into( Path to ) throws IOException, SAXException;
6744 
6745         /**
6746          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6747          * 
6748          * @param to the UUID of the new parent
6749          * @return the interface for additional requests or actions
6750          * @throws IOException if there is a problem reading the content being imported
6751          * @throws SAXException if there is a problem with the SAX Parser
6752          */
6753         Next into( UUID to ) throws IOException, SAXException;
6754 
6755         /**
6756          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6757          * 
6758          * @param idProperty the property that uniquely identifies the new parent
6759          * @return the interface for additional requests or actions
6760          * @throws IOException if there is a problem reading the content being imported
6761          * @throws SAXException if there is a problem with the SAX Parser
6762          */
6763         Next into( Property idProperty ) throws IOException, SAXException;
6764 
6765         /**
6766          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6767          * 
6768          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
6769          *        new parent
6770          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
6771          *        identifies the new parent
6772          * @return the interface for additional requests or actions
6773          * @throws IOException if there is a problem reading the content being imported
6774          * @throws SAXException if there is a problem with the SAX Parser
6775          */
6776         Next into( Property firstIdProperty,
6777                    Property... additionalIdProperties ) throws IOException, SAXException;
6778 
6779         /**
6780          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6781          * 
6782          * @param idProperties the properties that uniquely identifies the new parent
6783          * @return the interface for additional requests or actions
6784          * @throws IOException if there is a problem reading the content being imported
6785          * @throws SAXException if there is a problem with the SAX Parser
6786          */
6787         Next into( Iterable<Property> idProperties ) throws IOException, SAXException;
6788     }
6789 
6790     public interface BatchConjunction extends Conjunction<Batch>, Executable<Node> {
6791     }
6792 
6793     public interface GetNodeConjunction<Next> extends Conjunction<Next> {
6794         Node andReturn();
6795     }
6796 
6797     // ----------------------------------------------------------------------------------------------------------------
6798     // Node Implementation
6799     // ----------------------------------------------------------------------------------------------------------------
6800     @Immutable
6801     protected class GraphNode implements Node {
6802         private final ReadNodeRequest request;
6803 
6804         /*package*/GraphNode( ReadNodeRequest request ) {
6805             this.request = request;
6806         }
6807 
6808         public Location getLocation() {
6809             return request.getActualLocationOfNode();
6810         }
6811 
6812         public DateTime getExpirationTime() {
6813             CachePolicy policy = request.getCachePolicy();
6814             return policy == null ? null : request.getTimeLoaded().plus(policy.getTimeToLive(), TimeUnit.MILLISECONDS);
6815         }
6816 
6817         public Graph getGraph() {
6818             return Graph.this;
6819         }
6820 
6821         public Collection<Property> getProperties() {
6822             return request.getProperties();
6823         }
6824 
6825         public Property getProperty( Name name ) {
6826             return getPropertiesByName().get(name);
6827         }
6828 
6829         public Property getProperty( String nameStr ) {
6830             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
6831             return getPropertiesByName().get(name);
6832         }
6833 
6834         public Map<Name, Property> getPropertiesByName() {
6835             return request.getPropertiesByName();
6836         }
6837 
6838         public List<Location> getChildren() {
6839             return request.getChildren();
6840         }
6841 
6842         public List<Location> getChildren( Name namePattern ) {
6843             List<Location> result = new ArrayList<Location>();
6844             for (Location child : getChildren()) {
6845                 if (child.getPath().endsWith(namePattern)) result.add(child);
6846             }
6847             return result;
6848         }
6849 
6850         public boolean hasChildren() {
6851             return request.getChildren().size() > 0;
6852         }
6853 
6854         public List<Segment> getChildrenSegments() {
6855             return getSegments(getChildren());
6856         }
6857 
6858         public Iterator<Location> iterator() {
6859             return request.getChildren().iterator();
6860         }
6861 
6862         @Override
6863         public int hashCode() {
6864             return getLocation().hashCode();
6865         }
6866 
6867         @Override
6868         public boolean equals( Object obj ) {
6869             if (obj instanceof Node) {
6870                 Node that = (Node)obj;
6871                 return this.getLocation().isSame(that.getLocation());
6872             }
6873             return false;
6874         }
6875 
6876         @Override
6877         public String toString() {
6878             return "Node " + getLocation().toString();
6879         }
6880     }
6881 
6882     // ----------------------------------------------------------------------------------------------------------------
6883     // Results implementation for the batched requests
6884     // ----------------------------------------------------------------------------------------------------------------
6885     @Immutable
6886     class BatchResults implements Results {
6887         private final Map<Path, BatchResultsNode> nodes = new HashMap<Path, BatchResultsNode>();
6888         private final List<Request> requests;
6889 
6890         /*package*/BatchResults( List<Request> requests ) {
6891             this.requests = Collections.unmodifiableList(requests);
6892             // Now create the results ...
6893             for (Request request : requests) {
6894                 DateTime expires;
6895                 BatchResultsNode node;
6896 
6897                 switch (request.getType()) {
6898                     case READ_ALL_PROPERTIES:
6899                         ReadAllPropertiesRequest readAll = (ReadAllPropertiesRequest)request;
6900                         expires = computeExpirationTime(readAll);
6901                         getOrCreateNode(readAll.getActualLocationOfNode(), expires).setProperties(readAll.getPropertiesByName());
6902                         break;
6903                     case READ_PROPERTY:
6904                         ReadPropertyRequest read = (ReadPropertyRequest)request;
6905                         expires = computeExpirationTime(read);
6906                         getOrCreateNode(read.getActualLocationOfNode(), expires).addProperty(read.getProperty());
6907                         break;
6908                     case READ_NODE:
6909                         ReadNodeRequest readNode = (ReadNodeRequest)request;
6910                         expires = computeExpirationTime(readNode);
6911                         node = getOrCreateNode(readNode.getActualLocationOfNode(), expires);
6912                         node.setProperties(readNode.getPropertiesByName());
6913                         node.setChildren(readNode.getChildren());
6914                         break;
6915                     case READ_BLOCK_OF_CHILDREN:
6916                         throw new IllegalStateException();
6917                     case READ_ALL_CHILDREN:
6918                         ReadAllChildrenRequest readAllChildren = (ReadAllChildrenRequest)request;
6919                         expires = computeExpirationTime(readAllChildren);
6920                         getOrCreateNode(readAllChildren.getActualLocationOfNode(), expires).setChildren(readAllChildren.getChildren());
6921                         break;
6922                     case READ_BRANCH:
6923                         ReadBranchRequest readBranch = (ReadBranchRequest)request;
6924                         expires = computeExpirationTime(readBranch);
6925                         for (Location location : readBranch) {
6926                             node = getOrCreateNode(location, expires);
6927                             node.setProperties(readBranch.getPropertiesFor(location));
6928                             node.setChildren(readBranch.getChildren(location));
6929                         }
6930                         break;
6931                     default:
6932                         // Do nothing with other request types ...
6933                         break;
6934                 }
6935             }
6936             for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
6937                 entry.getValue().freeze();
6938             }
6939         }
6940 
6941         /*package*/BatchResults( Request request ) {
6942             this.requests = Collections.singletonList(request);
6943             // Now create the results ...
6944             DateTime expires;
6945             BatchResultsNode node;
6946 
6947             switch (request.getType()) {
6948                 case READ_ALL_PROPERTIES:
6949                     ReadAllPropertiesRequest readAll = (ReadAllPropertiesRequest)request;
6950                     expires = computeExpirationTime(readAll);
6951                     getOrCreateNode(readAll.getActualLocationOfNode(), expires).setProperties(readAll.getPropertiesByName());
6952                     break;
6953                 case READ_PROPERTY:
6954                     ReadPropertyRequest read = (ReadPropertyRequest)request;
6955                     expires = computeExpirationTime(read);
6956                     getOrCreateNode(read.getActualLocationOfNode(), expires).addProperty(read.getProperty());
6957                     break;
6958                 case READ_NODE:
6959                     ReadNodeRequest readNode = (ReadNodeRequest)request;
6960                     expires = computeExpirationTime(readNode);
6961                     node = getOrCreateNode(readNode.getActualLocationOfNode(), expires);
6962                     node.setProperties(readNode.getPropertiesByName());
6963                     node.setChildren(readNode.getChildren());
6964                     break;
6965                 case READ_BLOCK_OF_CHILDREN:
6966                     throw new IllegalStateException();
6967                 case READ_ALL_CHILDREN:
6968                     ReadAllChildrenRequest readAllChildren = (ReadAllChildrenRequest)request;
6969                     expires = computeExpirationTime(readAllChildren);
6970                     getOrCreateNode(readAllChildren.getActualLocationOfNode(), expires).setChildren(readAllChildren.getChildren());
6971                     break;
6972                 case READ_BRANCH:
6973                     ReadBranchRequest readBranch = (ReadBranchRequest)request;
6974                     expires = computeExpirationTime(readBranch);
6975                     for (Location location : readBranch) {
6976                         node = getOrCreateNode(location, expires);
6977                         node.setProperties(readBranch.getPropertiesFor(location));
6978                         node.setChildren(readBranch.getChildren(location));
6979                     }
6980                     break;
6981                 default:
6982                     // Do nothing with other request types ...
6983                     break;
6984             }
6985             for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
6986                 entry.getValue().freeze();
6987             }
6988         }
6989 
6990         BatchResults() {
6991             this.requests = Collections.emptyList();
6992         }
6993 
6994         /**
6995          * {@inheritDoc}
6996          * 
6997          * @see org.modeshape.graph.Results#getRequests()
6998          */
6999         public List<Request> getRequests() {
7000             return requests;
7001         }
7002 
7003         private BatchResultsNode getOrCreateNode( Location location,
7004                                                   DateTime expirationTime ) {
7005             BatchResultsNode node = nodes.get(location);
7006             if (node == null) {
7007                 node = new BatchResultsNode(location, expirationTime);
7008                 assert location != null;
7009                 assert location.getPath() != null;
7010                 nodes.put(location.getPath(), node);
7011             }
7012             return node;
7013         }
7014 
7015         public Graph getGraph() {
7016             return Graph.this;
7017         }
7018 
7019         protected void checkIsAbsolute( Path path ) {
7020             if (!path.isAbsolute()) {
7021                 throw new IllegalArgumentException(GraphI18n.pathIsNotAbsolute.text(path));
7022             }
7023         }
7024 
7025         public Node getNode( String pathStr ) {
7026             Path path = createPath(pathStr);
7027             checkIsAbsolute(path);
7028             return nodes.get(path);
7029         }
7030 
7031         public Node getNode( Path path ) {
7032             CheckArg.isNotNull(path, "path");
7033             checkIsAbsolute(path);
7034             return nodes.get(path);
7035         }
7036 
7037         public Node getNode( Location location ) {
7038             CheckArg.isNotNull(location, "location");
7039             CheckArg.isNotNull(location.getPath(), "location.getPath()");
7040             return nodes.get(location.getPath());
7041         }
7042 
7043         public boolean includes( String path ) {
7044             return getNode(path) != null;
7045         }
7046 
7047         public boolean includes( Path path ) {
7048             return getNode(path) != null;
7049         }
7050 
7051         public boolean includes( Location location ) {
7052             return getNode(location) != null;
7053         }
7054 
7055         public Iterator<Node> iterator() {
7056             List<Path> paths = new ArrayList<Path>(nodes.keySet());
7057             Collections.sort(paths);
7058             final Iterator<Path> pathIter = paths.iterator();
7059             return new Iterator<Node>() {
7060                 public boolean hasNext() {
7061                     return pathIter.hasNext();
7062                 }
7063 
7064                 public Node next() {
7065                     Path nextPath = pathIter.next();
7066                     return getNode(nextPath);
7067                 }
7068 
7069                 public void remove() {
7070                     throw new UnsupportedOperationException();
7071                 }
7072             };
7073         }
7074     }
7075 
7076     @Immutable
7077     class BatchResultsNode implements Node {
7078         private final Location location;
7079         private final DateTime expirationTime;
7080         private Map<Name, Property> properties;
7081         private List<Location> children;
7082 
7083         BatchResultsNode( Location location,
7084                           DateTime expirationTime ) {
7085             this.location = location;
7086             this.expirationTime = expirationTime;
7087         }
7088 
7089         public DateTime getExpirationTime() {
7090             return expirationTime;
7091         }
7092 
7093         void addProperty( Property property ) {
7094             if (this.properties == null) this.properties = new HashMap<Name, Property>();
7095             this.properties.put(property.getName(), property);
7096         }
7097 
7098         void setProperties( Map<Name, Property> properties ) {
7099             this.properties = properties;
7100         }
7101 
7102         void setChildren( List<Location> children ) {
7103             this.children = children;
7104         }
7105 
7106         void freeze() {
7107             if (properties != null) properties = Collections.unmodifiableMap(properties);
7108             else properties = Collections.emptyMap();
7109             if (children != null) children = Collections.unmodifiableList(children);
7110             else children = Collections.emptyList();
7111         }
7112 
7113         public List<Segment> getChildrenSegments() {
7114             return getSegments(getChildren());
7115         }
7116 
7117         public Graph getGraph() {
7118             return Graph.this;
7119         }
7120 
7121         public Location getLocation() {
7122             return location;
7123         }
7124 
7125         public Collection<Property> getProperties() {
7126             return properties.values();
7127         }
7128 
7129         public Map<Name, Property> getPropertiesByName() {
7130             return properties;
7131         }
7132 
7133         public Property getProperty( Name name ) {
7134             return properties.get(name);
7135         }
7136 
7137         public Property getProperty( String nameStr ) {
7138             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
7139             return properties.get(name);
7140         }
7141 
7142         public List<Location> getChildren() {
7143             return children;
7144         }
7145 
7146         public List<Location> getChildren( Name namePattern ) {
7147             List<Location> result = new ArrayList<Location>();
7148             for (Location child : getChildren()) {
7149                 if (child.getPath().endsWith(namePattern)) result.add(child);
7150             }
7151             return result;
7152         }
7153 
7154         public boolean hasChildren() {
7155             return children.size() != 0;
7156         }
7157 
7158         public Iterator<Location> iterator() {
7159             return children.iterator();
7160         }
7161 
7162         @Override
7163         public int hashCode() {
7164             return location.hashCode();
7165         }
7166 
7167         @Override
7168         public boolean equals( Object obj ) {
7169             if (obj instanceof Node) {
7170                 Node that = (Node)obj;
7171                 return this.location.isSame(that.getLocation());
7172             }
7173             return false;
7174         }
7175 
7176         @Override
7177         public String toString() {
7178             return "Node " + getLocation().toString();
7179         }
7180 
7181     }
7182 
7183     // ----------------------------------------------------------------------------------------------------------------
7184     // Subgraph and SubgraphNode implementations
7185     // ----------------------------------------------------------------------------------------------------------------
7186     @Immutable
7187     class SubgraphResults implements Subgraph {
7188         private final ReadBranchRequest request;
7189 
7190         SubgraphResults( ReadBranchRequest request ) {
7191             this.request = request;
7192         }
7193 
7194         public Graph getGraph() {
7195             return Graph.this;
7196         }
7197 
7198         public Location getLocation() {
7199             return request.getActualLocationOfNode();
7200         }
7201 
7202         public SubgraphNode getRoot() {
7203             return getNode(getLocation());
7204         }
7205 
7206         public int getMaximumDepth() {
7207             return request.maximumDepth();
7208         }
7209 
7210         public Iterator<SubgraphNode> iterator() {
7211             final Iterator<Location> iter = request.iterator();
7212             return new Iterator<SubgraphNode>() {
7213                 public boolean hasNext() {
7214                     return iter.hasNext();
7215                 }
7216 
7217                 public SubgraphNode next() {
7218                     return getNode(iter.next());
7219                 }
7220 
7221                 public void remove() {
7222                     throw new UnsupportedOperationException();
7223                 }
7224             };
7225         }
7226 
7227         public boolean includes( Path path ) {
7228             CheckArg.isNotNull(path, "path");
7229             path = getAbsolutePath(path);
7230             return request.includes(path);
7231         }
7232 
7233         public boolean includes( Location location ) {
7234             CheckArg.isNotNull(location, "location");
7235             return request.includes(location);
7236         }
7237 
7238         public boolean includes( String pathStr ) {
7239             Path path = createPath(pathStr);
7240             path = getAbsolutePath(path);
7241             return includes(path);
7242         }
7243 
7244         public SubgraphNode getNode( Location location ) {
7245             if (!location.hasPath()) return null;
7246             Location actualLocation = request.getLocationFor(location.getPath());
7247             if (actualLocation == null) return null;
7248             return new SubgraphNodeImpl(actualLocation, request);
7249         }
7250 
7251         public SubgraphNode getNode( Path path ) {
7252             path = getAbsolutePath(path);
7253             if (!includes(path)) return null;
7254             Location location = request.getLocationFor(path);
7255             if (location == null) return null;
7256             return new SubgraphNodeImpl(location, request);
7257         }
7258 
7259         public SubgraphNode getNode( String pathStr ) {
7260             CheckArg.isNotEmpty(pathStr, "path");
7261             Path path = createPath(pathStr);
7262             path = getAbsolutePath(path);
7263             return getNode(path);
7264         }
7265 
7266         public SubgraphNode getNode( Name relativePath ) {
7267             Path path = getGraph().getContext()
7268                                   .getValueFactories()
7269                                   .getPathFactory()
7270                                   .create(getLocation().getPath(), relativePath);
7271             path = path.getNormalizedPath();
7272             return getNode(path);
7273         }
7274 
7275         protected Path getAbsolutePath( Path absoluteOrRelative ) {
7276             Path result = absoluteOrRelative;
7277             if (!result.isAbsolute()) {
7278                 result = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), result);
7279                 result = result.getNormalizedPath();
7280             }
7281             return result;
7282         }
7283 
7284         @Override
7285         public int hashCode() {
7286             return getLocation().hashCode();
7287         }
7288 
7289         /**
7290          * {@inheritDoc}
7291          * 
7292          * @see java.lang.Object#equals(java.lang.Object)
7293          */
7294         @Override
7295         public boolean equals( Object obj ) {
7296             if (obj instanceof SubgraphResults) {
7297                 SubgraphResults that = (SubgraphResults)obj;
7298                 return getLocation().equals(that.getLocation()) && request.equals(that.request);
7299             } else if (obj instanceof Subgraph) {
7300                 Subgraph that = (Subgraph)obj;
7301                 if (!getLocation().equals(that.getLocation())) return false;
7302                 Iterator<SubgraphNode> thisIter = this.iterator();
7303                 Iterator<SubgraphNode> thatIter = that.iterator();
7304                 while (thisIter.hasNext() && thatIter.hasNext()) {
7305                     SubgraphNode thisNode = thisIter.next();
7306                     SubgraphNode thatNode = thatIter.next();
7307                     if (!thisNode.getLocation().equals(thatNode.getLocation())) return false;
7308                     if (!thisNode.getProperties().equals(thatNode.getProperties())) return false;
7309                     if (!thisNode.getChildren().equals(thatNode.getChildren())) return false;
7310                 }
7311                 if (thisIter.hasNext() || thatIter.hasNext()) return false;
7312                 return true;
7313             }
7314             return false;
7315         }
7316 
7317         @Override
7318         public String toString() {
7319             return "Subgraph\n" + getToString(getContext());
7320         }
7321 
7322         /**
7323          * Get the string representation of this subgraph tree.
7324          * 
7325          * @param context the execution context in which the conversion is to take place
7326          * @return the string representation; never null
7327          */
7328         public String getToString( ExecutionContext context ) {
7329             StringBuilder sb = new StringBuilder();
7330             getRecursiveString(context, getRoot(), sb, 0);
7331             return sb.toString();
7332         }
7333 
7334         private void getRecursiveString( ExecutionContext context,
7335                                          SubgraphNode node,
7336                                          StringBuilder str,
7337                                          int indentLevel ) {
7338             for (int i = 0; i < indentLevel; ++i) {
7339                 str.append("  ");
7340             }
7341             str.append(node.toString());
7342 
7343             // Recursively add children at one greater tab level
7344             for (Location nextLoc : node.getChildren()) {
7345                 SubgraphNode childNode = getNode(nextLoc);
7346                 // child node location may exist, but the subgraph may not have
7347                 // been constructed deep enough to instantiate the subnode, so
7348                 // check for null
7349                 if (childNode != null) {
7350                     getRecursiveString(context, childNode, str, indentLevel + 1);
7351                 }
7352             }
7353         }
7354 
7355     }
7356 
7357     protected static final List<Location> NO_CHILDREN = Collections.emptyList();
7358 
7359     @Immutable
7360     class SubgraphNodeImpl implements SubgraphNode {
7361         private final Location location;
7362         private final ReadBranchRequest request;
7363 
7364         SubgraphNodeImpl( Location location,
7365                           ReadBranchRequest request ) {
7366             this.location = location;
7367             this.request = request;
7368         }
7369 
7370         public DateTime getExpirationTime() {
7371             return computeExpirationTime(request);
7372         }
7373 
7374         public List<Location> getChildren() {
7375             List<Location> children = request.getChildren(location);
7376             if (children == null) children = NO_CHILDREN;
7377             return children;
7378         }
7379 
7380         public List<Location> getChildren( Name namePattern ) {
7381             List<Location> result = new ArrayList<Location>();
7382             for (Location child : getChildren()) {
7383                 if (child.getPath().endsWith(namePattern)) result.add(child);
7384             }
7385             return result;
7386         }
7387 
7388         public Graph getGraph() {
7389             return Graph.this;
7390         }
7391 
7392         public Location getLocation() {
7393             return location;
7394         }
7395 
7396         public Collection<Property> getProperties() {
7397             return getPropertiesByName().values();
7398         }
7399 
7400         public Map<Name, Property> getPropertiesByName() {
7401             return request.getPropertiesFor(location);
7402         }
7403 
7404         public Property getProperty( Name name ) {
7405             return getPropertiesByName().get(name);
7406         }
7407 
7408         public Property getProperty( String nameStr ) {
7409             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
7410             return getPropertiesByName().get(name);
7411         }
7412 
7413         public boolean hasChildren() {
7414             return getChildren().size() != 0;
7415         }
7416 
7417         public List<Segment> getChildrenSegments() {
7418             return getSegments(getChildren());
7419         }
7420 
7421         public Iterator<Location> iterator() {
7422             return getChildren().iterator();
7423         }
7424 
7425         public SubgraphNode getNode( String childName ) {
7426             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childName);
7427             Location location = request.getLocationFor(path);
7428             if (location == null) return null;
7429             return new SubgraphNodeImpl(location, request);
7430         }
7431 
7432         public SubgraphNode getNode( Name childName ) {
7433             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childName);
7434             Location location = request.getLocationFor(path);
7435             if (location == null) return null;
7436             return new SubgraphNodeImpl(location, request);
7437         }
7438 
7439         public SubgraphNode getNode( Segment childSegment ) {
7440             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childSegment);
7441             path = path.getNormalizedPath();
7442             Location location = request.getLocationFor(path);
7443             if (location == null) return null;
7444             return new SubgraphNodeImpl(location, request);
7445         }
7446 
7447         public SubgraphNode getNode( Path relativePath ) {
7448             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), relativePath);
7449             path = path.getNormalizedPath();
7450             Location location = request.getLocationFor(path);
7451             if (location == null) return null;
7452             return new SubgraphNodeImpl(location, request);
7453         }
7454 
7455         @Override
7456         public int hashCode() {
7457             return location.hashCode();
7458         }
7459 
7460         @Override
7461         public boolean equals( Object obj ) {
7462             if (obj instanceof Node) {
7463                 Node that = (Node)obj;
7464                 return this.location.isSame(that.getLocation());
7465             }
7466             return false;
7467         }
7468 
7469         @Override
7470         public String toString() {
7471             return getNodeString(getContext(), location);
7472         }
7473 
7474         private String getNodeString( ExecutionContext context,
7475                                       Location location ) {
7476             StringBuilder sb = new StringBuilder();
7477             sb.append('<'); // Bracket the node
7478             ValueFactory<String> strings = context.getValueFactories().getStringFactory();
7479 
7480             String name = "";
7481             if (location.getPath().getLastSegment() != null) {
7482                 name = strings.create(location.getPath().getLastSegment());
7483             } else {
7484                 name = strings.create(location.getPath());
7485             }
7486 
7487             if (name.startsWith("{")) {
7488                 // Remove {xxxx} namespace prefix
7489                 int end = name.indexOf('}');
7490                 name = name.substring(end + 1, name.length());
7491             }
7492 
7493             // Surround name in double quotes
7494             sb.append("name = ").append('\"').append(name).append('\"').append(" ");
7495             boolean first = true;
7496             if (getProperties() != null) {
7497                 for (Property entry : getProperties()) {
7498 
7499                     if (first) {
7500                         first = false;
7501                     } else sb.append(" ");
7502                     sb.append(getPropertyString(entry));
7503                 }
7504             }
7505             sb.append(">\n");
7506 
7507             return sb.toString();
7508         }
7509 
7510         private String getPropertyString( Property property ) {
7511             // Surround property value in double quotes so final property looks like:
7512             // color = "blue" (single valued property)
7513             // colors = ["blue", "red", "green"] (multi-valued property)
7514 
7515             StringBuilder sb = new StringBuilder();
7516             sb.append(getContext().getValueFactories().getStringFactory().create(property.getName()));
7517             sb.append(" = ");
7518             if (property.isEmpty()) {
7519                 sb.append("null");
7520             } else if (property.isSingle()) {
7521                 String valueStr = getContext().getValueFactories().getStringFactory().create(property.getValues().next());
7522                 sb.append('\"').append(valueStr).append('\"');
7523             } else {
7524                 sb.append('[');
7525                 boolean first = true;
7526                 for (Object value : property.getValuesAsArray()) {
7527                     if (first) first = false;
7528                     else sb.append(",");
7529                     String valueStr = getContext().getValueFactories().getStringFactory().create(value);
7530                     sb.append('\"').append(valueStr).append('\"');
7531                 }
7532                 if (property.isMultiple()) sb.append(']');
7533             }
7534             return sb.toString();
7535         }
7536     }
7537 
7538     // ----------------------------------------------------------------------------------------------------------------
7539     // Action Implementations
7540     // ----------------------------------------------------------------------------------------------------------------
7541     @Immutable
7542     protected abstract class AbstractAction<T> implements Conjunction<T> {
7543         private final T afterConjunction;
7544 
7545         /*package*/AbstractAction( T afterConjunction ) {
7546             this.afterConjunction = afterConjunction;
7547         }
7548 
7549         /*package*/T afterConjunction() {
7550             return this.afterConjunction;
7551         }
7552 
7553         public T and() {
7554             return this.afterConjunction;
7555         }
7556 
7557         /*package*/Path createPath( String path ) {
7558             return Graph.this.getContext().getValueFactories().getPathFactory().create(path);
7559         }
7560 
7561         /*package*/Name createName( String name ) {
7562             return Graph.this.getContext().getValueFactories().getNameFactory().create(name);
7563         }
7564     }
7565 
7566     @NotThreadSafe
7567     protected abstract class MoveAction<T> extends AbstractAction<T> implements Move<T> {
7568         private final Locations from;
7569         private Name newName;
7570 
7571         /*package*/MoveAction( T afterConjunction,
7572                                 Location from ) {
7573             super(afterConjunction);
7574             this.from = new Locations(from);
7575         }
7576 
7577         public Move<T> and( Location from ) {
7578             this.from.add(from);
7579             return this;
7580         }
7581 
7582         public Move<T> and( String from ) {
7583             this.from.add(Location.create(createPath(from)));
7584             return this;
7585         }
7586 
7587         public Move<T> and( Path from ) {
7588             this.from.add(Location.create(from));
7589             return this;
7590         }
7591 
7592         public Move<T> and( Property firstFrom,
7593                             Property... additionalFroms ) {
7594             this.from.add(Location.create(firstFrom, additionalFroms));
7595             return this;
7596         }
7597 
7598         public Move<T> and( Iterable<Property> idPropertiesFrom ) {
7599             this.from.add(Location.create(idPropertiesFrom));
7600             return this;
7601         }
7602 
7603         public Move<T> and( Property from ) {
7604             this.from.add(Location.create(from));
7605             return this;
7606         }
7607 
7608         public Move<T> and( UUID from ) {
7609             this.from.add(Location.create(from));
7610             return this;
7611         }
7612 
7613         public Into<T> as( Name newName ) {
7614             this.newName = newName;
7615             return this;
7616         }
7617 
7618         /**
7619          * {@inheritDoc}
7620          * 
7621          * @see org.modeshape.graph.Graph.AsName#as(java.lang.String)
7622          */
7623         public Into<T> as( String newName ) {
7624             return as(createName(newName));
7625         }
7626 
7627         /**
7628          * Submit any requests to move the targets into the supplied parent location
7629          * 
7630          * @param from the location(s) that are being moved; never null
7631          * @param into the parent location
7632          * @param before the location of the child of the parent before which this node should be placed
7633          * @param newName the new name for the node being moved; may be null
7634          * @return this object, for method chaining
7635          */
7636         protected abstract T submit( Locations from,
7637                                      Location into,
7638                                      Location before,
7639                                      Name newName );
7640 
7641         /**
7642          * Submit any requests to move the targets into the supplied parent location
7643          * 
7644          * @param from the location(s) that are being moved; never null
7645          * @param into the parent location
7646          * @param newName the new name for the node being moved; may be null
7647          * @return this object, for method chaining
7648          */
7649         protected T submit( Locations from,
7650                             Location into,
7651                             Name newName ) {
7652             return submit(from, into, null, newName);
7653         }
7654 
7655         public T into( Location into ) {
7656             return submit(from, into, null, newName);
7657         }
7658 
7659         public T into( Path into ) {
7660             return submit(from, Location.create(into), newName);
7661         }
7662 
7663         public T into( UUID into ) {
7664             return submit(from, Location.create(into), newName);
7665         }
7666 
7667         public T into( Property firstIdProperty,
7668                        Property... additionalIdProperties ) {
7669             return submit(from, Location.create(firstIdProperty, additionalIdProperties), newName);
7670         }
7671 
7672         public T into( Property into ) {
7673             return submit(from, Location.create(into), newName);
7674         }
7675 
7676         public T into( String into ) {
7677             return submit(from, Location.create(createPath(into)), newName);
7678         }
7679 
7680         public T before( Location before ) {
7681             return submit(from, null, before, newName);
7682         }
7683 
7684         public T before( Path before ) {
7685             return submit(from, null, Location.create(before), newName);
7686         }
7687 
7688         public T before( UUID before ) {
7689             return submit(from, null, Location.create(before), newName);
7690         }
7691 
7692         public T before( Property firstIdProperty,
7693                          Property... additionalIdProperties ) {
7694             return submit(from, null, Location.create(firstIdProperty, additionalIdProperties), newName);
7695         }
7696 
7697         public T before( Property before ) {
7698             return submit(from, null, Location.create(before), newName);
7699         }
7700 
7701         public T before( String before ) {
7702             return submit(from, null, Location.create(createPath(before)), newName);
7703         }
7704     }
7705 
7706     @NotThreadSafe
7707     protected abstract class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
7708         protected Locations from;
7709         protected String fromWorkspaceName;
7710 
7711         /*package*/CopyAction( T afterConjunction,
7712                                 Location from ) {
7713             super(afterConjunction);
7714             this.from = new Locations(from);
7715             this.fromWorkspaceName = Graph.this.getCurrentWorkspaceName();
7716         }
7717 
7718         public Copy<T> and( Location from ) {
7719             this.from.add(from);
7720             return this;
7721         }
7722 
7723         public Copy<T> and( String from ) {
7724             this.from.add(Location.create(createPath(from)));
7725             return this;
7726         }
7727 
7728         public Copy<T> and( Path from ) {
7729             this.from.add(Location.create(from));
7730             return this;
7731         }
7732 
7733         public Copy<T> and( Property firstFrom,
7734                             Property... additionalFroms ) {
7735             this.from.add(Location.create(firstFrom, additionalFroms));
7736             return this;
7737         }
7738 
7739         public Copy<T> and( Iterable<Property> idProperties ) {
7740             this.from.add(Location.create(idProperties));
7741             return this;
7742         }
7743 
7744         public Copy<T> and( Property from ) {
7745             this.from.add(Location.create(from));
7746             return this;
7747         }
7748 
7749         public Copy<T> and( UUID from ) {
7750             this.from.add(Location.create(from));
7751             return this;
7752         }
7753 
7754         /**
7755          * Submit any requests to move the targets into the supplied parent location
7756          * 
7757          * @param fromWorkspaceName the name of the workspace containing the {@code from} locations
7758          * @param from the locations that are being copied
7759          * @param into the parent location
7760          * @param nameForCopy the name that should be used for the copy, or null if the name should be the same as the original
7761          * @return this object, for method chaining
7762          */
7763         protected abstract T submit( String fromWorkspaceName,
7764                                      Locations from,
7765                                      Location into,
7766                                      Name nameForCopy );
7767 
7768         public CopyTarget<T> fromWorkspace( String workspaceName ) {
7769             this.fromWorkspaceName = workspaceName;
7770 
7771             return this;
7772         }
7773 
7774         public T into( Location into ) {
7775             return submit(this.fromWorkspaceName, this.from, into, null);
7776         }
7777 
7778         public T into( Path into ) {
7779             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7780         }
7781 
7782         public T into( UUID into ) {
7783             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7784         }
7785 
7786         public T into( Property firstIdProperty,
7787                        Property... additionalIdProperties ) {
7788             return submit(this.fromWorkspaceName, this.from, Location.create(firstIdProperty, additionalIdProperties), null);
7789         }
7790 
7791         public T into( Property into ) {
7792             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7793         }
7794 
7795         public T into( String into ) {
7796             return submit(this.fromWorkspaceName, this.from, Location.create(createPath(into)), null);
7797         }
7798 
7799         public T to( Location desiredLocation ) {
7800             if (!desiredLocation.hasPath()) {
7801                 throw new IllegalArgumentException(GraphI18n.unableToCopyToLocationWithoutAPath.text(this.from, desiredLocation));
7802             }
7803             Path desiredPath = desiredLocation.getPath();
7804             if (desiredPath.isRoot()) {
7805                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredLocation));
7806             }
7807             Path parent = desiredPath.getParent();
7808             return submit(this.fromWorkspaceName, this.from, Location.create(parent), desiredPath.getLastSegment().getName());
7809         }
7810 
7811         public T to( Path desiredPath ) {
7812             if (desiredPath.isRoot()) {
7813                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredPath));
7814             }
7815             Path parent = desiredPath.getParent();
7816             return submit(this.fromWorkspaceName, this.from, Location.create(parent), desiredPath.getLastSegment().getName());
7817         }
7818 
7819         public T to( String desiredPath ) {
7820             return to(createPath(desiredPath));
7821         }
7822     }
7823 
7824     @NotThreadSafe
7825     public abstract class CloneAction<T> extends AbstractAction<T> implements Clone<T> {
7826         protected final Location from;
7827 
7828         /*package*/CloneAction( T afterConjunction,
7829                                  Location from ) {
7830             super(afterConjunction);
7831             this.from = from;
7832         }
7833 
7834         protected abstract T submit( String fromWorkspaceName,
7835                                      Location from,
7836                                      String intoWorkspaceName,
7837                                      Location into,
7838                                      Name desiredName,
7839                                      Segment desiredSegment,
7840                                      boolean removeExisting );
7841 
7842         public AsChild<Into<WithUuids<T>>> fromWorkspace( final String workspaceName ) {
7843             final CloneAction<T> source = this;
7844             return new AsChild<Into<WithUuids<T>>>() {
7845                 public Into<WithUuids<T>> as( final Name name ) {
7846                     return new CloneTargetAction<T>(afterConjunction(), source) {
7847                         @Override
7848                         protected T submit( Location into,
7849                                             boolean removeExisting ) {
7850                             String intoWorkspaceName = getCurrentWorkspaceName();
7851                             return source.submit(workspaceName, from, intoWorkspaceName, into, name, null, removeExisting);
7852                         }
7853                     };
7854 
7855                 }
7856 
7857                 public Into<WithUuids<T>> as( final String name ) {
7858                     return as(getContext().getValueFactories().getNameFactory().create(name));
7859                 }
7860 
7861                 public Into<WithUuids<T>> as( final Segment segment ) {
7862                     return new CloneTargetAction<T>(afterConjunction(), source) {
7863                         @Override
7864                         protected T submit( Location into,
7865                                             boolean removeExisting ) {
7866                             String intoWorkspaceName = getCurrentWorkspaceName();
7867                             return source.submit(workspaceName, from, intoWorkspaceName, into, null, segment, removeExisting);
7868                         }
7869                     };
7870                 }
7871 
7872             };
7873         }
7874     }
7875 
7876     @NotThreadSafe
7877     public abstract class CloneTargetAction<T> extends AbstractAction<T> implements Into<WithUuids<T>> {
7878         protected final CloneAction<T> source;
7879 
7880         /*package*/CloneTargetAction( T afterConjunction,
7881                                        CloneAction<T> source ) {
7882             super(afterConjunction);
7883             this.source = source;
7884         }
7885 
7886         protected abstract T submit( Location into,
7887                                      boolean removeExisting );
7888 
7889         public WithUuids<T> into( final Location into ) {
7890             return new WithUuids<T>() {
7891                 public T failingIfAnyUuidsMatch() {
7892                     submit(into, false);
7893                     return and();
7894                 }
7895 
7896                 public T replacingExistingNodesWithSameUuids() {
7897                     submit(into, true);
7898                     return and();
7899 
7900                 }
7901             };
7902         }
7903 
7904         public WithUuids<T> into( Path into ) {
7905             return into(Location.create(into));
7906         }
7907 
7908         public WithUuids<T> into( UUID into ) {
7909             return into(Location.create(into));
7910         }
7911 
7912         public WithUuids<T> into( Property firstIdProperty,
7913                                   Property... additionalIdProperties ) {
7914             return into(Location.create(firstIdProperty, additionalIdProperties));
7915         }
7916 
7917         public WithUuids<T> into( Property into ) {
7918             return into(Location.create(into));
7919         }
7920 
7921         public WithUuids<T> into( String into ) {
7922             return into(Location.create(createPath(into)));
7923         }
7924     }
7925 
7926     @NotThreadSafe
7927     protected abstract class CreateAction<T> extends AbstractAction<T> implements Create<T> {
7928         private final String workspaceName;
7929         private final Location parent;
7930         private final Name childName;
7931         private final Map<Name, Property> properties = new HashMap<Name, Property>();
7932         private boolean submitted = false;
7933         private NodeConflictBehavior conflictBehavior = NodeConflictBehavior.APPEND;
7934 
7935         /*package*/CreateAction( T afterConjunction,
7936                                   Location parent,
7937                                   String workspaceName,
7938                                   Name childName ) {
7939             super(afterConjunction);
7940             this.parent = parent;
7941             this.workspaceName = workspaceName;
7942             this.childName = childName;
7943         }
7944 
7945         /**
7946          * {@inheritDoc}
7947          * 
7948          * @see org.modeshape.graph.Graph.Create#ifAbsent()
7949          */
7950         public CreateAction<T> ifAbsent() {
7951             conflictBehavior = NodeConflictBehavior.DO_NOT_REPLACE;
7952             return this;
7953         }
7954 
7955         /**
7956          * {@inheritDoc}
7957          * 
7958          * @see org.modeshape.graph.Graph.Create#orReplace()
7959          */
7960         public CreateAction<T> orReplace() {
7961             conflictBehavior = NodeConflictBehavior.REPLACE;
7962             return this;
7963         }
7964 
7965         /**
7966          * {@inheritDoc}
7967          * 
7968          * @see org.modeshape.graph.Graph.Create#orUpdate()
7969          */
7970         public CreateAction<T> orUpdate() {
7971             conflictBehavior = NodeConflictBehavior.UPDATE;
7972             return this;
7973         }
7974 
7975         /**
7976          * {@inheritDoc}
7977          * 
7978          * @see org.modeshape.graph.Graph.Create#byAppending()
7979          */
7980         public CreateAction<T> byAppending() {
7981             conflictBehavior = NodeConflictBehavior.APPEND;
7982             return this;
7983         }
7984 
7985         public Create<T> and( UUID uuid ) {
7986             PropertyFactory factory = getContext().getPropertyFactory();
7987             properties.put(ModeShapeLexicon.UUID, factory.create(ModeShapeLexicon.UUID, uuid));
7988             return this;
7989         }
7990 
7991         public Create<T> and( Property property ) {
7992             properties.put(property.getName(), property);
7993             return this;
7994         }
7995 
7996         public Create<T> and( Iterable<Property> properties ) {
7997             for (Property property : properties) {
7998                 this.properties.put(property.getName(), property);
7999             }
8000             return this;
8001         }
8002 
8003         public Create<T> and( String name,
8004                               Object... values ) {
8005             ExecutionContext context = getContext();
8006             PropertyFactory factory = context.getPropertyFactory();
8007             NameFactory nameFactory = context.getValueFactories().getNameFactory();
8008             Name propertyName = nameFactory.create(name);
8009             properties.put(propertyName, factory.create(propertyName, values));
8010             return this;
8011         }
8012 
8013         public Create<T> and( Name name,
8014                               Object... values ) {
8015             PropertyFactory factory = getContext().getPropertyFactory();
8016             properties.put(name, factory.create(name, values));
8017             return this;
8018         }
8019 
8020         public Create<T> and( Property property,
8021                               Property... additionalProperties ) {
8022             properties.put(property.getName(), property);
8023             for (Property additionalProperty : additionalProperties) {
8024                 properties.put(additionalProperty.getName(), additionalProperty);
8025             }
8026             return this;
8027         }
8028 
8029         public Create<T> with( UUID uuid ) {
8030             return and(uuid);
8031         }
8032 
8033         public Create<T> with( Property property ) {
8034             return and(property);
8035         }
8036 
8037         public Create<T> with( Iterable<Property> properties ) {
8038             return and(properties);
8039         }
8040 
8041         public Create<T> with( Property property,
8042                                Property... additionalProperties ) {
8043             return and(property, additionalProperties);
8044         }
8045 
8046         public Create<T> with( String name,
8047                                Object... values ) {
8048             return and(name, values);
8049         }
8050 
8051         public Create<T> with( Name name,
8052                                Object... values ) {
8053             return and(name, values);
8054         }
8055 
8056         protected abstract T submit( Location parent,
8057                                      String workspaceName,
8058                                      Name childName,
8059                                      Collection<Property> properties,
8060                                      NodeConflictBehavior conflictBehavior );
8061 
8062         @Override
8063         public T and() {
8064             if (!submitted) {
8065                 submit(parent, workspaceName, childName, this.properties.values(), this.conflictBehavior);
8066                 submitted = true;
8067             }
8068             return super.and();
8069         }
8070     }
8071 
8072     @NotThreadSafe
8073     protected abstract class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
8074         private final Location parent;
8075 
8076         protected CreateNodeNamedAction( T afterConjunction,
8077                                          Location parent ) {
8078             super(afterConjunction);
8079             this.parent = parent;
8080         }
8081 
8082         public CreateAction<T> nodeNamed( String name ) {
8083             NameFactory factory = getContext().getValueFactories().getNameFactory();
8084             Name nameObj = factory.create(name);
8085             return createWith(afterConjunction(), parent, nameObj);
8086         }
8087 
8088         public CreateAction<T> nodeNamed( Name name ) {
8089             return createWith(afterConjunction(), parent, name);
8090         }
8091 
8092         protected abstract CreateAction<T> createWith( T afterConjunction,
8093                                                        Location parent,
8094                                                        Name nodeName );
8095     }
8096 
8097     @Immutable
8098     protected static final class GraphWorkspace implements Workspace {
8099         private final String name;
8100         private final Location root;
8101 
8102         GraphWorkspace( String name,
8103                         Location root ) {
8104             assert name != null;
8105             assert root != null;
8106             this.name = name;
8107             this.root = root;
8108         }
8109 
8110         /**
8111          * {@inheritDoc}
8112          * 
8113          * @see org.modeshape.graph.Workspace#getName()
8114          */
8115         public String getName() {
8116             return name;
8117         }
8118 
8119         /**
8120          * {@inheritDoc}
8121          * 
8122          * @see org.modeshape.graph.Workspace#getRoot()
8123          */
8124         public Location getRoot() {
8125             return root;
8126         }
8127 
8128         /**
8129          * {@inheritDoc}
8130          * 
8131          * @see java.lang.Object#hashCode()
8132          */
8133         @Override
8134         public int hashCode() {
8135             return this.name.hashCode();
8136         }
8137 
8138         /**
8139          * {@inheritDoc}
8140          * 
8141          * @see java.lang.Object#equals(java.lang.Object)
8142          */
8143         @Override
8144         public boolean equals( Object obj ) {
8145             if (obj == this) return true;
8146             if (obj instanceof GraphWorkspace) {
8147                 GraphWorkspace that = (GraphWorkspace)obj;
8148                 if (!this.getName().equals(that.getName())) return false;
8149                 // all root nodes should be equivalent, so no need to check
8150                 return true;
8151             }
8152             return false;
8153         }
8154 
8155         /**
8156          * {@inheritDoc}
8157          * 
8158          * @see java.lang.Object#toString()
8159          */
8160         @Override
8161         public String toString() {
8162             return "Workspace \"" + this.name + "\" (root = " + this.root + " )";
8163         }
8164     }
8165 
8166     /**
8167      * A set of nodes returned from a {@link Graph graph}, with methods to access the properties and children of the nodes in the
8168      * result. The {@link #iterator()} method can be used to iterate all over the nodes in the result.
8169      * 
8170      * @param <NodeType> the type of node that tis results deals with
8171      */
8172     @Immutable
8173     public interface BaseResults<NodeType extends Node> extends Iterable<NodeType> {
8174 
8175         /**
8176          * Get the graph containing the node.
8177          * 
8178          * @return the graph
8179          */
8180         Graph getGraph();
8181 
8182         /**
8183          * Get the node at the supplied location.
8184          * 
8185          * @param path the path of the node in these results
8186          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8187          */
8188         NodeType getNode( String path );
8189 
8190         /**
8191          * Get the node at the supplied location.
8192          * 
8193          * @param path the path of the node in these results
8194          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8195          */
8196         NodeType getNode( Path path );
8197 
8198         /**
8199          * Get the node at the supplied location.
8200          * 
8201          * @param location the location of the node
8202          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8203          */
8204         NodeType getNode( Location location );
8205 
8206         /**
8207          * Return whether these results include a node at the supplied location.
8208          * 
8209          * @param path the path of the node in these results
8210          * @return true if this subgraph includes the supplied location, or false otherwise
8211          */
8212         boolean includes( String path );
8213 
8214         /**
8215          * Return whether this subgraph has a node at the supplied location.
8216          * 
8217          * @param path the path of the node in these results
8218          * @return true if these results includes the supplied location, or false otherwise
8219          */
8220         boolean includes( Path path );
8221 
8222         /**
8223          * Return whether this subgraph has a node at the supplied location.
8224          * 
8225          * @param location the location of the node in these results
8226          * @return true if these results includes the supplied location, or false otherwise
8227          */
8228         boolean includes( Location location );
8229 
8230     }
8231 }