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     protected Path createPath( String path ) {
2976         return getContext().getValueFactories().getPathFactory().create(path);
2977     }
2978 
2979     protected Name createName( String name ) {
2980         return getContext().getValueFactories().getNameFactory().create(name);
2981     }
2982 
2983     protected List<Segment> getSegments( List<Location> locations ) {
2984         List<Segment> segments = new ArrayList<Segment>(locations.size());
2985         for (Location location : locations) {
2986             segments.add(location.getPath().getLastSegment());
2987         }
2988         return segments;
2989     }
2990 
2991     /**
2992      * Begin a batch of requests to perform various operations. Use this approach when multiple operations are to be built and
2993      * then executed with one submission to the underlying {@link #getSourceName() repository source}. The {@link Results results}
2994      * are not available until the {@link Batch#execute()} method is invoked.
2995      * 
2996      * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
2997      * @see Batch#execute()
2998      * @see Results
2999      */
3000     public Batch batch() {
3001         return new Batch(new BatchRequestBuilder());
3002     }
3003 
3004     /**
3005      * Begin a batch of requests to perform various operations, but specify the queue where all accumulated requests should be
3006      * placed. Use this approach when multiple operations are to be built and then executed with one submission to the underlying
3007      * {@link #getSourceName() repository source}. The {@link Results results} are not available until the {@link Batch#execute()}
3008      * method is invoked.
3009      * 
3010      * @param builder the request builder that should be used; may not be null
3011      * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
3012      * @see Batch#execute()
3013      * @see Results
3014      */
3015     public Batch batch( BatchRequestBuilder builder ) {
3016         CheckArg.isNotNull(builder, "builder");
3017         return new Batch(builder);
3018     }
3019 
3020     /**
3021      * Interface for creating multiple requests to perform various operations. Note that all the requests are accumulated until
3022      * the {@link #execute()} method is called. The results of all the operations are then available in the {@link Results} object
3023      * returned by the {@link #execute()}.
3024      */
3025     @Immutable
3026     public final class Batch implements Executable<Node> {
3027         protected final BatchRequestBuilder requestQueue;
3028         protected final BatchConjunction nextRequests;
3029         protected final String workspaceName;
3030         protected boolean executed = false;
3031 
3032         /*package*/Batch( BatchRequestBuilder builder ) {
3033             assert builder != null;
3034             this.requestQueue = builder;
3035             this.workspaceName = Graph.this.getCurrentWorkspaceName();
3036             this.nextRequests = new BatchConjunction() {
3037                 public Batch and() {
3038                     return Batch.this;
3039                 }
3040 
3041                 public Results execute() {
3042                     return Batch.this.execute();
3043                 }
3044             };
3045         }
3046 
3047         /**
3048          * Return whether this batch has been {@link #execute() executed}.
3049          * 
3050          * @return true if this batch has already been executed, or false otherwise
3051          */
3052         public boolean hasExecuted() {
3053             return executed;
3054         }
3055 
3056         /**
3057          * Determine whether this batch needs to be executed (there are requests and the batch has not been executed yet).
3058          * 
3059          * @return true if there are some requests in this batch that need to be executed, or false execution is not required
3060          */
3061         public boolean isExecuteRequired() {
3062             return !executed && requestQueue.hasRequests();
3063         }
3064 
3065         /**
3066          * Obtain the graph that this batch uses.
3067          * 
3068          * @return the graph; never null
3069          */
3070         public Graph getGraph() {
3071             return Graph.this;
3072         }
3073 
3074         /**
3075          * Get the name of the workspace that this batch is using. This is always constant throughout the lifetime of the batch.
3076          * 
3077          * @return the name of the workspace; never null
3078          */
3079         public String getCurrentWorkspaceName() {
3080             return this.workspaceName;
3081         }
3082 
3083         protected final void assertNotExecuted() {
3084             if (executed) {
3085                 throw new IllegalStateException(GraphI18n.unableToAddMoreRequestsToAlreadyExecutedBatch.text());
3086             }
3087         }
3088 
3089         /**
3090          * Begin the request to move the specified node into a parent node at a different location, which is specified via the
3091          * <code>into(...)</code> method on the returned {@link Move} object.
3092          * <p>
3093          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3094          * called.
3095          * </p>
3096          * 
3097          * @param from the node that is to be moved.
3098          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3099          *         to be moved
3100          */
3101         public Move<BatchConjunction> move( Node from ) {
3102             return move(from.getLocation());
3103         }
3104 
3105         /**
3106          * Begin the request to move a node at the specified location into a parent node at a different location, which is
3107          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3108          * <p>
3109          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3110          * called.
3111          * </p>
3112          * 
3113          * @param from the location of the node that is to be moved.
3114          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3115          *         to be moved
3116          */
3117         public final Move<BatchConjunction> move( Location from ) {
3118             assertNotExecuted();
3119             return new MoveAction<BatchConjunction>(this.nextRequests, from) {
3120                 @Override
3121                 protected BatchConjunction submit( Locations from,
3122                                                    Location into,
3123                                                    Location before,
3124                                                    Name newName ) {
3125                     String workspaceName = getCurrentWorkspaceName();
3126                     do {
3127                         requestQueue.moveBranch(from.getLocation(), into, before, workspaceName, newName);
3128                     } while ((from = from.next()) != null);
3129                     return and();
3130                 }
3131             };
3132         }
3133 
3134         /**
3135          * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
3136          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3137          * <p>
3138          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3139          * called.
3140          * </p>
3141          * 
3142          * @param fromPath the path to the node that is to be moved.
3143          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3144          *         to be moved
3145          */
3146         public Move<BatchConjunction> move( String fromPath ) {
3147             return move(Location.create(createPath(fromPath)));
3148         }
3149 
3150         /**
3151          * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
3152          * specified via the <code>into(...)</code> method on the returned {@link Move} object.
3153          * <p>
3154          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3155          * called.
3156          * </p>
3157          * 
3158          * @param from the path to the node that is to be moved.
3159          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3160          *         to be moved
3161          */
3162         public Move<BatchConjunction> move( Path from ) {
3163             return move(Location.create(from));
3164         }
3165 
3166         /**
3167          * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which
3168          * is specified via the <code>into(...)</code> method on the returned {@link Move} object.
3169          * <p>
3170          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3171          * called.
3172          * </p>
3173          * 
3174          * @param from the UUID of the node that is to be moved.
3175          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3176          *         to be moved
3177          */
3178         public Move<BatchConjunction> move( UUID from ) {
3179             return move(Location.create(from));
3180         }
3181 
3182         /**
3183          * Begin the request to move a node with the specified unique identification property into a parent node at a different
3184          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3185          * identification property should uniquely identify a single node.
3186          * <p>
3187          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3188          * called.
3189          * </p>
3190          * 
3191          * @param idProperty the unique identification property of the node that is to be moved.
3192          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3193          *         to be moved
3194          */
3195         public Move<BatchConjunction> move( Property idProperty ) {
3196             return move(Location.create(idProperty));
3197         }
3198 
3199         /**
3200          * Begin the request to move a node with the specified identification properties into a parent node at a different
3201          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3202          * identification properties should uniquely identify a single node.
3203          * <p>
3204          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3205          * called.
3206          * </p>
3207          * 
3208          * @param firstIdProperty the first identification property of the node that is to be moved
3209          * @param additionalIdProperties the remaining identification properties of the node that is to be moved
3210          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3211          *         to be moved
3212          */
3213         public Move<BatchConjunction> move( Property firstIdProperty,
3214                                             Property... additionalIdProperties ) {
3215             return move(Location.create(firstIdProperty, additionalIdProperties));
3216         }
3217 
3218         /**
3219          * Begin the request to move a node with the specified identification properties into a parent node at a different
3220          * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
3221          * identification properties should uniquely identify a single node.
3222          * <p>
3223          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3224          * called.
3225          * </p>
3226          * 
3227          * @param idProperties the identification properties of the node that is to be moved
3228          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
3229          *         to be moved
3230          */
3231         public Move<BatchConjunction> move( Iterable<Property> idProperties ) {
3232             return move(Location.create(idProperties));
3233         }
3234 
3235         /**
3236          * Request to lock the specified node. This request is submitted to the repository immediately.
3237          * 
3238          * @param at the node that is to be locked
3239          * @return an object that allows the scope of the lock to be defined
3240          */
3241         public LockScope<LockTimeout<BatchConjunction>> lock( Node at ) {
3242             return lock(at.getLocation());
3243         }
3244 
3245         /**
3246          * Request to lock the node at the given path. This request is submitted to the repository immediately.
3247          * 
3248          * @param atPath the path of the node that is to be locked
3249          * @return an object that allows the scope of the lock to be defined
3250          */
3251         public LockScope<LockTimeout<BatchConjunction>> lock( String atPath ) {
3252             return lock(Location.create(createPath(atPath)));
3253         }
3254 
3255         /**
3256          * Request to lock the node at the given path. This request is submitted to the repository immediately.
3257          * 
3258          * @param at the path of the node that is to be locked
3259          * @return an object that allows the scope of the lock to be defined
3260          */
3261         public LockScope<LockTimeout<BatchConjunction>> lock( Path at ) {
3262             return lock(Location.create(at));
3263         }
3264 
3265         /**
3266          * Request to lock the node with the given UUID. This request is submitted to the repository immediately.
3267          * 
3268          * @param at the UUID of the node that is to be locked
3269          * @return an object that allows the scope of the lock to be defined
3270          */
3271         public LockScope<LockTimeout<BatchConjunction>> lock( UUID at ) {
3272             return lock(Location.create(at));
3273         }
3274 
3275         /**
3276          * Request to lock the node with the given unique identification property. This request is submitted to the repository
3277          * immediately.
3278          * 
3279          * @param idProperty the unique identifying property of the node that is to be locked
3280          * @return an object that allows the scope of the lock to be defined
3281          */
3282         public LockScope<LockTimeout<BatchConjunction>> lock( Property idProperty ) {
3283             return lock(Location.create(idProperty));
3284         }
3285 
3286         /**
3287          * Request to lock the node with the given identification properties. The identification properties should uniquely
3288          * identify a single node. This request is submitted to the repository immediately.
3289          * 
3290          * @param firstIdProperty the first identification property of the node that is to be copied
3291          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3292          * @return an object that allows the scope of the lock to be defined
3293          */
3294         public LockScope<LockTimeout<BatchConjunction>> lock( Property firstIdProperty,
3295                                                               Property... additionalIdProperties ) {
3296             return lock(Location.create(firstIdProperty, additionalIdProperties));
3297         }
3298 
3299         /**
3300          * Request to lock the node at the given location. This request is submitted to the repository immediately.
3301          * 
3302          * @param at the location of the node that is to be locked
3303          * @return an object that allows the scope of the lock to be defined
3304          */
3305         public LockScope<LockTimeout<BatchConjunction>> lock( Location at ) {
3306             return new LockAction<BatchConjunction>(this.nextRequests, at) {
3307                 @Override
3308                 protected BatchConjunction submit( Location target,
3309                                                    org.modeshape.graph.request.LockBranchRequest.LockScope lockScope,
3310                                                    long lockTimeoutInMillis ) {
3311                     String workspaceName = getCurrentWorkspaceName();
3312                     requests.lockBranch(workspaceName, target, lockScope, lockTimeoutInMillis);
3313                     return and();
3314                 }
3315             };
3316         }
3317 
3318         /**
3319          * Request to unlock the specified node. This request is submitted to the repository immediately.
3320          * 
3321          * @param at the node that is to be unlocked
3322          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3323          */
3324         public BatchConjunction unlock( Node at ) {
3325             return unlock(at.getLocation());
3326         }
3327 
3328         /**
3329          * Request to unlock the node at the given path. This request is submitted to the repository immediately.
3330          * 
3331          * @param atPath the path of the node that is to be unlocked
3332          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3333          */
3334         public BatchConjunction unlock( String atPath ) {
3335             return unlock(Location.create(createPath(atPath)));
3336         }
3337 
3338         /**
3339          * Request to unlock the node at the given path. This request is submitted to the repository immediately.
3340          * 
3341          * @param at the path of the node that is to be unlocked
3342          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3343          */
3344         public BatchConjunction unlock( Path at ) {
3345             return unlock(Location.create(at));
3346         }
3347 
3348         /**
3349          * Request to unlock the node with the given UUID. This request is submitted to the repository immediately.
3350          * 
3351          * @param at the UUID of the node that is to be unlocked
3352          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3353          */
3354         public BatchConjunction unlock( UUID at ) {
3355             return unlock(Location.create(at));
3356         }
3357 
3358         /**
3359          * Request to unlock the node with the given unique identification property. This request is submitted to the repository
3360          * immediately.
3361          * 
3362          * @param idProperty the unique identifying property of the node that is to be unlocked
3363          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3364          */
3365         public BatchConjunction unlock( Property idProperty ) {
3366             return unlock(Location.create(idProperty));
3367         }
3368 
3369         /**
3370          * Request to unlock the node with the given identification properties. The identification properties should uniquely
3371          * identify a single node. This request is submitted to the repository immediately.
3372          * 
3373          * @param firstIdProperty the first identification property of the node that is to be copied
3374          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3375          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3376          */
3377         public BatchConjunction unlock( Property firstIdProperty,
3378                                         Property... additionalIdProperties ) {
3379             return unlock(Location.create(firstIdProperty, additionalIdProperties));
3380         }
3381 
3382         /**
3383          * Request to unlock the node at the given location. This request is submitted to the repository immediately.
3384          * 
3385          * @param at the location of the node that is to be unlocked
3386          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
3387          */
3388         public BatchConjunction unlock( Location at ) {
3389             requests.unlockBranch(workspaceName, at);
3390             return this.nextRequests;
3391         }
3392 
3393         /**
3394          * Begin the request to clone the specified node into a parent node at a different location, which is specified via the
3395          * <code>into(...)</code> method on the returned {@link Clone} object.
3396          * <p>
3397          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3398          * called.
3399          * </p>
3400          * 
3401          * @param from the node that is to be copied.
3402          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3403          *         is to be copied
3404          */
3405         public Clone<BatchConjunction> clone( Node from ) {
3406             return clone(from.getLocation());
3407         }
3408 
3409         /**
3410          * Begin the request to clone a node at the specified location into a parent node at a different location, which is
3411          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3412          * <p>
3413          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3414          * called.
3415          * </p>
3416          * 
3417          * @param from the location of the node that is to be copied.
3418          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3419          *         is to be copied
3420          */
3421         public Clone<BatchConjunction> clone( Location from ) {
3422             assertNotExecuted();
3423             return new CloneAction<BatchConjunction>(this.nextRequests, from) {
3424                 @Override
3425                 protected BatchConjunction submit( String fromWorkspaceName,
3426                                                    Location from,
3427                                                    String intoWorkspaceName,
3428                                                    Location into,
3429                                                    Name desiredName,
3430                                                    Segment desiredSegment,
3431                                                    boolean removeExisting ) {
3432                     requestQueue.cloneBranch(from,
3433                                              fromWorkspaceName,
3434                                              into,
3435                                              intoWorkspaceName,
3436                                              desiredName,
3437                                              desiredSegment,
3438                                              removeExisting);
3439                     return and();
3440                 }
3441             };
3442         }
3443 
3444         /**
3445          * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
3446          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3447          * <p>
3448          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3449          * called.
3450          * </p>
3451          * 
3452          * @param fromPath the path to the node that is to be copied.
3453          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3454          *         is to be copied
3455          */
3456         public Clone<BatchConjunction> clone( String fromPath ) {
3457             return clone(Location.create(createPath(fromPath)));
3458         }
3459 
3460         /**
3461          * Begin the request to clone a node located at the supplied path into a parent node at a different location, which is
3462          * specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3463          * <p>
3464          * Like all other methods on the {@link Graph}, the clone request will be performed immediately when the
3465          * <code>into(...)</code> method is called.
3466          * </p>
3467          * 
3468          * @param from the path to the node that is to be copied.
3469          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3470          *         is to be copied
3471          */
3472         public Clone<BatchConjunction> clone( Path from ) {
3473             return clone(Location.create(from));
3474         }
3475 
3476         /**
3477          * Begin the request to clone a node with the specified unique identifier into a parent node at a different location,
3478          * which is specified via the <code>into(...)</code> method on the returned {@link Clone} object.
3479          * <p>
3480          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3481          * called.
3482          * </p>
3483          * 
3484          * @param from the UUID of the node that is to be copied.
3485          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3486          *         is to be copied
3487          */
3488         public Clone<BatchConjunction> clone( UUID from ) {
3489             return clone(Location.create(from));
3490         }
3491 
3492         /**
3493          * Begin the request to clone a node with the specified unique identification property into a parent node at a different
3494          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3495          * identification property should uniquely identify a single node.
3496          * <p>
3497          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3498          * called.
3499          * </p>
3500          * 
3501          * @param idProperty the unique identification property of the node that is to be copied.
3502          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3503          *         is to be copied
3504          */
3505         public Clone<BatchConjunction> clone( Property idProperty ) {
3506             return clone(Location.create(idProperty));
3507         }
3508 
3509         /**
3510          * Begin the request to clone a node with the specified identification properties into a parent node at a different
3511          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3512          * identification properties should uniquely identify a single node.
3513          * <p>
3514          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3515          * called.
3516          * </p>
3517          * 
3518          * @param firstIdProperty the first identification property of the node that is to be copied
3519          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3520          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3521          *         is to be copied
3522          */
3523         public Clone<BatchConjunction> clone( Property firstIdProperty,
3524                                               Property... additionalIdProperties ) {
3525             return clone(Location.create(firstIdProperty, additionalIdProperties));
3526         }
3527 
3528         /**
3529          * Begin the request to clone a node with the specified identification properties into a parent node at a different
3530          * location, which is specified via the <code>into(...)</code> method on the returned {@link Clone} object. The
3531          * identification properties should uniquely identify a single node.
3532          * <p>
3533          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3534          * called.
3535          * </p>
3536          * 
3537          * @param idProperties the identification properties of the node that is to be copied
3538          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3539          *         is to be copied
3540          */
3541         public Clone<BatchConjunction> clone( Iterable<Property> idProperties ) {
3542             return clone(Location.create(idProperties));
3543         }
3544 
3545         /**
3546          * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
3547          * <code>into(...)</code> method on the returned {@link Copy} object.
3548          * <p>
3549          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3550          * called.
3551          * </p>
3552          * 
3553          * @param from the node that is to be copied.
3554          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3555          *         is to be copied
3556          */
3557         public Copy<BatchConjunction> copy( Node from ) {
3558             return copy(from.getLocation());
3559         }
3560 
3561         /**
3562          * Begin the request to copy a node at the specified location into a parent node at a different location, which is
3563          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3564          * <p>
3565          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3566          * called.
3567          * </p>
3568          * 
3569          * @param from the location of the node that is to be copied.
3570          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3571          *         is to be copied
3572          */
3573         public Copy<BatchConjunction> copy( Location from ) {
3574             assertNotExecuted();
3575             return new CopyAction<BatchConjunction>(this.nextRequests, from) {
3576                 @Override
3577                 protected BatchConjunction submit( String fromWorkspaceName,
3578                                                    Locations from,
3579                                                    Location into,
3580                                                    Name copyName ) {
3581 
3582                     String intoWorkspaceName = getCurrentWorkspaceName();
3583                     if (fromWorkspaceName == null) fromWorkspaceName = intoWorkspaceName;
3584                     do {
3585                         requestQueue.copyBranch(from.getLocation(), fromWorkspaceName, into, intoWorkspaceName, copyName, null);
3586                     } while ((from = from.next()) != null);
3587                     return and();
3588                 }
3589             };
3590         }
3591 
3592         /**
3593          * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
3594          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3595          * <p>
3596          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3597          * called.
3598          * </p>
3599          * 
3600          * @param fromPath the path to the node that is to be copied.
3601          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3602          *         is to be copied
3603          */
3604         public Copy<BatchConjunction> copy( String fromPath ) {
3605             return copy(Location.create(createPath(fromPath)));
3606         }
3607 
3608         /**
3609          * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
3610          * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3611          * <p>
3612          * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the
3613          * <code>into(...)</code> method is called.
3614          * </p>
3615          * 
3616          * @param from the path to the node that is to be copied.
3617          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3618          *         is to be copied
3619          */
3620         public Copy<BatchConjunction> copy( Path from ) {
3621             return copy(Location.create(from));
3622         }
3623 
3624         /**
3625          * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which
3626          * is specified via the <code>into(...)</code> method on the returned {@link Copy} object.
3627          * <p>
3628          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3629          * called.
3630          * </p>
3631          * 
3632          * @param from the UUID of the node that is to be copied.
3633          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3634          *         is to be copied
3635          */
3636         public Copy<BatchConjunction> copy( UUID from ) {
3637             return copy(Location.create(from));
3638         }
3639 
3640         /**
3641          * Begin the request to copy a node with the specified unique identification property into a parent node at a different
3642          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3643          * identification property should uniquely identify a single node.
3644          * <p>
3645          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3646          * called.
3647          * </p>
3648          * 
3649          * @param idProperty the unique identification property of the node that is to be copied.
3650          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3651          *         is to be copied
3652          */
3653         public Copy<BatchConjunction> copy( Property idProperty ) {
3654             return copy(Location.create(idProperty));
3655         }
3656 
3657         /**
3658          * Begin the request to copy a node with the specified identification properties into a parent node at a different
3659          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3660          * identification properties should uniquely identify a single node.
3661          * <p>
3662          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3663          * called.
3664          * </p>
3665          * 
3666          * @param firstIdProperty the first identification property of the node that is to be copied
3667          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3668          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3669          *         is to be copied
3670          */
3671         public Copy<BatchConjunction> copy( Property firstIdProperty,
3672                                             Property... additionalIdProperties ) {
3673             return copy(Location.create(firstIdProperty, additionalIdProperties));
3674         }
3675 
3676         /**
3677          * Begin the request to copy a node with the specified identification properties into a parent node at a different
3678          * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
3679          * identification properties should uniquely identify a single node.
3680          * <p>
3681          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3682          * called.
3683          * </p>
3684          * 
3685          * @param idProperties the identification properties of the node that is to be copied
3686          * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
3687          *         is to be copied
3688          */
3689         public Copy<BatchConjunction> copy( Iterable<Property> idProperties ) {
3690             return copy(Location.create(idProperties));
3691         }
3692 
3693         /**
3694          * Request to delete the specified node.
3695          * <p>
3696          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3697          * called.
3698          * </p>
3699          * 
3700          * @param at the node that is to be deleted
3701          * @return an object that may be used to start another request
3702          */
3703         public BatchConjunction delete( Node at ) {
3704             return delete(at.getLocation());
3705         }
3706 
3707         /**
3708          * Request to delete the node at the given location.
3709          * <p>
3710          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3711          * called.
3712          * </p>
3713          * 
3714          * @param at the location of the node that is to be deleted
3715          * @return an object that may be used to start another request
3716          */
3717         public BatchConjunction delete( Location at ) {
3718             assertNotExecuted();
3719             this.requestQueue.deleteBranch(at, getCurrentWorkspaceName());
3720             return nextRequests;
3721         }
3722 
3723         /**
3724          * Request to delete the node at the given path.
3725          * <p>
3726          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3727          * called.
3728          * </p>
3729          * 
3730          * @param atPath the path of the node that is to be deleted
3731          * @return an object that may be used to start another request
3732          */
3733         public BatchConjunction delete( String atPath ) {
3734             return delete(Location.create(createPath(atPath)));
3735         }
3736 
3737         /**
3738          * Request to delete the node at the given path.
3739          * <p>
3740          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3741          * called.
3742          * </p>
3743          * 
3744          * @param at the path of the node that is to be deleted
3745          * @return an object that may be used to start another request
3746          */
3747         public BatchConjunction delete( Path at ) {
3748             return delete(Location.create(at));
3749         }
3750 
3751         /**
3752          * Request to delete the node with the given UUID.
3753          * <p>
3754          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3755          * called.
3756          * </p>
3757          * 
3758          * @param at the UUID of the node that is to be deleted
3759          * @return an object that may be used to start another request
3760          */
3761         public BatchConjunction delete( UUID at ) {
3762             return delete(Location.create(at));
3763         }
3764 
3765         /**
3766          * Request to delete the node with the given unique identification property.
3767          * <p>
3768          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3769          * called.
3770          * </p>
3771          * 
3772          * @param idProperty the unique identifying property of the node that is to be deleted
3773          * @return an object that may be used to start another request
3774          */
3775         public BatchConjunction delete( Property idProperty ) {
3776             return delete(Location.create(idProperty));
3777         }
3778 
3779         /**
3780          * Request to delete the node with the given identification properties. The identification properties should uniquely
3781          * identify a single node.
3782          * <p>
3783          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3784          * called.
3785          * </p>
3786          * 
3787          * @param firstIdProperty the first identification property of the node that is to be copied
3788          * @param additionalIdProperties the remaining identification properties of the node that is to be copied
3789          * @return an object that may be used to start another request
3790          */
3791         public BatchConjunction delete( Property firstIdProperty,
3792                                         Property... additionalIdProperties ) {
3793             return delete(Location.create(firstIdProperty, additionalIdProperties));
3794         }
3795 
3796         /**
3797          * Request to delete the node with the given identification properties. The identification properties should uniquely
3798          * identify a single node.
3799          * <p>
3800          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3801          * called.
3802          * </p>
3803          * 
3804          * @param idProperties the identification property of the node that is to be copied
3805          * @return an object that may be used to start another request
3806          */
3807         public BatchConjunction delete( Iterable<Property> idProperties ) {
3808             return delete(Location.create(idProperties));
3809         }
3810 
3811         /**
3812          * Begin the request to create a node located at the supplied path.
3813          * <p>
3814          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3815          * called.
3816          * </p>
3817          * 
3818          * @param atPath the path to the node that is to be created.
3819          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3820          *         node where the node is to be created
3821          */
3822         public Create<Batch> create( String atPath ) {
3823             return create(createPath(atPath));
3824         }
3825 
3826         /**
3827          * Begin the request to create a node located at the supplied path.
3828          * <p>
3829          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3830          * called.
3831          * </p>
3832          * 
3833          * @param atPath the path to the node that is to be created.
3834          * @param property a property for the new node
3835          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3836          *         node where the node is to be created
3837          */
3838         public Create<Batch> create( String atPath,
3839                                      Property property ) {
3840             return create(createPath(atPath)).with(property);
3841         }
3842 
3843         /**
3844          * Begin the request to create a node located at the supplied path.
3845          * <p>
3846          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3847          * called.
3848          * </p>
3849          * 
3850          * @param atPath the path to the node that is to be created.
3851          * @param firstProperty a property for the new node
3852          * @param additionalProperties additional properties for the new node
3853          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3854          *         node where the node is to be created
3855          */
3856         public Create<Batch> create( String atPath,
3857                                      Property firstProperty,
3858                                      Property... additionalProperties ) {
3859             return create(createPath(atPath)).with(firstProperty, additionalProperties);
3860         }
3861 
3862         /**
3863          * Begin the request to create a node located at the supplied path.
3864          * <p>
3865          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3866          * called.
3867          * </p>
3868          * 
3869          * @param at the path to the node that is to be created.
3870          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3871          *         node where the node is to be created
3872          */
3873         public final Create<Batch> create( Path at ) {
3874             assertNotExecuted();
3875             CheckArg.isNotNull(at, "at");
3876             Path parent = at.getParent();
3877             Name name = at.getLastSegment().getName();
3878             return create(Location.create(parent), name);
3879         }
3880 
3881         protected final CreateAction<Batch> create( Location parent,
3882                                                     Name child ) {
3883             return new CreateAction<Batch>(this, parent, getCurrentWorkspaceName(), child) {
3884                 @Override
3885                 protected Batch submit( Location parent,
3886                                         String workspaceName,
3887                                         Name childName,
3888                                         Collection<Property> properties,
3889                                         NodeConflictBehavior behavior ) {
3890                     requestQueue.createNode(parent, workspaceName, childName, properties.iterator(), behavior);
3891                     return Batch.this;
3892                 }
3893             };
3894         }
3895 
3896         /**
3897          * Begin the request to create a node located at the supplied path.
3898          * <p>
3899          * Like all other methods on the {@link Batch}, the request will be performed when the {@link Batch#execute()} method is
3900          * called.
3901          * </p>
3902          * 
3903          * @param at the path to the node that is to be created.
3904          * @param properties the iterator over the properties for the new node
3905          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3906          *         node where the node is to be created
3907          */
3908         public Create<Batch> create( Path at,
3909                                      Iterable<Property> properties ) {
3910             Create<Batch> action = create(at);
3911             for (Property property : properties) {
3912                 action.and(property);
3913             }
3914             return action;
3915         }
3916 
3917         /**
3918          * Begin the request to create a node located at the supplied path.
3919          * <p>
3920          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3921          * called.
3922          * </p>
3923          * 
3924          * @param at the path to the node that is to be created.
3925          * @param property a property for the new node
3926          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3927          *         node where the node is to be created
3928          */
3929         public Create<Batch> create( Path at,
3930                                      Property property ) {
3931             return create(at).with(property);
3932         }
3933 
3934         /**
3935          * Begin the request to create a node located at the supplied path.
3936          * <p>
3937          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
3938          * called.
3939          * </p>
3940          * 
3941          * @param at the path to the node that is to be created.
3942          * @param firstProperty a property for the new node
3943          * @param additionalProperties additional properties for the new node
3944          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
3945          *         node where the node is to be created
3946          */
3947         public Create<Batch> create( Path at,
3948                                      Property firstProperty,
3949                                      Property... additionalProperties ) {
3950             return create(at).with(firstProperty, additionalProperties);
3951         }
3952 
3953         /**
3954          * Begin the request to create a node under the existing parent node at the supplied location. This request is submitted
3955          * to the repository after the returned components are completed.
3956          * 
3957          * @param parent the location of the parent
3958          * @return the object used to start creating a node
3959          */
3960         public CreateNodeNamed<Batch> createUnder( Location parent ) {
3961             CheckArg.isNotNull(parent, "parent");
3962             return new CreateNodeNamedAction<Batch>(this, parent) {
3963                 @Override
3964                 protected CreateAction<Batch> createWith( Batch batch,
3965                                                           Location parent,
3966                                                           Name childName ) {
3967                     return Batch.this.create(parent, childName);
3968                 }
3969             };
3970         }
3971 
3972         public AddValue<Batch> addValue( Object value ) {
3973             return new AddValueAction<Batch>(this, this.getCurrentWorkspaceName(), value) {
3974 
3975                 @Override
3976                 protected Batch submit( String workspaceName,
3977                                         Location on,
3978                                         Name property,
3979                                         List<Object> values ) {
3980                     requests.addValues(workspaceName, on, property, values);
3981                     return nextRequests.and();
3982                 }
3983             };
3984         }
3985 
3986         public RemoveValue<Batch> removeValue( Object value ) {
3987             return new RemoveValueAction<Batch>(this, this.getCurrentWorkspaceName(), value) {
3988 
3989                 @Override
3990                 protected Batch submit( String workspaceName,
3991                                         Location on,
3992                                         Name property,
3993                                         List<Object> values ) {
3994                     requests.removeValues(workspaceName, on, property, values);
3995                     return nextRequests.and();
3996                 }
3997             };
3998         }
3999 
4000         /**
4001          * Set the properties on a node.
4002          * 
4003          * @param properties the properties to set
4004          * @return the interface that should be used to specify the node on which the properties are to be set.
4005          */
4006         public On<BatchConjunction> set( final Property... properties ) {
4007             return new On<BatchConjunction>() {
4008                 public BatchConjunction on( Location location ) {
4009                     requestQueue.setProperties(location, getCurrentWorkspaceName(), properties);
4010                     return nextRequests;
4011                 }
4012 
4013                 public BatchConjunction on( String path ) {
4014                     return on(Location.create(createPath(path)));
4015                 }
4016 
4017                 public BatchConjunction on( Path path ) {
4018                     return on(Location.create(path));
4019                 }
4020 
4021                 public BatchConjunction on( Property idProperty ) {
4022                     return on(Location.create(idProperty));
4023                 }
4024 
4025                 public BatchConjunction on( Property firstIdProperty,
4026                                             Property... additionalIdProperties ) {
4027                     return on(Location.create(firstIdProperty, additionalIdProperties));
4028                 }
4029 
4030                 public BatchConjunction on( Iterable<Property> idProperties ) {
4031                     return on(Location.create(idProperties));
4032                 }
4033 
4034                 public BatchConjunction on( UUID uuid ) {
4035                     return on(Location.create(uuid));
4036                 }
4037             };
4038         }
4039 
4040         /**
4041          * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
4042          * value(s) and the location of the node onto which the property should be set.
4043          * 
4044          * @param propertyName the property name
4045          * @return the interface used to specify the values
4046          */
4047         public SetValues<BatchConjunction> set( String propertyName ) {
4048             Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
4049             return set(name);
4050         }
4051 
4052         /**
4053          * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
4054          * value(s) and the location of the node onto which the property should be set.
4055          * 
4056          * @param propertyName the property name
4057          * @return the interface used to specify the values
4058          */
4059         public SetValues<BatchConjunction> set( final Name propertyName ) {
4060             return new SetValues<BatchConjunction>() {
4061                 public SetValuesTo<BatchConjunction> on( final Location location ) {
4062                     return new SetValuesTo<BatchConjunction>() {
4063                         public BatchConjunction to( Node value ) {
4064                             return to(value.getLocation());
4065                         }
4066 
4067                         public BatchConjunction to( Location value ) {
4068                             Reference ref = (Reference)convertReferenceValue(value);
4069                             Property property = getContext().getPropertyFactory().create(propertyName, ref);
4070                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4071                             return nextRequests;
4072                         }
4073 
4074                         protected BatchConjunction toValue( Object value ) {
4075                             Property property = getContext().getPropertyFactory().create(propertyName, value);
4076                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4077                             return nextRequests;
4078                         }
4079 
4080                         public BatchConjunction to( String value ) {
4081                             return toValue(value);
4082                         }
4083 
4084                         public BatchConjunction to( int value ) {
4085                             return toValue(Integer.valueOf(value));
4086                         }
4087 
4088                         public BatchConjunction to( long value ) {
4089                             return toValue(Long.valueOf(value));
4090                         }
4091 
4092                         public BatchConjunction to( boolean value ) {
4093                             return toValue(Boolean.valueOf(value));
4094                         }
4095 
4096                         public BatchConjunction to( float value ) {
4097                             return toValue(Float.valueOf(value));
4098                         }
4099 
4100                         public BatchConjunction to( double value ) {
4101                             return toValue(Double.valueOf(value));
4102                         }
4103 
4104                         public BatchConjunction to( BigDecimal value ) {
4105                             return toValue(value);
4106                         }
4107 
4108                         public BatchConjunction to( Calendar value ) {
4109                             return toValue(value);
4110                         }
4111 
4112                         public BatchConjunction to( Date value ) {
4113                             return toValue(value);
4114                         }
4115 
4116                         public BatchConjunction to( DateTime value ) {
4117                             return toValue(value);
4118                         }
4119 
4120                         public BatchConjunction to( Name value ) {
4121                             return toValue(value);
4122                         }
4123 
4124                         public BatchConjunction to( Path value ) {
4125                             return toValue(value);
4126                         }
4127 
4128                         public BatchConjunction to( Reference value ) {
4129                             return toValue(value);
4130                         }
4131 
4132                         public BatchConjunction to( URI value ) {
4133                             return toValue(value);
4134                         }
4135 
4136                         public BatchConjunction to( UUID value ) {
4137                             return toValue(value);
4138                         }
4139 
4140                         public BatchConjunction to( Binary value ) {
4141                             return toValue(value);
4142                         }
4143 
4144                         public BatchConjunction to( byte[] value ) {
4145                             return toValue(value);
4146                         }
4147 
4148                         public BatchConjunction to( InputStream stream,
4149                                                     long approximateLength ) {
4150                             Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
4151                             return toValue(value);
4152                         }
4153 
4154                         public BatchConjunction to( Reader reader,
4155                                                     long approximateLength ) {
4156                             Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
4157                             return toValue(value);
4158                         }
4159 
4160                         public BatchConjunction to( Object value ) {
4161                             value = convertReferenceValue(value);
4162                             Property property = getContext().getPropertyFactory().create(propertyName, value);
4163                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4164                             return nextRequests;
4165                         }
4166 
4167                         public BatchConjunction to( Object firstValue,
4168                                                     Object... otherValues ) {
4169                             firstValue = convertReferenceValue(firstValue);
4170                             for (int i = 0, len = otherValues.length; i != len; ++i) {
4171                                 otherValues[i] = convertReferenceValue(otherValues[i]);
4172                             }
4173                             Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
4174                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4175                             return nextRequests;
4176                         }
4177 
4178                         public BatchConjunction to( Object[] values ) {
4179                             for (int i = 0; i != values.length; ++i) {
4180                                 values[i] = convertReferenceValue(values[i]);
4181                             }
4182                             Property property = getContext().getPropertyFactory().create(propertyName, values);
4183                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4184                             return nextRequests;
4185                         }
4186 
4187                         public BatchConjunction to( Iterable<?> values ) {
4188                             List<Object> valueList = new LinkedList<Object>();
4189                             for (Object value : values) {
4190                                 value = convertReferenceValue(value);
4191                                 valueList.add(value);
4192                             }
4193                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
4194                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4195                             return nextRequests;
4196                         }
4197 
4198                         public BatchConjunction to( Iterator<?> values ) {
4199                             List<Object> valueList = new LinkedList<Object>();
4200                             while (values.hasNext()) {
4201                                 Object value = values.next();
4202                                 valueList.add(value);
4203                             }
4204                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
4205                             requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
4206                             return nextRequests;
4207                         }
4208 
4209                     };
4210                 }
4211 
4212                 public SetValuesTo<BatchConjunction> on( String path ) {
4213                     return on(Location.create(createPath(path)));
4214                 }
4215 
4216                 public SetValuesTo<BatchConjunction> on( Path path ) {
4217                     return on(Location.create(path));
4218                 }
4219 
4220                 public SetValuesTo<BatchConjunction> on( Property idProperty ) {
4221                     return on(Location.create(idProperty));
4222                 }
4223 
4224                 public SetValuesTo<BatchConjunction> on( Property firstIdProperty,
4225                                                          Property... additionalIdProperties ) {
4226                     return on(Location.create(firstIdProperty, additionalIdProperties));
4227                 }
4228 
4229                 public SetValuesTo<BatchConjunction> on( Iterable<Property> idProperties ) {
4230                     return on(Location.create(idProperties));
4231                 }
4232 
4233                 public SetValuesTo<BatchConjunction> on( UUID uuid ) {
4234                     return on(Location.create(uuid));
4235                 }
4236 
4237                 public On<BatchConjunction> to( Node value ) {
4238                     Object reference = convertReferenceValue(value);
4239                     return set(getContext().getPropertyFactory().create(propertyName, reference));
4240                 }
4241 
4242                 public On<BatchConjunction> to( Location value ) {
4243                     Object reference = convertReferenceValue(value);
4244                     return set(getContext().getPropertyFactory().create(propertyName, reference));
4245                 }
4246 
4247                 protected On<BatchConjunction> toValue( Object value ) {
4248                     return set(getContext().getPropertyFactory().create(propertyName, value));
4249                 }
4250 
4251                 public On<BatchConjunction> to( String value ) {
4252                     return toValue(value);
4253                 }
4254 
4255                 public On<BatchConjunction> to( int value ) {
4256                     return toValue(Integer.valueOf(value));
4257                 }
4258 
4259                 public On<BatchConjunction> to( long value ) {
4260                     return toValue(Long.valueOf(value));
4261                 }
4262 
4263                 public On<BatchConjunction> to( boolean value ) {
4264                     return toValue(Boolean.valueOf(value));
4265                 }
4266 
4267                 public On<BatchConjunction> to( float value ) {
4268                     return toValue(Float.valueOf(value));
4269                 }
4270 
4271                 public On<BatchConjunction> to( double value ) {
4272                     return toValue(Double.valueOf(value));
4273                 }
4274 
4275                 public On<BatchConjunction> to( BigDecimal value ) {
4276                     return toValue(value);
4277                 }
4278 
4279                 public On<BatchConjunction> to( Calendar value ) {
4280                     return toValue(value);
4281                 }
4282 
4283                 public On<BatchConjunction> to( Date value ) {
4284                     return toValue(value);
4285                 }
4286 
4287                 public On<BatchConjunction> to( DateTime value ) {
4288                     return toValue(value);
4289                 }
4290 
4291                 public On<BatchConjunction> to( Name value ) {
4292                     return toValue(value);
4293                 }
4294 
4295                 public On<BatchConjunction> to( Path value ) {
4296                     return toValue(value);
4297                 }
4298 
4299                 public On<BatchConjunction> to( Reference value ) {
4300                     return toValue(value);
4301                 }
4302 
4303                 public On<BatchConjunction> to( URI value ) {
4304                     return toValue(value);
4305                 }
4306 
4307                 public On<BatchConjunction> to( UUID value ) {
4308                     return toValue(value);
4309                 }
4310 
4311                 public On<BatchConjunction> to( Binary value ) {
4312                     return toValue(value);
4313                 }
4314 
4315                 public On<BatchConjunction> to( byte[] value ) {
4316                     return toValue(value);
4317                 }
4318 
4319                 public On<BatchConjunction> to( InputStream stream,
4320                                                 long approximateLength ) {
4321                     Binary value = getContext().getValueFactories().getBinaryFactory().create(stream, approximateLength);
4322                     return toValue(value);
4323                 }
4324 
4325                 public On<BatchConjunction> to( Reader reader,
4326                                                 long approximateLength ) {
4327                     Binary value = getContext().getValueFactories().getBinaryFactory().create(reader, approximateLength);
4328                     return toValue(value);
4329                 }
4330 
4331                 public On<BatchConjunction> to( Object value ) {
4332                     value = convertReferenceValue(value);
4333                     return set(getContext().getPropertyFactory().create(propertyName, value));
4334                 }
4335 
4336                 public On<BatchConjunction> to( Object firstValue,
4337                                                 Object... otherValues ) {
4338                     Object[] values = new Object[otherValues.length + 1];
4339                     values[0] = convertReferenceValue(firstValue);
4340                     for (int i = 0, len = otherValues.length; i != len; ++i) {
4341                         values[i + 1] = convertReferenceValue(otherValues[i]);
4342                     }
4343                     return set(getContext().getPropertyFactory().create(propertyName, values));
4344                 }
4345 
4346                 public On<BatchConjunction> to( Object[] values ) {
4347                     for (int i = 0, len = values.length; i != len; ++i) {
4348                         values[i] = convertReferenceValue(values[i]);
4349                     }
4350                     return set(getContext().getPropertyFactory().create(propertyName, values));
4351                 }
4352 
4353                 public On<BatchConjunction> to( Iterable<?> values ) {
4354                     List<Object> valueList = new LinkedList<Object>();
4355                     for (Object value : values) {
4356                         value = convertReferenceValue(value);
4357                         valueList.add(value);
4358                     }
4359                     return set(getContext().getPropertyFactory().create(propertyName, valueList));
4360                 }
4361 
4362                 public On<BatchConjunction> to( Iterator<?> values ) {
4363                     List<Object> valueList = new LinkedList<Object>();
4364                     while (values.hasNext()) {
4365                         Object value = values.next();
4366                         valueList.add(value);
4367                     }
4368                     return set(getContext().getPropertyFactory().create(propertyName, valueList));
4369                 }
4370             };
4371         }
4372 
4373         /**
4374          * Remove properties from the node at the given location.
4375          * 
4376          * @param propertyNames the names of the properties to be removed
4377          * @return the remove request object that should be used to specify the node from which the properties are to be removed.
4378          */
4379         public On<BatchConjunction> remove( final Name... propertyNames ) {
4380             return new On<BatchConjunction>() {
4381                 public BatchConjunction on( Location location ) {
4382                     requestQueue.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
4383                     return nextRequests;
4384                 }
4385 
4386                 public BatchConjunction on( String path ) {
4387                     return on(Location.create(createPath(path)));
4388                 }
4389 
4390                 public BatchConjunction on( Path path ) {
4391                     return on(Location.create(path));
4392                 }
4393 
4394                 public BatchConjunction on( Property idProperty ) {
4395                     return on(Location.create(idProperty));
4396                 }
4397 
4398                 public BatchConjunction on( Property firstIdProperty,
4399                                             Property... additionalIdProperties ) {
4400                     return on(Location.create(firstIdProperty, additionalIdProperties));
4401                 }
4402 
4403                 public BatchConjunction on( Iterable<Property> idProperties ) {
4404                     return on(Location.create(idProperties));
4405                 }
4406 
4407                 public BatchConjunction on( UUID uuid ) {
4408                     return on(Location.create(uuid));
4409                 }
4410             };
4411         }
4412 
4413         /**
4414          * Remove properties from the node at the given location.
4415          * 
4416          * @param propertyNames the names of the properties to be removed
4417          * @return the remove request object that should be used to specify the node from which the properties are to be removed.
4418          */
4419         public On<BatchConjunction> remove( String... propertyNames ) {
4420             NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
4421             int number = propertyNames.length;
4422             final Name[] names = new Name[number];
4423             for (int i = 0; i != number; ++i) {
4424                 names[i] = nameFactory.create(propertyNames[i]);
4425             }
4426             return new On<BatchConjunction>() {
4427                 public BatchConjunction on( Location location ) {
4428                     requestQueue.removeProperties(location, getCurrentWorkspaceName(), names);
4429                     return nextRequests;
4430                 }
4431 
4432                 public BatchConjunction on( String path ) {
4433                     return on(Location.create(createPath(path)));
4434                 }
4435 
4436                 public BatchConjunction on( Path path ) {
4437                     return on(Location.create(path));
4438                 }
4439 
4440                 public BatchConjunction on( Property idProperty ) {
4441                     return on(Location.create(idProperty));
4442                 }
4443 
4444                 public BatchConjunction on( Property firstIdProperty,
4445                                             Property... additionalIdProperties ) {
4446                     return on(Location.create(firstIdProperty, additionalIdProperties));
4447                 }
4448 
4449                 public BatchConjunction on( Iterable<Property> idProperties ) {
4450                     return on(Location.create(idProperties));
4451                 }
4452 
4453                 public BatchConjunction on( UUID uuid ) {
4454                     return on(Location.create(uuid));
4455                 }
4456             };
4457         }
4458 
4459         /**
4460          * Request to read the node with the supplied UUID.
4461          * <p>
4462          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4463          * called.
4464          * </p>
4465          * 
4466          * @param uuid the UUID of the node that is to be read
4467          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4468          */
4469         public BatchConjunction read( UUID uuid ) {
4470             return read(Location.create(uuid));
4471         }
4472 
4473         /**
4474          * Request to read the node at the supplied location.
4475          * <p>
4476          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4477          * called.
4478          * </p>
4479          * 
4480          * @param location the location of the node that is to be read
4481          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4482          */
4483         public BatchConjunction read( Location location ) {
4484             assertNotExecuted();
4485             requestQueue.readNode(location, getCurrentWorkspaceName());
4486             return nextRequests;
4487         }
4488 
4489         /**
4490          * Request to read the node at the supplied path.
4491          * <p>
4492          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4493          * called.
4494          * </p>
4495          * 
4496          * @param path the path of the node that is to be read
4497          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4498          */
4499         public BatchConjunction read( String path ) {
4500             return read(Location.create(createPath(path)));
4501         }
4502 
4503         /**
4504          * Request to read the node at the supplied path.
4505          * <p>
4506          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4507          * called.
4508          * </p>
4509          * 
4510          * @param path the path of the node that is to be read
4511          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4512          */
4513         public BatchConjunction read( Path path ) {
4514             return read(Location.create(path));
4515         }
4516 
4517         /**
4518          * Request to read the node with the supplied unique identifier property.
4519          * <p>
4520          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4521          * called.
4522          * </p>
4523          * 
4524          * @param idProperty the identification property that is unique to the node that is to be read
4525          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4526          */
4527         public BatchConjunction read( Property idProperty ) {
4528             return read(Location.create(idProperty));
4529         }
4530 
4531         /**
4532          * Request to read the node with the supplied unique identifier properties.
4533          * <p>
4534          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4535          * called.
4536          * </p>
4537          * 
4538          * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
4539          * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be
4540          *        read
4541          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4542          */
4543         public BatchConjunction read( Property firstIdProperty,
4544                                       Property... additionalIdProperties ) {
4545             return read(Location.create(firstIdProperty, additionalIdProperties));
4546         }
4547 
4548         /**
4549          * Request to read the node with the supplied unique identifier properties.
4550          * <p>
4551          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4552          * called.
4553          * </p>
4554          * 
4555          * @param idProperties the identification properties that uniquely identify the node that is to be read
4556          * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
4557          */
4558         public BatchConjunction read( Iterable<Property> idProperties ) {
4559             return read(Location.create(idProperties));
4560         }
4561 
4562         /**
4563          * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
4564          * returned {@link On} object.
4565          * <p>
4566          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4567          * called.
4568          * </p>
4569          * 
4570          * @param propertyName the name of the property that is to be read
4571          * @return the object that is used to specified the node whose property is to be read
4572          */
4573         public On<BatchConjunction> readProperty( String propertyName ) {
4574             assertNotExecuted();
4575             Name name = Graph.this.getContext().getValueFactories().getNameFactory().create(propertyName);
4576             return readProperty(name);
4577         }
4578 
4579         /**
4580          * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
4581          * returned {@link On} object.
4582          * <p>
4583          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4584          * called.
4585          * </p>
4586          * 
4587          * @param name the name of the property that is to be read
4588          * @return the object that is used to specified the node whose property is to be read
4589          */
4590         public On<BatchConjunction> readProperty( final Name name ) {
4591             assertNotExecuted();
4592             return new On<BatchConjunction>() {
4593                 public BatchConjunction on( String path ) {
4594                     return on(Location.create(createPath(path)));
4595                 }
4596 
4597                 public BatchConjunction on( Path path ) {
4598                     return on(Location.create(path));
4599                 }
4600 
4601                 public BatchConjunction on( Property idProperty ) {
4602                     return on(Location.create(idProperty));
4603                 }
4604 
4605                 public BatchConjunction on( Property firstIdProperty,
4606                                             Property... additionalIdProperties ) {
4607                     return on(Location.create(firstIdProperty, additionalIdProperties));
4608                 }
4609 
4610                 public BatchConjunction on( Iterable<Property> idProperties ) {
4611                     return on(Location.create(idProperties));
4612                 }
4613 
4614                 public BatchConjunction on( UUID uuid ) {
4615                     return on(Location.create(uuid));
4616                 }
4617 
4618                 public BatchConjunction on( Location at ) {
4619                     requestQueue.readProperty(at, getCurrentWorkspaceName(), name);
4620                     return Batch.this.nextRequests;
4621                 }
4622             };
4623         }
4624 
4625         /**
4626          * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
4627          * object.
4628          * <p>
4629          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4630          * called.
4631          * </p>
4632          * 
4633          * @return the object that is used to specified the node whose properties are to be read,
4634          */
4635         public On<BatchConjunction> readProperties() {
4636             assertNotExecuted();
4637             return new On<BatchConjunction>() {
4638                 public BatchConjunction on( Location location ) {
4639                     requestQueue.readAllProperties(location, getCurrentWorkspaceName());
4640                     return Batch.this.nextRequests;
4641                 }
4642 
4643                 public BatchConjunction on( String path ) {
4644                     return on(Location.create(createPath(path)));
4645                 }
4646 
4647                 public BatchConjunction on( Path path ) {
4648                     return on(Location.create(path));
4649                 }
4650 
4651                 public BatchConjunction on( Property idProperty ) {
4652                     return on(Location.create(idProperty));
4653                 }
4654 
4655                 public BatchConjunction on( Property firstIdProperty,
4656                                             Property... additionalIdProperties ) {
4657                     return on(Location.create(firstIdProperty, additionalIdProperties));
4658                 }
4659 
4660                 public BatchConjunction on( Iterable<Property> idProperties ) {
4661                     return on(Location.create(idProperties));
4662                 }
4663 
4664                 public BatchConjunction on( UUID uuid ) {
4665                     return on(Location.create(uuid));
4666                 }
4667             };
4668         }
4669 
4670         /**
4671          * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
4672          * object.
4673          * <p>
4674          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4675          * called.
4676          * </p>
4677          * 
4678          * @return the object that is used to specified the node whose children are to be read
4679          */
4680         public Of<BatchConjunction> readChildren() {
4681             assertNotExecuted();
4682             return new Of<BatchConjunction>() {
4683                 public BatchConjunction of( String path ) {
4684                     return of(Location.create(createPath(path)));
4685                 }
4686 
4687                 public BatchConjunction of( Path path ) {
4688                     return of(Location.create(path));
4689                 }
4690 
4691                 public BatchConjunction of( Property idProperty ) {
4692                     return of(Location.create(idProperty));
4693                 }
4694 
4695                 public BatchConjunction of( Property firstIdProperty,
4696                                             Property... additionalIdProperties ) {
4697                     return of(Location.create(firstIdProperty, additionalIdProperties));
4698                 }
4699 
4700                 public BatchConjunction of( Iterable<Property> idProperties ) {
4701                     return of(Location.create(idProperties));
4702                 }
4703 
4704                 public BatchConjunction of( UUID uuid ) {
4705                     return of(Location.create(uuid));
4706                 }
4707 
4708                 public BatchConjunction of( Location at ) {
4709                     requestQueue.readAllChildren(at, getCurrentWorkspaceName());
4710                     return Batch.this.nextRequests;
4711                 }
4712             };
4713         }
4714 
4715         /**
4716          * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code>
4717          * in the resulting {@link At} object.
4718          * <p>
4719          * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
4720          * called.
4721          * </p>
4722          * 
4723          * @param depth the maximum depth of the subgraph that should be read
4724          * @return the component that should be used to specify the location of the node that is the top of the subgraph
4725          */
4726         public At<BatchConjunction> readSubgraphOfDepth( final int depth ) {
4727             assertNotExecuted();
4728             return new At<BatchConjunction>() {
4729                 public BatchConjunction at( Location location ) {
4730                     requestQueue.readBranch(location, getCurrentWorkspaceName(), depth);
4731                     return Batch.this.nextRequests;
4732                 }
4733 
4734                 public BatchConjunction at( String path ) {
4735                     return at(Location.create(createPath(path)));
4736                 }
4737 
4738                 public BatchConjunction at( Path path ) {
4739                     return at(Location.create(path));
4740                 }
4741 
4742                 public BatchConjunction at( UUID uuid ) {
4743                     return at(Location.create(uuid));
4744                 }
4745 
4746                 public BatchConjunction at( Property idProperty ) {
4747                     return at(Location.create(idProperty));
4748                 }
4749 
4750                 public BatchConjunction at( Property firstIdProperty,
4751                                             Property... additionalIdProperties ) {
4752                     return at(Location.create(firstIdProperty, additionalIdProperties));
4753                 }
4754 
4755                 public BatchConjunction at( Iterable<Property> idProperties ) {
4756                     return at(Location.create(idProperties));
4757                 }
4758             };
4759         }
4760 
4761         /**
4762          * {@inheritDoc}
4763          * 
4764          * @see org.modeshape.graph.Graph.Executable#execute()
4765          */
4766         public Results execute() {
4767             executed = true;
4768             Request request = requestQueue.pop();
4769             if (request == null) {
4770                 return new BatchResults();
4771             }
4772             Graph.this.execute(request);
4773             if (RequestType.COMPOSITE == request.getType()) {
4774                 CompositeRequest composite = (CompositeRequest)request;
4775                 return new BatchResults(composite.getRequests());
4776             }
4777             return new BatchResults(request);
4778         }
4779 
4780         /**
4781          * {@inheritDoc}
4782          * 
4783          * @see java.lang.Object#toString()
4784          */
4785         @Override
4786         public String toString() {
4787             StringBuilder sb = new StringBuilder();
4788             sb.append("Pending requests:\n");
4789             sb.append(requestQueue.toString());
4790             return sb.toString();
4791         }
4792     }
4793 
4794     /**
4795      * Utility method for checking a property value. If the value is a {@link Node} or {@link Location}, a {@link Reference} value
4796      * is created (if the node/location has a UUID); otherwise, the value is returned as is.
4797      * 
4798      * @param value the property value
4799      * @return the property value, which may be a {@link Reference} if the input value is a Node or Location
4800      */
4801     protected Object convertReferenceValue( Object value ) {
4802         if (value instanceof Node) {
4803             Node node = (Node)value;
4804             UUID uuid = node.getLocation().getUuid();
4805             if (uuid == null) {
4806                 // Look for a property ...
4807                 Property uuidProperty = node.getProperty(ModeShapeLexicon.UUID);
4808                 if (uuidProperty != null) {
4809                     uuid = getContext().getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
4810                 } else {
4811                     uuidProperty = node.getProperty(JcrLexicon.UUID);
4812                     if (uuidProperty != null) {
4813                         uuid = getContext().getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
4814                     }
4815                 }
4816             }
4817             if (uuid == null) {
4818                 String nodeString = node.getLocation().getString(getContext().getNamespaceRegistry());
4819                 String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
4820                 throw new IllegalArgumentException(msg);
4821             }
4822             return getContext().getValueFactories().getReferenceFactory().create(uuid);
4823         }
4824         if (value instanceof Location) {
4825             Location location = (Location)value;
4826             UUID uuid = location.getUuid();
4827             if (uuid == null) {
4828                 String nodeString = location.getString(getContext().getNamespaceRegistry());
4829                 String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
4830                 throw new IllegalArgumentException(msg);
4831             }
4832             return getContext().getValueFactories().getReferenceFactory().create(uuid);
4833         }
4834         return value;
4835     }
4836 
4837     protected static DateTime computeExpirationTime( CacheableRequest request ) {
4838         CachePolicy policy = request.getCachePolicy();
4839         return policy == null ? null : request.getTimeLoaded().plus(policy.getTimeToLive(), TimeUnit.MILLISECONDS);
4840     }
4841 
4842     /**
4843      * The interface used to complete a query submission.
4844      */
4845     public interface BuildQuery {
4846         /**
4847          * Use the supplied hints when executing the query.
4848          * 
4849          * @param hints the hints
4850          * @return this same interface for method chaining purposes; never null
4851          * @throws IllegalArgumentException if the hints reference is null
4852          */
4853         BuildQuery using( PlanHints hints );
4854 
4855         /**
4856          * Use the supplied variables when executing the query.
4857          * 
4858          * @param variables the variables
4859          * @return this same interface for method chaining purposes; never null
4860          * @throws IllegalArgumentException if the variables reference is null
4861          */
4862         BuildQuery using( Map<String, Object> variables );
4863 
4864         /**
4865          * Use the supplied value for the given variable name when executing the query.
4866          * 
4867          * @param variableName the variable value
4868          * @param value the value to replace the variable during execution
4869          * @return this same interface for method chaining purposes; never null
4870          * @throws IllegalArgumentException if the variable name is null
4871          */
4872         BuildQuery using( String variableName,
4873                           Object value );
4874 
4875         /**
4876          * Execute the query and get the results.
4877          * 
4878          * @return the query results
4879          */
4880         QueryResults execute();
4881     }
4882 
4883     /**
4884      * The interface used to specify the name of a new workspace.
4885      */
4886     public interface NameWorkspace {
4887 
4888         /**
4889          * Specify the name of the new workspace that is to be created.
4890          * 
4891          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4892          * @return the workspace; never null
4893          * @throws IllegalArgumentException if the name of the new workspace is null
4894          * @throws InvalidWorkspaceException if there is already an existing workspace with the supplied name
4895          */
4896         Workspace named( String workspaceName );
4897 
4898         /**
4899          * Specify the name of the new workspace that is to be created. If a workspace with the supplied name already exists, the
4900          * new workspace name will be adjusted so that it is unique.
4901          * 
4902          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4903          * @return the workspace; never null
4904          * @throws IllegalArgumentException if the name of the new workspace is null
4905          */
4906         Workspace namedSomethingLike( String workspaceName );
4907     }
4908 
4909     /**
4910      * The interface used to create a new workspace.
4911      */
4912     public interface CreateWorkspace extends NameWorkspace {
4913         /**
4914          * Specify that the new workspace should be initialized as a clone of another existing workspace.
4915          * 
4916          * @param originalWorkspaceName the name of the existing workspace that will be cloned to create the new workspace;
4917          * @return the interface that should be used to set the name of the new workspace; never null
4918          * @throws IllegalArgumentException if the name of the original workspace is null
4919          * @throws InvalidWorkspaceException if there is no such workspace with the supplied name
4920          */
4921         NameWorkspace clonedFrom( String originalWorkspaceName );
4922     }
4923 
4924     /**
4925      * The interface used to destroy a workspace.
4926      */
4927     public interface DestroyWorkspace {
4928         /**
4929          * Specify the name of the new workspace that is to be destroyed.
4930          * 
4931          * @param workspaceName the name of the existing workspace that will be cloned to create the new workspace;
4932          * @return true if the workspace was destroyed, or false otherwise
4933          * @throws IllegalArgumentException if the name of the workspace is null
4934          * @throws InvalidWorkspaceException if there is no existing workspace with the supplied name
4935          */
4936         boolean named( String workspaceName );
4937     }
4938 
4939     /**
4940      * A interface used to execute the accumulated {@link Batch requests}.
4941      * 
4942      * @param <NodeType> the type of node that is returned
4943      */
4944     public interface Executable<NodeType extends Node> {
4945         /**
4946          * Stop accumulating the requests, submit them to the repository source, and return the results.
4947          * 
4948          * @return the results containing the requested information from the repository.
4949          * @throws PathNotFoundException if a request used a node that did not exist
4950          * @throws InvalidRequestException if a request was not valid
4951          * @throws InvalidWorkspaceException if the workspace used in a request was not valid
4952          * @throws UnsupportedRequestException if a request was not supported by the source
4953          * @throws RepositorySourceException if an error occurs during execution
4954          * @throws RuntimeException if a runtime error occurs during execution
4955          */
4956         Results execute();
4957     }
4958 
4959     /**
4960      * A interface that can be used to finish the current request and start another.
4961      * 
4962      * @param <Next> the interface that will be used to start another request
4963      */
4964     public interface Conjunction<Next> {
4965         /**
4966          * Finish the request and prepare to start another.
4967          * 
4968          * @return the interface that can be used to start another request; never null
4969          */
4970         Next and();
4971     }
4972 
4973     /**
4974      * A component that defines the location into which a node should be copied or moved.
4975      * 
4976      * @param <Next> The interface that is to be returned when this request is completed
4977      */
4978     public interface Into<Next> {
4979         /**
4980          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
4981          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
4982          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
4983          * {@link To#to(Location)} instead.
4984          * 
4985          * @param parentLocation the location of the new parent
4986          * @return the interface for additional requests or actions
4987          * @see To#to(Location)
4988          */
4989         Next into( Location parentLocation );
4990 
4991         /**
4992          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
4993          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
4994          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
4995          * {@link To#to(String)} instead.
4996          * 
4997          * @param parentPath the path of the new parent
4998          * @return the interface for additional requests or actions
4999          * @see To#to(String)
5000          */
5001         Next into( String parentPath );
5002 
5003         /**
5004          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5005          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5006          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5007          * {@link To#to(Path)} instead.
5008          * 
5009          * @param parentPath the path of the new parent
5010          * @return the interface for additional requests or actions
5011          * @see To#to(Path)
5012          */
5013         Next into( Path parentPath );
5014 
5015         /**
5016          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5017          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5018          * same-name-sibling index).
5019          * 
5020          * @param parentUuid the UUID of the new parent
5021          * @return the interface for additional requests or actions
5022          */
5023         Next into( UUID parentUuid );
5024 
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).
5029          * 
5030          * @param parentIdProperty the property that uniquely identifies the new parent
5031          * @return the interface for additional requests or actions
5032          */
5033         Next into( Property parentIdProperty );
5034 
5035         /**
5036          * Finish the request by specifying the location of the parent into which the node should be copied/moved. This operation
5037          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5038          * same-name-sibling index).
5039          * 
5040          * @param firstParentIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies
5041          *        the new parent
5042          * @param additionalParentIdProperties the additional properties that, with the <code>additionalIdProperties</code>,
5043          *        uniquely identifies the new parent
5044          * @return the interface for additional requests or actions
5045          */
5046         Next into( Property firstParentIdProperty,
5047                    Property... additionalParentIdProperties );
5048     }
5049 
5050     /**
5051      * A component that defines the location before which a node should be copied or moved. This is similar to an {@link Into},
5052      * but it allows for placing a node at a particular location within the new destination, rather than always placing the moved
5053      * or copied node as the last child of the new parent.
5054      * 
5055      * @param <Next> The interface that is to be returned when this request is completed
5056      */
5057     public interface Before<Next> {
5058         /**
5059          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5060          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5061          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5062          * {@link To#to(Location)} instead.
5063          * 
5064          * @param parentLocation the location of the new parent
5065          * @return the interface for additional requests or actions
5066          * @see To#to(Location)
5067          */
5068         Next before( Location parentLocation );
5069 
5070         /**
5071          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5072          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5073          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5074          * {@link To#to(String)} instead.
5075          * 
5076          * @param parentPath the path of the new parent
5077          * @return the interface for additional requests or actions
5078          * @see To#to(String)
5079          */
5080         Next before( String parentPath );
5081 
5082         /**
5083          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5084          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5085          * same-name-sibling index). If you want to control the name of the node for the newly copied/moved node, use
5086          * {@link To#to(Path)} instead.
5087          * 
5088          * @param parentPath the path of the new parent
5089          * @return the interface for additional requests or actions
5090          * @see To#to(Path)
5091          */
5092         Next before( Path parentPath );
5093 
5094         /**
5095          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5096          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5097          * same-name-sibling index).
5098          * 
5099          * @param parentUuid the UUID of the new parent
5100          * @return the interface for additional requests or actions
5101          */
5102         Next before( UUID parentUuid );
5103 
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).
5108          * 
5109          * @param parentIdProperty the property that uniquely identifies the new parent
5110          * @return the interface for additional requests or actions
5111          */
5112         Next before( Property parentIdProperty );
5113 
5114         /**
5115          * Finish the request by specifying the location of the node before which the node should be copied/moved. This operation
5116          * will result in the copied/moved node having the same name as the original (but with the appropriately-determined
5117          * same-name-sibling index).
5118          * 
5119          * @param firstParentIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies
5120          *        the new parent
5121          * @param additionalParentIdProperties the additional properties that, with the <code>additionalIdProperties</code>,
5122          *        uniquely identifies the new parent
5123          * @return the interface for additional requests or actions
5124          */
5125         Next before( Property firstParentIdProperty,
5126                      Property... additionalParentIdProperties );
5127     }
5128 
5129     /**
5130      * A component that defines the name of a property to which a value should be added.
5131      * 
5132      * @param <Next> The interface that is to be returned when this request is completed
5133      */
5134     public interface ToName<Next> {
5135 
5136         public Next to( String name );
5137 
5138         public Next to( Name name );
5139 
5140     }
5141 
5142     /**
5143      * A component that defines the name of a property from which a value should be removed.
5144      * 
5145      * @param <Next> The interface that is to be returned when this request is completed
5146      */
5147     public interface FromName<Next> {
5148 
5149         public Next from( String name );
5150 
5151         public Next from( Name name );
5152 
5153     }
5154 
5155     /**
5156      * A component that defines the location to which a node should be copied or moved.
5157      * 
5158      * @param <Next> The interface that is to be returned when this request is completed
5159      */
5160     public interface To<Next> {
5161         /**
5162          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5163          * {@link Into#into(Location)}, which specifies the location of the parent and which assumes the new node should have the
5164          * same name as the original, this method allows the caller to specify a new name for the new node.
5165          * 
5166          * @param desiredLocation the desired location for the new node, which must have a {@link Location#getPath() path}
5167          * @return the interface for additional requests or actions
5168          * @see Into#into(Location)
5169          */
5170         Next to( Location desiredLocation );
5171 
5172         /**
5173          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5174          * {@link Into#into(String)}, which specifies the location of the parent and which assumes the new node should have the
5175          * same name as the original, this method allows the caller to specify a new name for the new node.
5176          * 
5177          * @param desiredPath the path for the new node
5178          * @return the interface for additional requests or actions
5179          * @see Into#into(String)
5180          */
5181         Next to( String desiredPath );
5182 
5183         /**
5184          * Finish the request by specifying the Location.create where the node should be copied/moved. Unlike
5185          * {@link Into#into(Path)} , which specifies the location of the parent and which assumes the new node should have the
5186          * same name as the original, this method allows the caller to specify a new name for the new node.
5187          * 
5188          * @param desiredPath the path for the new node
5189          * @return the interface for additional requests or actions
5190          * @see Into#into(Path)
5191          */
5192         Next to( Path desiredPath );
5193     }
5194 
5195     /**
5196      * A component that defines a new child name for a node.
5197      * 
5198      * @param <Next> The interface that is to be returned when this request is completed
5199      */
5200     public interface AsChild<Next> {
5201         /**
5202          * Finish the request by specifying the exact segment for the new child node. If no segment already exists, this request
5203          * should fail.
5204          * 
5205          * @param newSegment the new name
5206          * @return the interface for additional requests or actions
5207          */
5208         Next as( Path.Segment newSegment );
5209 
5210         /**
5211          * Finish the request by specifying the name of the new child node. This method indicates that the child should be added
5212          * as a new node with the given name at the end of the parents children
5213          * 
5214          * @param newName the new name
5215          * @return the interface for additional requests or actions
5216          */
5217         Next as( Name newName );
5218 
5219         /**
5220          * Finish the request by specifying the name of the new child node. This method indicates that the child should be added
5221          * as a new node with the given name at the end of the parents children
5222          * 
5223          * @param newName the new name
5224          * @return the interface for additional requests or actions
5225          */
5226         Next as( String newName );
5227     }
5228 
5229     /**
5230      * A component that defines a new name for a node.
5231      * 
5232      * @param <Next> The interface that is to be returned when this request is completed
5233      */
5234     public interface AsName<Next> {
5235         /**
5236          * Finish the request by specifying the new name.
5237          * 
5238          * @param newName the new name
5239          * @return the interface for additional requests or actions
5240          */
5241         Next as( String newName );
5242 
5243         /**
5244          * Finish the request by specifying the new name.
5245          * 
5246          * @param newName the new name
5247          * @return the interface for additional requests or actions
5248          */
5249         Next as( Name newName );
5250     }
5251 
5252     /**
5253      * A interface that is used to add more locations that are to be copied/moved.
5254      * 
5255      * @param <Next> The interface that is to be returned when this request is completed
5256      */
5257     public interface And<Next> {
5258         /**
5259          * Specify that another node should also be copied or moved.
5260          * 
5261          * @param from the location of the node to be copied or moved
5262          * @return the interface for finishing the request
5263          */
5264         Next and( Location from );
5265 
5266         /**
5267          * Specify that another node should also be copied or moved.
5268          * 
5269          * @param fromPath the path of the node to be copied or moved
5270          * @return the interface for finishing the request
5271          */
5272         Next and( String fromPath );
5273 
5274         /**
5275          * Specify that another node should also be copied or moved.
5276          * 
5277          * @param from the path of the node to be copied or moved
5278          * @return the interface for finishing the request
5279          */
5280         Next and( Path from );
5281 
5282         /**
5283          * Specify that another node should also be copied or moved.
5284          * 
5285          * @param from the UUID of the node to be copied or moved
5286          * @return the interface for finishing the request
5287          */
5288         Next and( UUID from );
5289 
5290         /**
5291          * Specify that another node should also be copied or moved.
5292          * 
5293          * @param idProperty the property that uniquely identifies the node to be copied or moved
5294          * @return the interface for finishing the request
5295          */
5296         Next and( Property idProperty );
5297 
5298         /**
5299          * Specify that another node should also be copied or moved.
5300          * 
5301          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5302          *        node to be copied or moved
5303          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5304          *        identifies the node to be copied or moved
5305          * @return the interface for finishing the request
5306          */
5307         Next and( Property firstIdProperty,
5308                   Property... additionalIdProperties );
5309 
5310         /**
5311          * Specify that another node should also be copied or moved.
5312          * 
5313          * @param idProperties the properties that uniquely identifies the node to be copied or moved
5314          * @return the interface for finishing the request
5315          */
5316         Next and( Iterable<Property> idProperties );
5317     }
5318 
5319     /**
5320      * The interface for defining additional nodes to be moved and the parent into which the node(s) are to be moved.
5321      * 
5322      * @param <Next> The interface that is to be returned when this request is completed
5323      */
5324     public interface Move<Next> extends AsName<Into<Next>>, Into<Next>, Before<Next>, And<Move<Next>> {
5325     }
5326 
5327     /**
5328      * The interface for defining additional nodes to be copied and the locations where the copy is to be placed. The
5329      * <code>to(...)</code> methods allow you to specify the location of the copy, including the name for the node that results
5330      * from the copy. Alternatively, you can use the <code>into(...)</code> methods to specify the parent location where the copy
5331      * is to be placed, which will assume the new copy will have the same name as the original.
5332      * 
5333      * @param <Next> The interface that is to be returned when this request is completed
5334      */
5335     public interface Copy<Next> extends FromWorkspace<CopyTarget<Next>>, CopyTarget<Next>, And<Copy<Next>> {
5336     }
5337 
5338     public interface CopyTarget<Next> extends To<Next>, Into<Next> {
5339     }
5340 
5341     /**
5342      * The interface for defining a branch of nodes to be cloned and the location where the clone is to be placed. Cloning a
5343      * branch differs from copying a branch in that:
5344      * <ol>
5345      * <li>Nodes can be copied within the same workspace or to another workspace; cloned nodes must be copied from one workspace
5346      * into another.</li>
5347      * <li>Copied nodes always get new UUIDs; cloned nodes always maintain their UUIDs and hence must define the behavior that
5348      * occurs if a node with the same UUID already exists in the new workspace.</li>
5349      * <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
5350      * the new parent's children; nodes can be cloned to an exact location among the parent's children, replacing the existing
5351      * node at that location.</li>
5352      * </ol>
5353      * 
5354      * @param <Next>
5355      */
5356     public interface Clone<Next> extends FromWorkspace<AsChild<Into<WithUuids<Next>>>> {
5357 
5358     }
5359 
5360     /**
5361      * The interface for specifying that a node should come from a workspace other than the current workspace.
5362      * 
5363      * @param <Next> The interface that is to be returned when this request is completed
5364      */
5365     public interface FromWorkspace<Next> {
5366         Next fromWorkspace( String workspaceName );
5367     }
5368 
5369     /**
5370      * The interface for specifying how UUID conflicts should be handled.
5371      * 
5372      * @param <Next> The interface that is to be returned when this request is completed
5373      */
5374     public interface WithUuids<Next> {
5375         Next failingIfAnyUuidsMatch();
5376 
5377         Next replacingExistingNodesWithSameUuids();
5378     }
5379 
5380     /**
5381      * Interface for specifying whether a lock should be deep in scope
5382      * 
5383      * @param <Next> The interface that is to be returned when this create request is completed
5384      */
5385     public interface LockScope<Next> {
5386         Next andItsDescendants();
5387 
5388         Next only();
5389     }
5390 
5391     /**
5392      * Interface for specifying whether the maximum length of the lock
5393      * 
5394      * @param <Next> The interface that is to be returned when this create request is completed
5395      */
5396     public interface LockTimeout<Next> {
5397         Next withDefaultTimeout();
5398 
5399         Next withTimeoutOf( long milliseconds );
5400     }
5401 
5402     @NotThreadSafe
5403     protected abstract class LockAction<T> extends AbstractAction<T> implements LockScope<LockTimeout<T>> {
5404         private final Location target;
5405 
5406         /*package*/LockAction( T afterConjunction,
5407                                 Location target ) {
5408             super(afterConjunction);
5409             this.target = target;
5410         }
5411 
5412         protected abstract T submit( Location lock,
5413                                      org.modeshape.graph.request.LockBranchRequest.LockScope lockScope,
5414                                      long lockTimeoutInMillis );
5415 
5416         @SuppressWarnings( "synthetic-access" )
5417         public LockTimeout<T> andItsDescendants() {
5418             return new LockTimeout<T>() {
5419 
5420                 public T withDefaultTimeout() {
5421                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS, 0);
5422                 }
5423 
5424                 public T withTimeoutOf( long milliseconds ) {
5425                     return submit(target,
5426                                   org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS,
5427                                   milliseconds);
5428                 }
5429 
5430             };
5431         }
5432 
5433         @SuppressWarnings( "synthetic-access" )
5434         public LockTimeout<T> only() {
5435             return new LockTimeout<T>() {
5436 
5437                 public T withDefaultTimeout() {
5438                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_ONLY, 0);
5439                 }
5440 
5441                 public T withTimeoutOf( long milliseconds ) {
5442                     return submit(target, org.modeshape.graph.request.LockBranchRequest.LockScope.SELF_ONLY, milliseconds);
5443                 }
5444 
5445             };
5446         }
5447 
5448     }
5449 
5450     /**
5451      * The interface for defining additional properties on a new node.
5452      * 
5453      * @param <Next> The interface that is to be returned when this create request is completed
5454      */
5455     public interface Create<Next> extends Conjunction<Next> {
5456         /**
5457          * Create the node only if there is no existing node with the same {@link Path.Segment#getName() name} (ignoring
5458          * {@link Path.Segment#getIndex() same-name-sibling indexes}).
5459          * 
5460          * @return this interface for continued specification of the request
5461          */
5462         Create<Next> ifAbsent();
5463 
5464         /**
5465          * Create the node if it does not exist, or update any existing node that has the same {@link Path.Segment#getName() name}
5466          * (ignoring {@link Path.Segment#getIndex() same-name-sibling indexes}).
5467          * 
5468          * @return this interface for continued specification of the request
5469          */
5470         Create<Next> orUpdate();
5471 
5472         /**
5473          * Create the node if it does not exist, or replace any existing node that has the same {@link Path.Segment#getName()
5474          * name} (ignoring {@link Path.Segment#getIndex() same-name-sibling indexes}).
5475          * 
5476          * @return this interface for continued specification of the request
5477          */
5478         Create<Next> orReplace();
5479 
5480         /**
5481          * Create the node if it does not exist by appending or adjusting the {@link Path.Segment#getIndex() same-name-sibling
5482          * index}). This is the default behavior.
5483          * 
5484          * @return this interface for continued specification of the request
5485          */
5486         Create<Next> byAppending();
5487 
5488         /**
5489          * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
5490          * 
5491          * @param uuid the UUID
5492          * @return this same interface so additional properties may be added
5493          */
5494         Create<Next> with( UUID uuid );
5495 
5496         /**
5497          * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
5498          * 
5499          * @param property the property
5500          * @return this same interface so additional properties may be added
5501          */
5502         Create<Next> with( Property property );
5503 
5504         /**
5505          * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
5506          * 
5507          * @param properties the properties that should be added
5508          * @return this same interface so additional properties may be added
5509          */
5510         Create<Next> with( Iterable<Property> properties );
5511 
5512         /**
5513          * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
5514          * 
5515          * @param propertyName the name of the property
5516          * @param values the property values
5517          * @return this same interface so additional properties may be added
5518          */
5519         Create<Next> with( String propertyName,
5520                            Object... values );
5521 
5522         /**
5523          * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
5524          * 
5525          * @param propertyName the name of the property
5526          * @param values the property values
5527          * @return this same interface so additional properties may be added
5528          */
5529         Create<Next> with( Name propertyName,
5530                            Object... values );
5531 
5532         /**
5533          * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
5534          * 
5535          * @param firstProperty the first property
5536          * @param additionalProperties the additional property
5537          * @return this same interface so additional properties may be added
5538          */
5539         Create<Next> with( Property firstProperty,
5540                            Property... additionalProperties );
5541 
5542         /**
5543          * Specify the UUID that should the new node should have.
5544          * 
5545          * @param uuid the UUID
5546          * @return this same interface so additional properties may be added
5547          */
5548         Create<Next> and( UUID uuid );
5549 
5550         /**
5551          * Specify a property that should the new node should have.
5552          * 
5553          * @param property the property
5554          * @return this same interface so additional properties may be added
5555          */
5556         Create<Next> and( Property property );
5557 
5558         /**
5559          * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
5560          * the properties in the supplied {@link Iterable}.
5561          * 
5562          * @param properties the properties that should be added
5563          * @return this same interface so additional properties may be added
5564          */
5565         Create<Next> and( Iterable<Property> properties );
5566 
5567         /**
5568          * Specify a property that should the new node should have.
5569          * 
5570          * @param propertyName the name of the property
5571          * @param values the property values
5572          * @return this same interface so additional properties may be added
5573          */
5574         Create<Next> and( String propertyName,
5575                           Object... values );
5576 
5577         /**
5578          * Specify a property that should the new node should have.
5579          * 
5580          * @param propertyName the name of the property
5581          * @param values the property values
5582          * @return this same interface so additional properties may be added
5583          */
5584         Create<Next> and( Name propertyName,
5585                           Object... values );
5586 
5587         /**
5588          * Specify properties that should the new node should have.
5589          * 
5590          * @param firstProperty the first property
5591          * @param additionalProperties the additional property
5592          * @return this same interface so additional properties may be added
5593          */
5594         Create<Next> and( Property firstProperty,
5595                           Property... additionalProperties );
5596     }
5597 
5598     /**
5599      * The interface for defining additional properties on a new node.
5600      * 
5601      * @param <Next> The interface that is to be returned when this create request is completed
5602      */
5603     public interface CreateAt<Next> extends Conjunction<Next> {
5604         /**
5605          * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
5606          * 
5607          * @param uuid the UUID
5608          * @return this same interface so additional properties may be added
5609          */
5610         CreateAt<Next> with( UUID uuid );
5611 
5612         /**
5613          * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
5614          * 
5615          * @param property the property
5616          * @return this same interface so additional properties may be added
5617          */
5618         CreateAt<Next> with( Property property );
5619 
5620         /**
5621          * Specify property that should the new node should have. This is an alias for {@link #and(Iterable)}.
5622          * 
5623          * @param properties the properties that should be added
5624          * @return this same interface so additional properties may be added
5625          */
5626         CreateAt<Next> with( Iterable<Property> properties );
5627 
5628         /**
5629          * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
5630          * 
5631          * @param propertyName the name of the property
5632          * @param values the property values
5633          * @return this same interface so additional properties may be added
5634          */
5635         CreateAt<Next> with( String propertyName,
5636                              Object... values );
5637 
5638         /**
5639          * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
5640          * 
5641          * @param propertyName the name of the property
5642          * @param values the property values
5643          * @return this same interface so additional properties may be added
5644          */
5645         CreateAt<Next> with( Name propertyName,
5646                              Object... values );
5647 
5648         /**
5649          * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
5650          * 
5651          * @param firstProperty the first property
5652          * @param additionalProperties the additional property
5653          * @return this same interface so additional properties may be added
5654          */
5655         CreateAt<Next> with( Property firstProperty,
5656                              Property... additionalProperties );
5657 
5658         /**
5659          * Specify the UUID that should the new node should have.
5660          * 
5661          * @param uuid the UUID
5662          * @return this same interface so additional properties may be added
5663          */
5664         CreateAt<Next> and( UUID uuid );
5665 
5666         /**
5667          * Specify a property that should the new node should have.
5668          * 
5669          * @param property the property
5670          * @return this same interface so additional properties may be added
5671          */
5672         CreateAt<Next> and( Property property );
5673 
5674         /**
5675          * Specify property that should the new node should have. This is equivalent to calling {@link #and(Property)} for each of
5676          * the properties in the supplied {@link Iterable}.
5677          * 
5678          * @param properties the properties that should be added
5679          * @return this same interface so additional properties may be added
5680          */
5681         CreateAt<Next> and( Iterable<Property> properties );
5682 
5683         /**
5684          * Specify a property that should the new node should have.
5685          * 
5686          * @param propertyName the name of the property
5687          * @param values the property values
5688          * @return this same interface so additional properties may be added
5689          */
5690         CreateAt<Next> and( String propertyName,
5691                             Object... values );
5692 
5693         /**
5694          * Specify a property that should the new node should have.
5695          * 
5696          * @param propertyName the name of the property
5697          * @param values the property values
5698          * @return this same interface so additional properties may be added
5699          */
5700         CreateAt<Next> and( Name propertyName,
5701                             Object... values );
5702 
5703         /**
5704          * Specify properties that should the new node should have.
5705          * 
5706          * @param firstProperty the first property
5707          * @param additionalProperties the additional property
5708          * @return this same interface so additional properties may be added
5709          */
5710         CreateAt<Next> and( Property firstProperty,
5711                             Property... additionalProperties );
5712 
5713         /**
5714          * Complete this request, submit it, and return the actual location of the created node.
5715          * 
5716          * @return the actual location of the just-created node; never null
5717          */
5718         Location getLocation();
5719 
5720         /**
5721          * Complete this request, submit it, and return the actual node that was created.
5722          * 
5723          * @return the actual node that was just created; never null
5724          */
5725         Node getNode();
5726     }
5727 
5728     /**
5729      * The interface for defining the node upon which a request operates.
5730      * 
5731      * @param <Next> The interface that is to be returned when the request is completed
5732      */
5733     public interface On<Next> {
5734         /**
5735          * Specify the location of the node upon which the request is to operate.
5736          * 
5737          * @param to the location of the new parent
5738          * @return the interface for additional requests or actions
5739          */
5740         Next on( Location to );
5741 
5742         /**
5743          * Specify the path of the node upon which the request is to operate.
5744          * 
5745          * @param toPath the path of the new parent
5746          * @return the interface for additional requests or actions
5747          */
5748         Next on( String toPath );
5749 
5750         /**
5751          * Specify the path of the node upon which the request is to operate.
5752          * 
5753          * @param to the path of the new parent
5754          * @return the interface for additional requests or actions
5755          */
5756         Next on( Path to );
5757 
5758         /**
5759          * Specify the UUID of the node upon which the request is to operate.
5760          * 
5761          * @param to the UUID of the new parent
5762          * @return the interface for additional requests or actions
5763          */
5764         Next on( UUID to );
5765 
5766         /**
5767          * Specify the unique identification property that identifies the node upon which the request is to operate.
5768          * 
5769          * @param idProperty the property that uniquely identifies the new parent
5770          * @return the interface for additional requests or actions
5771          */
5772         Next on( Property idProperty );
5773 
5774         /**
5775          * Specify the unique identification properties that identify the node upon which the request is to operate.
5776          * 
5777          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5778          *        new parent
5779          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5780          *        identifies the new parent
5781          * @return the interface for additional requests or actions
5782          */
5783         Next on( Property firstIdProperty,
5784                  Property... additionalIdProperties );
5785 
5786         /**
5787          * Specify the unique identification properties that identify the node upon which the request is to operate.
5788          * 
5789          * @param idProperties the properties that uniquely identifies the new parent
5790          * @return the interface for additional requests or actions
5791          */
5792         Next on( Iterable<Property> idProperties );
5793     }
5794 
5795     /**
5796      * The interface for defining the node upon which a request operates, including a method that accepts multiple locations.
5797      * 
5798      * @param <Next> The interface that is to be returned when the request is completed
5799      */
5800     public interface OnMultiple<Next> extends On<Next> {
5801         /**
5802          * Specify the location of each node upon which the requests are to operate.
5803          * 
5804          * @param to the locations
5805          * @return the interface for additional requests or actions
5806          */
5807         Map<Location, Next> on( Collection<Location> to );
5808 
5809         /**
5810          * Specify the location of each node upon which the requests are to operate.
5811          * 
5812          * @param firstTo the first location
5813          * @param additional the additional location
5814          * @return the interface for additional requests or actions
5815          */
5816         Map<Location, Next> on( Location firstTo,
5817                                 Location... additional );
5818 
5819         /**
5820          * Specify the path of each node upon which the requests are to operate.
5821          * 
5822          * @param firstPath the first path
5823          * @param additional the additional path
5824          * @return the interface for additional requests or actions
5825          */
5826         Map<Location, Next> on( String firstPath,
5827                                 String... additional );
5828 
5829         /**
5830          * Specify the path of each node upon which the requests are to operate.
5831          * 
5832          * @param firstPath the first path
5833          * @param additional the additional path
5834          * @return the interface for additional requests or actions
5835          */
5836         Map<Location, Next> on( Path firstPath,
5837                                 Path... additional );
5838 
5839         /**
5840          * Specify the UUID of each node upon which the requests are to operate.
5841          * 
5842          * @param firstPath the first UUID of the node
5843          * @param additional the additional UUIDs
5844          * @return the interface for additional requests or actions
5845          */
5846         Map<Location, Next> on( UUID firstPath,
5847                                 UUID... additional );
5848     }
5849 
5850     /**
5851      * The interface for defining the node upon which a request operates.
5852      * 
5853      * @param <Next> The interface that is to be returned when the request is completed
5854      */
5855     public interface Of<Next> {
5856         /**
5857          * Specify the location of the node upon which the request is to operate.
5858          * 
5859          * @param to the location of the new parent
5860          * @return the interface for additional requests or actions
5861          */
5862         Next of( Location to );
5863 
5864         /**
5865          * Specify the path of the node upon which the request is to operate.
5866          * 
5867          * @param toPath the path of the new parent
5868          * @return the interface for additional requests or actions
5869          */
5870         Next of( String toPath );
5871 
5872         /**
5873          * Specify the path of the node upon which the request is to operate.
5874          * 
5875          * @param to the path of the new parent
5876          * @return the interface for additional requests or actions
5877          */
5878         Next of( Path to );
5879 
5880         /**
5881          * Specify the UUID of the node upon which the request is to operate.
5882          * 
5883          * @param to the UUID of the new parent
5884          * @return the interface for additional requests or actions
5885          */
5886         Next of( UUID to );
5887 
5888         /**
5889          * Specify the unique identification property that identifies the node upon which the request is to operate.
5890          * 
5891          * @param idProperty the property that uniquely identifies the new parent
5892          * @return the interface for additional requests or actions
5893          */
5894         Next of( Property idProperty );
5895 
5896         /**
5897          * Specify the unique identification properties that identify the node upon which the request is to operate.
5898          * 
5899          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5900          *        new parent
5901          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5902          *        identifies the new parent
5903          * @return the interface for additional requests or actions
5904          */
5905         Next of( Property firstIdProperty,
5906                  Property... additionalIdProperties );
5907 
5908         /**
5909          * Specify the unique identification properties that identify the node upon which the request is to operate.
5910          * 
5911          * @param idProperties the properties that uniquely identifies the new parent
5912          * @return the interface for additional requests or actions
5913          */
5914         Next of( Iterable<Property> idProperties );
5915     }
5916 
5917     /**
5918      * The interface for defining the node upon which which a request operates.
5919      * 
5920      * @param <Next> The interface that is to be returned when the request is completed
5921      */
5922     public interface At<Next> {
5923         /**
5924          * Specify the location of the node upon which the request is to operate.
5925          * 
5926          * @param to the location of the new parent
5927          * @return the interface for additional requests or actions
5928          */
5929         Next at( Location to );
5930 
5931         /**
5932          * Specify the path of the node upon which the request is to operate.
5933          * 
5934          * @param toPath the path of the new parent
5935          * @return the interface for additional requests or actions
5936          */
5937         Next at( String toPath );
5938 
5939         /**
5940          * Specify the path of the node upon which the request is to operate.
5941          * 
5942          * @param to the path of the new parent
5943          * @return the interface for additional requests or actions
5944          */
5945         Next at( Path to );
5946 
5947         /**
5948          * Specify the UUID of the node upon which the request is to operate.
5949          * 
5950          * @param to the UUID of the new parent
5951          * @return the interface for additional requests or actions
5952          */
5953         Next at( UUID to );
5954 
5955         /**
5956          * Specify the unique identification property that identifies the node upon which the request is to operate.
5957          * 
5958          * @param idProperty the property that uniquely identifies the new parent
5959          * @return the interface for additional requests or actions
5960          */
5961         Next at( Property idProperty );
5962 
5963         /**
5964          * Specify the unique identification properties that identify the node upon which the request is to operate.
5965          * 
5966          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
5967          *        new parent
5968          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
5969          *        identifies the new parent
5970          * @return the interface for additional requests or actions
5971          */
5972         Next at( Property firstIdProperty,
5973                  Property... additionalIdProperties );
5974 
5975         /**
5976          * Specify the unique identification properties that identify the node upon which the request is to operate.
5977          * 
5978          * @param idProperties the properties that uniquely identifies the new parent
5979          * @return the interface for additional requests or actions
5980          */
5981         Next at( Iterable<Property> idProperties );
5982     }
5983 
5984     /**
5985      * A component used to supply the details for getting children of another node. If all of the children are to be obtained,
5986      * then the parent can be specified using one of the <code>of(...)</code> methods on this component. If, however, only some of
5987      * the nodes are to be returned (e.g., a "block" of children), then specify the {@link #inBlockOf(int) block size} followed by
5988      * the {@link BlockOfChildren block size and parent}.
5989      * 
5990      * @param <Next>
5991      */
5992     public interface Children<Next> extends Of<Next> {
5993         /**
5994          * Specify that a block of children are to be retreived, and in particular the number of children that are to be returned.
5995          * 
5996          * @param blockSize the number of children that are to be retrieved in the block; must be positive
5997          * @return the interface used to specify the starting point for the block and the parent
5998          */
5999         BlockOfChildren<Next> inBlockOf( int blockSize );
6000     }
6001 
6002     /**
6003      * A component used to specify a block of children starting either {@link #startingAt(int) at a particular index} or
6004      * {@link #startingAfter(Location) after a previous sibling}.
6005      * 
6006      * @param <Next>
6007      */
6008     public interface BlockOfChildren<Next> {
6009         /**
6010          * Specify the block of children is to start at the supplied index.
6011          * 
6012          * @param startingIndex the zero-based index of the first child to be returned in the block
6013          * @return interface used to specify the parent of the children; never null
6014          */
6015         Under<Next> startingAt( int startingIndex );
6016 
6017         /**
6018          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6019          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6020          * block.
6021          * 
6022          * @param previousSibling the location of the sibling node that is before the first node in the block
6023          * @return the children; never null
6024          */
6025         Next startingAfter( Location previousSibling );
6026 
6027         /**
6028          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6029          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6030          * block.
6031          * 
6032          * @param pathToPreviousSiblingName the path of the sibling node that is before the first node in the block
6033          * @return the children; never null
6034          */
6035         Next startingAfter( String pathToPreviousSiblingName );
6036 
6037         /**
6038          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6039          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6040          * block.
6041          * 
6042          * @param previousSibling the path of the sibling node that is before the first node in the block
6043          * @return the children; never null
6044          */
6045         Next startingAfter( Path previousSibling );
6046 
6047         /**
6048          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6049          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6050          * block.
6051          * 
6052          * @param previousSiblingUuid the UUID of the sibling node that is before the first node in the block
6053          * @return the children; never null
6054          */
6055         Next startingAfter( UUID previousSiblingUuid );
6056 
6057         /**
6058          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6059          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6060          * block.
6061          * 
6062          * @param idPropertyOfPreviousSibling the property that uniquely identifies the previous sibling
6063          * @return the children; never null
6064          */
6065         Next startingAfter( Property idPropertyOfPreviousSibling );
6066 
6067         /**
6068          * Specify the block of children is to start with the child immediately following the supplied node. This method is
6069          * typically used when a previous block of children has already been retrieved and this request is retrieving the next
6070          * block.
6071          * 
6072          * @param firstIdPropertyOfPreviousSibling the first property that, with the <code>additionalIdProperties</code>, uniquely
6073          *        identifies the previous sibling
6074          * @param additionalIdPropertiesOfPreviousSibling the additional properties that, with the
6075          *        <code>additionalIdProperties</code>, uniquely identifies the previous sibling
6076          * @return the children; never null
6077          */
6078         Next startingAfter( Property firstIdPropertyOfPreviousSibling,
6079                             Property... additionalIdPropertiesOfPreviousSibling );
6080     }
6081 
6082     /**
6083      * The interface for defining the node under which which a request operates.
6084      * 
6085      * @param <Next> The interface that is to be returned when the request is completed
6086      */
6087     public interface Under<Next> {
6088         /**
6089          * Specify the location of the node under which the request is to operate.
6090          * 
6091          * @param to the location of the new parent
6092          * @return the interface for additional requests or actions
6093          */
6094         Next under( Location to );
6095 
6096         /**
6097          * Specify the path of the node under which the request is to operate.
6098          * 
6099          * @param toPath the path of the new parent
6100          * @return the interface for additional requests or actions
6101          */
6102         Next under( String toPath );
6103 
6104         /**
6105          * Specify the path of the node under which the request is to operate.
6106          * 
6107          * @param to the path of the new parent
6108          * @return the interface for additional requests or actions
6109          */
6110         Next under( Path to );
6111 
6112         /**
6113          * Specify the UUID of the node under which the request is to operate.
6114          * 
6115          * @param to the UUID of the new parent
6116          * @return the interface for additional requests or actions
6117          */
6118         Next under( UUID to );
6119 
6120         /**
6121          * Specify the unique identification property that identifies the node under which the request is to operate.
6122          * 
6123          * @param idProperty the property that uniquely identifies the new parent
6124          * @return the interface for additional requests or actions
6125          */
6126         Next under( Property idProperty );
6127 
6128         /**
6129          * Specify the unique identification properties that identify the node under which the request is to operate.
6130          * 
6131          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
6132          *        new parent
6133          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
6134          *        identifies the new parent
6135          * @return the interface for additional requests or actions
6136          */
6137         Next under( Property firstIdProperty,
6138                     Property... additionalIdProperties );
6139     }
6140 
6141     /**
6142      * The interface for defining the node on which an {@link Graph#addValue(Object)} operation applies and what additional values
6143      * (if any) should be added.
6144      * 
6145      * @param <Next> The interface that is to be returned when the request is completed
6146      */
6147     public interface AddValue<Next> extends ToName<On<Next>> {
6148         /**
6149          * Specifies an additional value to be added
6150          * 
6151          * @param value the value to be added
6152          * @return an object that allows additional values to be specified for removal or for their location to be specified
6153          */
6154         AddValue<Next> andValue( Object value );
6155 
6156     }
6157 
6158     /**
6159      * The interface for defining the node on which an {@link Graph#removeValue(Object)} operation applies and what additional
6160      * values (if any) should be removed.
6161      * 
6162      * @param <Next> The interface that is to be returned when the request is completed
6163      */
6164     public interface RemoveValue<Next> extends FromName<On<Next>> {
6165 
6166         /**
6167          * Specifies an additional value to be removed
6168          * 
6169          * @param value the value to be removed
6170          * @return an object that allows additional values to be specified for removal or for their location to be specified
6171          */
6172         RemoveValue<Next> andValue( Object value );
6173 
6174     }
6175 
6176     public abstract class AddValueAction<T> extends AbstractAction<T> implements AddValue<T> {
6177 
6178         protected final String workspaceName;
6179         protected final List<Object> values = new LinkedList<Object>();
6180 
6181         protected AddValueAction( T afterConjunction,
6182                                   String workspaceName,
6183                                   Object firstValue ) {
6184             super(afterConjunction);
6185 
6186             this.workspaceName = workspaceName;
6187             this.values.add(firstValue);
6188         }
6189 
6190         public AddValue<T> andValue( Object nextValue ) {
6191             this.values.add(nextValue);
6192             return this;
6193         }
6194 
6195         public On<T> to( String name ) {
6196             return to(createName(name));
6197         }
6198 
6199         public On<T> to( final Name name ) {
6200             return new On<T>() {
6201 
6202                 public T on( Iterable<Property> idProperties ) {
6203                     return on(Location.create(idProperties));
6204                 }
6205 
6206                 public T on( Location to ) {
6207                     return submit(workspaceName, to, name, values);
6208                 }
6209 
6210                 public T on( Path to ) {
6211                     return on(Location.create(to));
6212                 }
6213 
6214                 public T on( Property firstIdProperty,
6215                              Property... additionalIdProperties ) {
6216                     return on(Location.create(firstIdProperty, additionalIdProperties));
6217                 }
6218 
6219                 public T on( Property idProperty ) {
6220                     return on(Location.create(idProperty));
6221                 }
6222 
6223                 public T on( String toPath ) {
6224                     return on(Location.create(createPath(toPath)));
6225                 }
6226 
6227                 public T on( UUID to ) {
6228                     return on(Location.create(to));
6229                 }
6230             };
6231         }
6232 
6233         protected abstract T submit( String workspaceName,
6234                                      Location on,
6235                                      Name property,
6236                                      List<Object> values );
6237 
6238     }
6239 
6240     public abstract class RemoveValueAction<T> extends AbstractAction<T> implements RemoveValue<T> {
6241 
6242         protected final String workspaceName;
6243         protected final List<Object> values = new LinkedList<Object>();
6244 
6245         protected RemoveValueAction( T afterConjunction,
6246                                      String workspaceName,
6247                                      Object firstValue ) {
6248             super(afterConjunction);
6249 
6250             this.workspaceName = workspaceName;
6251             this.values.add(firstValue);
6252         }
6253 
6254         public RemoveValue<T> andValue( Object nextValue ) {
6255             this.values.add(nextValue);
6256             return this;
6257         }
6258 
6259         public On<T> from( String name ) {
6260             return from(createName(name));
6261         }
6262 
6263         public On<T> from( final Name name ) {
6264             return new On<T>() {
6265 
6266                 public T on( Iterable<Property> idProperties ) {
6267                     return on(Location.create(idProperties));
6268                 }
6269 
6270                 public T on( Location to ) {
6271                     return submit(workspaceName, to, name, values);
6272                 }
6273 
6274                 public T on( Path to ) {
6275                     return on(Location.create(to));
6276                 }
6277 
6278                 public T on( Property firstIdProperty,
6279                              Property... additionalIdProperties ) {
6280                     return on(Location.create(firstIdProperty, additionalIdProperties));
6281                 }
6282 
6283                 public T on( Property idProperty ) {
6284                     return on(Location.create(idProperty));
6285                 }
6286 
6287                 public T on( String toPath ) {
6288                     return on(Location.create(createPath(toPath)));
6289                 }
6290 
6291                 public T on( UUID to ) {
6292                     return on(Location.create(to));
6293                 }
6294             };
6295         }
6296 
6297         protected abstract T submit( String workspaceName,
6298                                      Location on,
6299                                      Name property,
6300                                      List<Object> values );
6301 
6302     }
6303 
6304     /**
6305      * A component used to set the values on a property.
6306      * 
6307      * @param <Next> the next command
6308      */
6309     public interface SetValues<Next> extends On<SetValuesTo<Next>>, SetValuesTo<On<Next>> {
6310     }
6311 
6312     /**
6313      * A component used to set the values on a property.
6314      * 
6315      * @param <Next>
6316      */
6317     public interface SetValuesTo<Next> {
6318 
6319         /**
6320          * 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
6321          * {@link Location#getUuid() UUID}.
6322          * 
6323          * @param node the node to which a reference should be set
6324          * @return the interface for additional requests or actions
6325          * @throws IllegalArgumentException if the value is a Node that has no {@link Location#getUuid() UUID}
6326          */
6327         Next to( Node node );
6328 
6329         /**
6330          * Set the property value to be a reference to the given location. Note that it is an error if the Location does not have
6331          * a {@link Location#getUuid() UUID}.
6332          * 
6333          * @param location the location to which a reference should be set
6334          * @return the interface for additional requests or actions
6335          * @throws IllegalArgumentException if the value is a Location that has no {@link Location#getUuid() UUID}
6336          */
6337         Next to( Location location );
6338 
6339         /**
6340          * Set the property value to the given string.
6341          * 
6342          * @param value the property value
6343          * @return the interface for additional requests or actions
6344          */
6345         Next to( String value );
6346 
6347         /**
6348          * Set the property value to the given integer value.
6349          * 
6350          * @param value the property value
6351          * @return the interface for additional requests or actions
6352          */
6353         Next to( int value );
6354 
6355         /**
6356          * Set the property value to the given long value.
6357          * 
6358          * @param value the property value
6359          * @return the interface for additional requests or actions
6360          */
6361         Next to( long value );
6362 
6363         /**
6364          * Set the property value to the given boolean value.
6365          * 
6366          * @param value the property value
6367          * @return the interface for additional requests or actions
6368          */
6369         Next to( boolean value );
6370 
6371         /**
6372          * Set the property value to the given float value.
6373          * 
6374          * @param value the property value
6375          * @return the interface for additional requests or actions
6376          */
6377         Next to( float value );
6378 
6379         /**
6380          * Set the property value to the given double value.
6381          * 
6382          * @param value the property value
6383          * @return the interface for additional requests or actions
6384          */
6385         Next to( double value );
6386 
6387         /**
6388          * Set the property value to the given decimal value.
6389          * 
6390          * @param value the property value
6391          * @return the interface for additional requests or actions
6392          */
6393         Next to( BigDecimal value );
6394 
6395         /**
6396          * Set the property value to the date given by the supplied calendar.
6397          * 
6398          * @param value the property value
6399          * @return the interface for additional requests or actions
6400          */
6401         Next to( Calendar value );
6402 
6403         /**
6404          * Set the property value to the given date.
6405          * 
6406          * @param value the property value
6407          * @return the interface for additional requests or actions
6408          */
6409         Next to( Date value );
6410 
6411         /**
6412          * Set the property value to the given date-time instant.
6413          * 
6414          * @param value the property value
6415          * @return the interface for additional requests or actions
6416          */
6417         Next to( DateTime value );
6418 
6419         /**
6420          * Set the property value to the given Name.
6421          * 
6422          * @param value the property value
6423          * @return the interface for additional requests or actions
6424          */
6425         Next to( Name value );
6426 
6427         /**
6428          * Set the property value to the given Path.
6429          * 
6430          * @param value the property value
6431          * @return the interface for additional requests or actions
6432          */
6433         Next to( Path value );
6434 
6435         /**
6436          * Set the property value to the given Reference. See also {@link #to(Node)}.
6437          * 
6438          * @param value the property value
6439          * @return the interface for additional requests or actions
6440          */
6441         Next to( Reference value );
6442 
6443         /**
6444          * Set the property value to the given URI.
6445          * 
6446          * @param value the property value
6447          * @return the interface for additional requests or actions
6448          */
6449         Next to( URI value );
6450 
6451         /**
6452          * Set the property value to the given UUID.
6453          * 
6454          * @param value the property value
6455          * @return the interface for additional requests or actions
6456          */
6457         Next to( UUID value );
6458 
6459         /**
6460          * Set the property value to the given binary value.
6461          * 
6462          * @param value the property value
6463          * @return the interface for additional requests or actions
6464          */
6465         Next to( Binary value );
6466 
6467         /**
6468          * Set the property value to the given byte array.
6469          * 
6470          * @param value the property value
6471          * @return the interface for additional requests or actions
6472          */
6473         Next to( byte[] value );
6474 
6475         /**
6476          * Set the property value to the given string.
6477          * 
6478          * @param stream the stream containing the content to be used for the property value
6479          * @param approximateLength the approximate length of the content (in bytes)
6480          * @return the interface for additional requests or actions
6481          */
6482         Next to( InputStream stream,
6483                  long approximateLength );
6484 
6485         /**
6486          * Set the property value to the given string.
6487          * 
6488          * @param reader the reader containing the content to be used for the property value
6489          * @param approximateLength the approximate length of the content (in bytes)
6490          * @return the interface for additional requests or actions
6491          */
6492         Next to( Reader reader,
6493                  long approximateLength );
6494 
6495         /**
6496          * Set the property value to the given object. The supplied <code>value</code> should be a valid property value, or a
6497          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6498          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6499          * 
6500          * @param value the property value
6501          * @return the interface for additional requests or actions
6502          * @throws IllegalArgumentException if the value is a Node or Location that has no {@link Location#getUuid() UUID}
6503          */
6504         Next to( Object value );
6505 
6506         /**
6507          * Set the property values to the given object. Each of the supplied <code>values</code> should be a valid property value,
6508          * or a {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note
6509          * that it is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6510          * 
6511          * @param values the property values
6512          * @return the interface for additional requests or actions
6513          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6514          *         UUID}
6515          */
6516         Next to( Object[] values );
6517 
6518         /**
6519          * Set the property value to the given objects. Each of the supplied values should be a valid property value, or a
6520          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6521          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6522          * 
6523          * @param firstValue the first property value
6524          * @param otherValues the remaining property values
6525          * @return the interface for additional requests or actions
6526          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6527          *         UUID}
6528          */
6529         Next to( Object firstValue,
6530                  Object... otherValues );
6531 
6532         /**
6533          * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
6534          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6535          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6536          * 
6537          * @param values the container for the property values
6538          * @return the interface for additional requests or actions
6539          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6540          *         UUID}
6541          */
6542         Next to( Iterable<?> values );
6543 
6544         /**
6545          * Set the property value to the given object. Each of the supplied values should be a valid property value, or a
6546          * {@link Node} (or {@link Location}) if the property value is to be a reference to that node (or location). Note that it
6547          * is an error if the Node (or Location) does not have a {@link Location#getUuid() UUID}.
6548          * 
6549          * @param values the iterator over the property values
6550          * @return the interface for additional requests or actions
6551          * @throws IllegalArgumentException if the any of the values is a Node or Location that has no {@link Location#getUuid()
6552          *         UUID}
6553          */
6554         Next to( Iterator<?> values );
6555     }
6556 
6557     /**
6558      * A component that defines a node that is to be created.
6559      * 
6560      * @param <Next> The interface that is to be returned to complete the create request
6561      */
6562     public interface CreateNode<Next> {
6563         /**
6564          * Specify the name of the node that is to be created.
6565          * 
6566          * @param nodeName the name of the new node
6567          * @param properties the properties for the new node
6568          * @return the next component for making additional requests.
6569          */
6570         Next node( String nodeName,
6571                    Property... properties );
6572 
6573         /**
6574          * Specify the name of the node that is to be created.
6575          * 
6576          * @param nodeName the name of the new node
6577          * @param properties the properties for the new node
6578          * @return the next component for making additional requests.
6579          */
6580         Next node( String nodeName,
6581                    Iterator<Property> properties );
6582 
6583         /**
6584          * Specify the name of the node that is to be created.
6585          * 
6586          * @param nodeName the name of the new node
6587          * @param properties the properties for the new node
6588          * @return the next component for making additional requests.
6589          */
6590         Next node( String nodeName,
6591                    Iterable<Property> properties );
6592     }
6593 
6594     /**
6595      * A component that defines a node that is to be created.
6596      * 
6597      * @param <Next> The interface that is to be returned to complete the create request
6598      */
6599     public interface CreateNodeNamed<Next> {
6600         /**
6601          * Specify the name of the node that is to be created.
6602          * 
6603          * @param nodeName the name of the new node
6604          * @return the interface used to complete the request
6605          */
6606         Create<Next> nodeNamed( String nodeName );
6607 
6608         /**
6609          * Specify the name of the node that is to be created.
6610          * 
6611          * @param nodeName the name of the new node
6612          * @return the interface used to complete the request
6613          */
6614         Create<Next> nodeNamed( Name nodeName );
6615     }
6616 
6617     /**
6618      * A component that defines the location into which a node should be copied or moved.
6619      * 
6620      * @param <Next> The interface that is to be returned when this request is completed
6621      */
6622     public interface ImportInto<Next> {
6623 
6624         /**
6625          * Specify the name of the XML attribute that should be used as the node name. If this is not specified (or the attribute
6626          * name is null) the importer will look for the "jcr:name" attribute.
6627          * 
6628          * @param nameAttribute the name of the XML attribute containing the name for each node
6629          * @return the interface used to specify the location where the content should be placed
6630          */
6631         ImportInto<Next> usingAttributeForName( String nameAttribute );
6632 
6633         /**
6634          * Specify the name of the XML attribute that should be used as the node name. If this is not specified (or the attribute
6635          * name is null) the importer will look for the "jcr:name" attribute.
6636          * 
6637          * @param nameAttribute the name of the XML attribute containing the name for each node
6638          * @return the interface used to specify the location where the content should be placed
6639          */
6640         ImportInto<Next> usingAttributeForName( Name nameAttribute );
6641 
6642         /**
6643          * Specify the name of the XML attribute that should be used as the node's type. If this is not specified (or the
6644          * attribute name is null) the importer will look for the "jcr:primaryType" attribute.
6645          * 
6646          * @param typeAttribute the name of the XML attribute containing the type for each node
6647          * @return the interface used to specify the location where the content should be placed
6648          */
6649         ImportInto<Next> usingAttributeForType( String typeAttribute );
6650 
6651         /**
6652          * Specify the name of the XML attribute that should be used as the node's type. If this is not specified (or the
6653          * attribute name is null) the importer will look for the "jcr:primaryType" attribute.
6654          * 
6655          * @param typeAttribute the name of the XML attribute containing the type for each node
6656          * @return the interface used to specify the location where the content should be placed
6657          */
6658         ImportInto<Next> usingAttributeForType( Name typeAttribute );
6659 
6660         /**
6661          * Specify whether the root element in the XML document should be skipped (that is, not be represented by a node). By
6662          * default, the root element is not skipped.
6663          * 
6664          * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
6665          * @return the interface used to specify the location where the content should be placed
6666          */
6667         ImportInto<Next> skippingRootElement( boolean skip );
6668 
6669         /**
6670          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6671          * 
6672          * @param to the location of the new parent
6673          * @return the interface for additional requests or actions
6674          * @throws IOException if there is a problem reading the content being imported
6675          * @throws SAXException if there is a problem with the SAX Parser
6676          */
6677         Next into( Location to ) throws IOException, SAXException;
6678 
6679         /**
6680          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6681          * 
6682          * @param toPath the path of the new parent
6683          * @return the interface for additional requests or actions
6684          * @throws IOException if there is a problem reading the content being imported
6685          * @throws SAXException if there is a problem with the SAX Parser
6686          */
6687         Next into( String toPath ) throws IOException, SAXException;
6688 
6689         /**
6690          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6691          * 
6692          * @param to the path of the new parent
6693          * @return the interface for additional requests or actions
6694          * @throws IOException if there is a problem reading the content being imported
6695          * @throws SAXException if there is a problem with the SAX Parser
6696          */
6697         Next into( Path to ) throws IOException, SAXException;
6698 
6699         /**
6700          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6701          * 
6702          * @param to the UUID of the new parent
6703          * @return the interface for additional requests or actions
6704          * @throws IOException if there is a problem reading the content being imported
6705          * @throws SAXException if there is a problem with the SAX Parser
6706          */
6707         Next into( UUID to ) throws IOException, SAXException;
6708 
6709         /**
6710          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6711          * 
6712          * @param idProperty the property that uniquely identifies the new parent
6713          * @return the interface for additional requests or actions
6714          * @throws IOException if there is a problem reading the content being imported
6715          * @throws SAXException if there is a problem with the SAX Parser
6716          */
6717         Next into( Property idProperty ) throws IOException, SAXException;
6718 
6719         /**
6720          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6721          * 
6722          * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
6723          *        new parent
6724          * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
6725          *        identifies the new parent
6726          * @return the interface for additional requests or actions
6727          * @throws IOException if there is a problem reading the content being imported
6728          * @throws SAXException if there is a problem with the SAX Parser
6729          */
6730         Next into( Property firstIdProperty,
6731                    Property... additionalIdProperties ) throws IOException, SAXException;
6732 
6733         /**
6734          * Finish the import by specifying the Location.create into which the node should be copied/moved.
6735          * 
6736          * @param idProperties the properties that uniquely identifies the new parent
6737          * @return the interface for additional requests or actions
6738          * @throws IOException if there is a problem reading the content being imported
6739          * @throws SAXException if there is a problem with the SAX Parser
6740          */
6741         Next into( Iterable<Property> idProperties ) throws IOException, SAXException;
6742     }
6743 
6744     public interface BatchConjunction extends Conjunction<Batch>, Executable<Node> {
6745     }
6746 
6747     public interface GetNodeConjunction<Next> extends Conjunction<Next> {
6748         Node andReturn();
6749     }
6750 
6751     // ----------------------------------------------------------------------------------------------------------------
6752     // Node Implementation
6753     // ----------------------------------------------------------------------------------------------------------------
6754     @Immutable
6755     protected class GraphNode implements Node {
6756         private final ReadNodeRequest request;
6757 
6758         /*package*/GraphNode( ReadNodeRequest request ) {
6759             this.request = request;
6760         }
6761 
6762         public Location getLocation() {
6763             return request.getActualLocationOfNode();
6764         }
6765 
6766         public DateTime getExpirationTime() {
6767             CachePolicy policy = request.getCachePolicy();
6768             return policy == null ? null : request.getTimeLoaded().plus(policy.getTimeToLive(), TimeUnit.MILLISECONDS);
6769         }
6770 
6771         public Graph getGraph() {
6772             return Graph.this;
6773         }
6774 
6775         public Collection<Property> getProperties() {
6776             return request.getProperties();
6777         }
6778 
6779         public Property getProperty( Name name ) {
6780             return getPropertiesByName().get(name);
6781         }
6782 
6783         public Property getProperty( String nameStr ) {
6784             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
6785             return getPropertiesByName().get(name);
6786         }
6787 
6788         public Map<Name, Property> getPropertiesByName() {
6789             return request.getPropertiesByName();
6790         }
6791 
6792         public List<Location> getChildren() {
6793             return request.getChildren();
6794         }
6795 
6796         public boolean hasChildren() {
6797             return request.getChildren().size() > 0;
6798         }
6799 
6800         public List<Segment> getChildrenSegments() {
6801             return getSegments(getChildren());
6802         }
6803 
6804         public Iterator<Location> iterator() {
6805             return request.getChildren().iterator();
6806         }
6807 
6808         @Override
6809         public int hashCode() {
6810             return getLocation().hashCode();
6811         }
6812 
6813         @Override
6814         public boolean equals( Object obj ) {
6815             if (obj instanceof Node) {
6816                 Node that = (Node)obj;
6817                 return this.getLocation().isSame(that.getLocation());
6818             }
6819             return false;
6820         }
6821 
6822         @Override
6823         public String toString() {
6824             return "Node " + getLocation().toString();
6825         }
6826     }
6827 
6828     // ----------------------------------------------------------------------------------------------------------------
6829     // Results implementation for the batched requests
6830     // ----------------------------------------------------------------------------------------------------------------
6831     @Immutable
6832     class BatchResults implements Results {
6833         private final Map<Path, BatchResultsNode> nodes = new HashMap<Path, BatchResultsNode>();
6834         private final List<Request> requests;
6835 
6836         /*package*/BatchResults( List<Request> requests ) {
6837             this.requests = Collections.unmodifiableList(requests);
6838             // Now create the results ...
6839             for (Request request : requests) {
6840                 DateTime expires;
6841                 BatchResultsNode node;
6842 
6843                 switch (request.getType()) {
6844                     case READ_ALL_PROPERTIES:
6845                         ReadAllPropertiesRequest readAll = (ReadAllPropertiesRequest)request;
6846                         expires = computeExpirationTime(readAll);
6847                         getOrCreateNode(readAll.getActualLocationOfNode(), expires).setProperties(readAll.getPropertiesByName());
6848                         break;
6849                     case READ_PROPERTY:
6850                         ReadPropertyRequest read = (ReadPropertyRequest)request;
6851                         expires = computeExpirationTime(read);
6852                         getOrCreateNode(read.getActualLocationOfNode(), expires).addProperty(read.getProperty());
6853                         break;
6854                     case READ_NODE:
6855                         ReadNodeRequest readNode = (ReadNodeRequest)request;
6856                         expires = computeExpirationTime(readNode);
6857                         node = getOrCreateNode(readNode.getActualLocationOfNode(), expires);
6858                         node.setProperties(readNode.getPropertiesByName());
6859                         node.setChildren(readNode.getChildren());
6860                         break;
6861                     case READ_BLOCK_OF_CHILDREN:
6862                         throw new IllegalStateException();
6863                     case READ_ALL_CHILDREN:
6864                         ReadAllChildrenRequest readAllChildren = (ReadAllChildrenRequest)request;
6865                         expires = computeExpirationTime(readAllChildren);
6866                         getOrCreateNode(readAllChildren.getActualLocationOfNode(), expires).setChildren(readAllChildren.getChildren());
6867                         break;
6868                     case READ_BRANCH:
6869                         ReadBranchRequest readBranch = (ReadBranchRequest)request;
6870                         expires = computeExpirationTime(readBranch);
6871                         for (Location location : readBranch) {
6872                             node = getOrCreateNode(location, expires);
6873                             node.setProperties(readBranch.getPropertiesFor(location));
6874                             node.setChildren(readBranch.getChildren(location));
6875                         }
6876                         break;
6877                     default:
6878                         // Do nothing with other request types ...
6879                         break;
6880                 }
6881             }
6882             for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
6883                 entry.getValue().freeze();
6884             }
6885         }
6886 
6887         /*package*/BatchResults( Request request ) {
6888             this.requests = Collections.singletonList(request);
6889             // Now create the results ...
6890             DateTime expires;
6891             BatchResultsNode node;
6892 
6893             switch (request.getType()) {
6894                 case READ_ALL_PROPERTIES:
6895                     ReadAllPropertiesRequest readAll = (ReadAllPropertiesRequest)request;
6896                     expires = computeExpirationTime(readAll);
6897                     getOrCreateNode(readAll.getActualLocationOfNode(), expires).setProperties(readAll.getPropertiesByName());
6898                     break;
6899                 case READ_PROPERTY:
6900                     ReadPropertyRequest read = (ReadPropertyRequest)request;
6901                     expires = computeExpirationTime(read);
6902                     getOrCreateNode(read.getActualLocationOfNode(), expires).addProperty(read.getProperty());
6903                     break;
6904                 case READ_NODE:
6905                     ReadNodeRequest readNode = (ReadNodeRequest)request;
6906                     expires = computeExpirationTime(readNode);
6907                     node = getOrCreateNode(readNode.getActualLocationOfNode(), expires);
6908                     node.setProperties(readNode.getPropertiesByName());
6909                     node.setChildren(readNode.getChildren());
6910                     break;
6911                 case READ_BLOCK_OF_CHILDREN:
6912                     throw new IllegalStateException();
6913                 case READ_ALL_CHILDREN:
6914                     ReadAllChildrenRequest readAllChildren = (ReadAllChildrenRequest)request;
6915                     expires = computeExpirationTime(readAllChildren);
6916                     getOrCreateNode(readAllChildren.getActualLocationOfNode(), expires).setChildren(readAllChildren.getChildren());
6917                     break;
6918                 case READ_BRANCH:
6919                     ReadBranchRequest readBranch = (ReadBranchRequest)request;
6920                     expires = computeExpirationTime(readBranch);
6921                     for (Location location : readBranch) {
6922                         node = getOrCreateNode(location, expires);
6923                         node.setProperties(readBranch.getPropertiesFor(location));
6924                         node.setChildren(readBranch.getChildren(location));
6925                     }
6926                     break;
6927                 default:
6928                     // Do nothing with other request types ...
6929                     break;
6930             }
6931             for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
6932                 entry.getValue().freeze();
6933             }
6934         }
6935 
6936         BatchResults() {
6937             this.requests = Collections.emptyList();
6938         }
6939 
6940         /**
6941          * {@inheritDoc}
6942          * 
6943          * @see org.modeshape.graph.Results#getRequests()
6944          */
6945         public List<Request> getRequests() {
6946             return requests;
6947         }
6948 
6949         private BatchResultsNode getOrCreateNode( Location location,
6950                                                   DateTime expirationTime ) {
6951             BatchResultsNode node = nodes.get(location);
6952             if (node == null) {
6953                 node = new BatchResultsNode(location, expirationTime);
6954                 assert location != null;
6955                 assert location.getPath() != null;
6956                 nodes.put(location.getPath(), node);
6957             }
6958             return node;
6959         }
6960 
6961         public Graph getGraph() {
6962             return Graph.this;
6963         }
6964 
6965         protected void checkIsAbsolute( Path path ) {
6966             if (!path.isAbsolute()) {
6967                 throw new IllegalArgumentException(GraphI18n.pathIsNotAbsolute.text(path));
6968             }
6969         }
6970 
6971         public Node getNode( String pathStr ) {
6972             Path path = createPath(pathStr);
6973             checkIsAbsolute(path);
6974             return nodes.get(path);
6975         }
6976 
6977         public Node getNode( Path path ) {
6978             CheckArg.isNotNull(path, "path");
6979             checkIsAbsolute(path);
6980             return nodes.get(path);
6981         }
6982 
6983         public Node getNode( Location location ) {
6984             CheckArg.isNotNull(location, "location");
6985             CheckArg.isNotNull(location.getPath(), "location.getPath()");
6986             return nodes.get(location.getPath());
6987         }
6988 
6989         public boolean includes( String path ) {
6990             return getNode(path) != null;
6991         }
6992 
6993         public boolean includes( Path path ) {
6994             return getNode(path) != null;
6995         }
6996 
6997         public boolean includes( Location location ) {
6998             return getNode(location) != null;
6999         }
7000 
7001         public Iterator<Node> iterator() {
7002             List<Path> paths = new ArrayList<Path>(nodes.keySet());
7003             Collections.sort(paths);
7004             final Iterator<Path> pathIter = paths.iterator();
7005             return new Iterator<Node>() {
7006                 public boolean hasNext() {
7007                     return pathIter.hasNext();
7008                 }
7009 
7010                 public Node next() {
7011                     Path nextPath = pathIter.next();
7012                     return getNode(nextPath);
7013                 }
7014 
7015                 public void remove() {
7016                     throw new UnsupportedOperationException();
7017                 }
7018             };
7019         }
7020     }
7021 
7022     @Immutable
7023     class BatchResultsNode implements Node {
7024         private final Location location;
7025         private final DateTime expirationTime;
7026         private Map<Name, Property> properties;
7027         private List<Location> children;
7028 
7029         BatchResultsNode( Location location,
7030                           DateTime expirationTime ) {
7031             this.location = location;
7032             this.expirationTime = expirationTime;
7033         }
7034 
7035         public DateTime getExpirationTime() {
7036             return expirationTime;
7037         }
7038 
7039         void addProperty( Property property ) {
7040             if (this.properties == null) this.properties = new HashMap<Name, Property>();
7041             this.properties.put(property.getName(), property);
7042         }
7043 
7044         void setProperties( Map<Name, Property> properties ) {
7045             this.properties = properties;
7046         }
7047 
7048         void setChildren( List<Location> children ) {
7049             this.children = children;
7050         }
7051 
7052         void freeze() {
7053             if (properties != null) properties = Collections.unmodifiableMap(properties);
7054             else properties = Collections.emptyMap();
7055             if (children != null) children = Collections.unmodifiableList(children);
7056             else children = Collections.emptyList();
7057         }
7058 
7059         public List<Segment> getChildrenSegments() {
7060             return getSegments(getChildren());
7061         }
7062 
7063         public Graph getGraph() {
7064             return Graph.this;
7065         }
7066 
7067         public Location getLocation() {
7068             return location;
7069         }
7070 
7071         public Collection<Property> getProperties() {
7072             return properties.values();
7073         }
7074 
7075         public Map<Name, Property> getPropertiesByName() {
7076             return properties;
7077         }
7078 
7079         public Property getProperty( Name name ) {
7080             return properties.get(name);
7081         }
7082 
7083         public Property getProperty( String nameStr ) {
7084             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
7085             return properties.get(name);
7086         }
7087 
7088         public List<Location> getChildren() {
7089             return children;
7090         }
7091 
7092         public boolean hasChildren() {
7093             return children.size() != 0;
7094         }
7095 
7096         public Iterator<Location> iterator() {
7097             return children.iterator();
7098         }
7099 
7100         @Override
7101         public int hashCode() {
7102             return location.hashCode();
7103         }
7104 
7105         @Override
7106         public boolean equals( Object obj ) {
7107             if (obj instanceof Node) {
7108                 Node that = (Node)obj;
7109                 return this.location.isSame(that.getLocation());
7110             }
7111             return false;
7112         }
7113 
7114         @Override
7115         public String toString() {
7116             return "Node " + getLocation().toString();
7117         }
7118 
7119     }
7120 
7121     // ----------------------------------------------------------------------------------------------------------------
7122     // Subgraph and SubgraphNode implementations
7123     // ----------------------------------------------------------------------------------------------------------------
7124     @Immutable
7125     class SubgraphResults implements Subgraph {
7126         private final ReadBranchRequest request;
7127 
7128         SubgraphResults( ReadBranchRequest request ) {
7129             this.request = request;
7130         }
7131 
7132         public Graph getGraph() {
7133             return Graph.this;
7134         }
7135 
7136         public Location getLocation() {
7137             return request.getActualLocationOfNode();
7138         }
7139 
7140         public SubgraphNode getRoot() {
7141             return getNode(getLocation());
7142         }
7143 
7144         public int getMaximumDepth() {
7145             return request.maximumDepth();
7146         }
7147 
7148         public Iterator<SubgraphNode> iterator() {
7149             final Iterator<Location> iter = request.iterator();
7150             return new Iterator<SubgraphNode>() {
7151                 public boolean hasNext() {
7152                     return iter.hasNext();
7153                 }
7154 
7155                 public SubgraphNode next() {
7156                     return getNode(iter.next());
7157                 }
7158 
7159                 public void remove() {
7160                     throw new UnsupportedOperationException();
7161                 }
7162             };
7163         }
7164 
7165         public boolean includes( Path path ) {
7166             CheckArg.isNotNull(path, "path");
7167             path = getAbsolutePath(path);
7168             return request.includes(path);
7169         }
7170 
7171         public boolean includes( Location location ) {
7172             CheckArg.isNotNull(location, "location");
7173             return request.includes(location);
7174         }
7175 
7176         public boolean includes( String pathStr ) {
7177             Path path = createPath(pathStr);
7178             path = getAbsolutePath(path);
7179             return includes(path);
7180         }
7181 
7182         public SubgraphNode getNode( Location location ) {
7183             if (!location.hasPath()) return null;
7184             Location actualLocation = request.getLocationFor(location.getPath());
7185             if (actualLocation == null) return null;
7186             return new SubgraphNodeImpl(actualLocation, request);
7187         }
7188 
7189         public SubgraphNode getNode( Path path ) {
7190             path = getAbsolutePath(path);
7191             if (!includes(path)) return null;
7192             Location location = request.getLocationFor(path);
7193             if (location == null) return null;
7194             return new SubgraphNodeImpl(location, request);
7195         }
7196 
7197         public SubgraphNode getNode( String pathStr ) {
7198             CheckArg.isNotEmpty(pathStr, "path");
7199             Path path = createPath(pathStr);
7200             path = getAbsolutePath(path);
7201             return getNode(path);
7202         }
7203 
7204         public SubgraphNode getNode( Name relativePath ) {
7205             Path path = getGraph().getContext()
7206                                   .getValueFactories()
7207                                   .getPathFactory()
7208                                   .create(getLocation().getPath(), relativePath);
7209             path = path.getNormalizedPath();
7210             return getNode(path);
7211         }
7212 
7213         protected Path getAbsolutePath( Path absoluteOrRelative ) {
7214             Path result = absoluteOrRelative;
7215             if (!result.isAbsolute()) {
7216                 result = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), result);
7217                 result = result.getNormalizedPath();
7218             }
7219             return result;
7220         }
7221 
7222         @Override
7223         public int hashCode() {
7224             return getLocation().hashCode();
7225         }
7226 
7227         /**
7228          * {@inheritDoc}
7229          * 
7230          * @see java.lang.Object#equals(java.lang.Object)
7231          */
7232         @Override
7233         public boolean equals( Object obj ) {
7234             if (obj instanceof SubgraphResults) {
7235                 SubgraphResults that = (SubgraphResults)obj;
7236                 return getLocation().equals(that.getLocation()) && request.equals(that.request);
7237             } else if (obj instanceof Subgraph) {
7238                 Subgraph that = (Subgraph)obj;
7239                 if (!getLocation().equals(that.getLocation())) return false;
7240                 Iterator<SubgraphNode> thisIter = this.iterator();
7241                 Iterator<SubgraphNode> thatIter = that.iterator();
7242                 while (thisIter.hasNext() && thatIter.hasNext()) {
7243                     SubgraphNode thisNode = thisIter.next();
7244                     SubgraphNode thatNode = thatIter.next();
7245                     if (!thisNode.getLocation().equals(thatNode.getLocation())) return false;
7246                     if (!thisNode.getProperties().equals(thatNode.getProperties())) return false;
7247                     if (!thisNode.getChildren().equals(thatNode.getChildren())) return false;
7248                 }
7249                 if (thisIter.hasNext() || thatIter.hasNext()) return false;
7250                 return true;
7251             }
7252             return false;
7253         }
7254 
7255         @Override
7256         public String toString() {
7257             return "Subgraph\n" + getToString(getContext());
7258         }
7259 
7260         /**
7261          * Get the string representation of this subgraph tree.
7262          * 
7263          * @param context the execution context in which the conversion is to take place
7264          * @return the string representation; never null
7265          */
7266         public String getToString( ExecutionContext context ) {
7267             StringBuilder sb = new StringBuilder();
7268             getRecursiveString(context, getRoot(), sb, 0);
7269             return sb.toString();
7270         }
7271 
7272         private void getRecursiveString( ExecutionContext context,
7273                                          SubgraphNode node,
7274                                          StringBuilder str,
7275                                          int indentLevel ) {
7276             for (int i = 0; i < indentLevel; ++i) {
7277                 str.append("  ");
7278             }
7279             str.append(node.toString());
7280 
7281             // Recursively add children at one greater tab level
7282             for (Location nextLoc : node.getChildren()) {
7283                 SubgraphNode childNode = getNode(nextLoc);
7284                 // child node location may exist, but the subgraph may not have
7285                 // been constructed deep enough to instantiate the subnode, so
7286                 // check for null
7287                 if (childNode != null) {
7288                     getRecursiveString(context, childNode, str, indentLevel + 1);
7289                 }
7290             }
7291         }
7292 
7293     }
7294 
7295     protected static final List<Location> NO_CHILDREN = Collections.emptyList();
7296 
7297     @Immutable
7298     class SubgraphNodeImpl implements SubgraphNode {
7299         private final Location location;
7300         private final ReadBranchRequest request;
7301 
7302         SubgraphNodeImpl( Location location,
7303                           ReadBranchRequest request ) {
7304             this.location = location;
7305             this.request = request;
7306         }
7307 
7308         public DateTime getExpirationTime() {
7309             return computeExpirationTime(request);
7310         }
7311 
7312         public List<Location> getChildren() {
7313             List<Location> children = request.getChildren(location);
7314             if (children == null) children = NO_CHILDREN;
7315             return children;
7316         }
7317 
7318         public Graph getGraph() {
7319             return Graph.this;
7320         }
7321 
7322         public Location getLocation() {
7323             return location;
7324         }
7325 
7326         public Collection<Property> getProperties() {
7327             return getPropertiesByName().values();
7328         }
7329 
7330         public Map<Name, Property> getPropertiesByName() {
7331             return request.getPropertiesFor(location);
7332         }
7333 
7334         public Property getProperty( Name name ) {
7335             return getPropertiesByName().get(name);
7336         }
7337 
7338         public Property getProperty( String nameStr ) {
7339             Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
7340             return getPropertiesByName().get(name);
7341         }
7342 
7343         public boolean hasChildren() {
7344             return getChildren().size() != 0;
7345         }
7346 
7347         public List<Segment> getChildrenSegments() {
7348             return getSegments(getChildren());
7349         }
7350 
7351         public Iterator<Location> iterator() {
7352             return getChildren().iterator();
7353         }
7354 
7355         public SubgraphNode getNode( String childName ) {
7356             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childName);
7357             Location location = request.getLocationFor(path);
7358             if (location == null) return null;
7359             return new SubgraphNodeImpl(location, request);
7360         }
7361 
7362         public SubgraphNode getNode( Name childName ) {
7363             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childName);
7364             Location location = request.getLocationFor(path);
7365             if (location == null) return null;
7366             return new SubgraphNodeImpl(location, request);
7367         }
7368 
7369         public SubgraphNode getNode( Segment childSegment ) {
7370             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), childSegment);
7371             path = path.getNormalizedPath();
7372             Location location = request.getLocationFor(path);
7373             if (location == null) return null;
7374             return new SubgraphNodeImpl(location, request);
7375         }
7376 
7377         public SubgraphNode getNode( Path relativePath ) {
7378             Path path = getContext().getValueFactories().getPathFactory().create(location.getPath(), relativePath);
7379             path = path.getNormalizedPath();
7380             Location location = request.getLocationFor(path);
7381             if (location == null) return null;
7382             return new SubgraphNodeImpl(location, request);
7383         }
7384 
7385         @Override
7386         public int hashCode() {
7387             return location.hashCode();
7388         }
7389 
7390         @Override
7391         public boolean equals( Object obj ) {
7392             if (obj instanceof Node) {
7393                 Node that = (Node)obj;
7394                 return this.location.isSame(that.getLocation());
7395             }
7396             return false;
7397         }
7398 
7399         @Override
7400         public String toString() {
7401             return getNodeString(getContext(), location);
7402         }
7403 
7404         private String getNodeString( ExecutionContext context,
7405                                       Location location ) {
7406             StringBuilder sb = new StringBuilder();
7407             sb.append('<'); // Bracket the node
7408             ValueFactory<String> strings = context.getValueFactories().getStringFactory();
7409 
7410             String name = "";
7411             if (location.getPath().getLastSegment() != null) {
7412                 name = strings.create(location.getPath().getLastSegment());
7413             } else {
7414                 name = strings.create(location.getPath());
7415             }
7416 
7417             if (name.startsWith("{")) {
7418                 // Remove {xxxx} namespace prefix
7419                 int end = name.indexOf('}');
7420                 name = name.substring(end + 1, name.length());
7421             }
7422 
7423             // Surround name in double quotes
7424             sb.append("name = ").append('\"').append(name).append('\"').append(" ");
7425             boolean first = true;
7426             if (getProperties() != null) {
7427                 for (Property entry : getProperties()) {
7428 
7429                     if (first) {
7430                         first = false;
7431                     } else sb.append(" ");
7432                     sb.append(getPropertyString(entry));
7433                 }
7434             }
7435             sb.append(">\n");
7436 
7437             return sb.toString();
7438         }
7439 
7440         private String getPropertyString( Property property ) {
7441             // Surround property value in double quotes so final property looks like:
7442             // color = "blue" (single valued property)
7443             // colors = ["blue", "red", "green"] (multi-valued property)
7444 
7445             StringBuilder sb = new StringBuilder();
7446             sb.append(getContext().getValueFactories().getStringFactory().create(property.getName()));
7447             sb.append(" = ");
7448             if (property.isEmpty()) {
7449                 sb.append("null");
7450             } else if (property.isSingle()) {
7451                 String valueStr = getContext().getValueFactories().getStringFactory().create(property.getValues().next());
7452                 sb.append('\"').append(valueStr).append('\"');
7453             } else {
7454                 sb.append('[');
7455                 boolean first = true;
7456                 for (Object value : property.getValuesAsArray()) {
7457                     if (first) first = false;
7458                     else sb.append(",");
7459                     String valueStr = getContext().getValueFactories().getStringFactory().create(value);
7460                     sb.append('\"').append(valueStr).append('\"');
7461                 }
7462                 if (property.isMultiple()) sb.append(']');
7463             }
7464             return sb.toString();
7465         }
7466     }
7467 
7468     // ----------------------------------------------------------------------------------------------------------------
7469     // Action Implementations
7470     // ----------------------------------------------------------------------------------------------------------------
7471     @Immutable
7472     protected abstract class AbstractAction<T> implements Conjunction<T> {
7473         private final T afterConjunction;
7474 
7475         /*package*/AbstractAction( T afterConjunction ) {
7476             this.afterConjunction = afterConjunction;
7477         }
7478 
7479         /*package*/T afterConjunction() {
7480             return this.afterConjunction;
7481         }
7482 
7483         public T and() {
7484             return this.afterConjunction;
7485         }
7486 
7487         /*package*/Path createPath( String path ) {
7488             return Graph.this.getContext().getValueFactories().getPathFactory().create(path);
7489         }
7490 
7491         /*package*/Name createName( String name ) {
7492             return Graph.this.getContext().getValueFactories().getNameFactory().create(name);
7493         }
7494     }
7495 
7496     @NotThreadSafe
7497     protected abstract class MoveAction<T> extends AbstractAction<T> implements Move<T> {
7498         private final Locations from;
7499         private Name newName;
7500 
7501         /*package*/MoveAction( T afterConjunction,
7502                                 Location from ) {
7503             super(afterConjunction);
7504             this.from = new Locations(from);
7505         }
7506 
7507         public Move<T> and( Location from ) {
7508             this.from.add(from);
7509             return this;
7510         }
7511 
7512         public Move<T> and( String from ) {
7513             this.from.add(Location.create(createPath(from)));
7514             return this;
7515         }
7516 
7517         public Move<T> and( Path from ) {
7518             this.from.add(Location.create(from));
7519             return this;
7520         }
7521 
7522         public Move<T> and( Property firstFrom,
7523                             Property... additionalFroms ) {
7524             this.from.add(Location.create(firstFrom, additionalFroms));
7525             return this;
7526         }
7527 
7528         public Move<T> and( Iterable<Property> idPropertiesFrom ) {
7529             this.from.add(Location.create(idPropertiesFrom));
7530             return this;
7531         }
7532 
7533         public Move<T> and( Property from ) {
7534             this.from.add(Location.create(from));
7535             return this;
7536         }
7537 
7538         public Move<T> and( UUID from ) {
7539             this.from.add(Location.create(from));
7540             return this;
7541         }
7542 
7543         public Into<T> as( Name newName ) {
7544             this.newName = newName;
7545             return this;
7546         }
7547 
7548         /**
7549          * {@inheritDoc}
7550          * 
7551          * @see org.modeshape.graph.Graph.AsName#as(java.lang.String)
7552          */
7553         public Into<T> as( String newName ) {
7554             return as(createName(newName));
7555         }
7556 
7557         /**
7558          * Submit any requests to move the targets into the supplied parent location
7559          * 
7560          * @param from the location(s) that are being moved; never null
7561          * @param into the parent location
7562          * @param before the location of the child of the parent before which this node should be placed
7563          * @param newName the new name for the node being moved; may be null
7564          * @return this object, for method chaining
7565          */
7566         protected abstract T submit( Locations from,
7567                                      Location into,
7568                                      Location before,
7569                                      Name newName );
7570 
7571         /**
7572          * Submit any requests to move the targets into the supplied parent location
7573          * 
7574          * @param from the location(s) that are being moved; never null
7575          * @param into the parent location
7576          * @param newName the new name for the node being moved; may be null
7577          * @return this object, for method chaining
7578          */
7579         protected T submit( Locations from,
7580                             Location into,
7581                             Name newName ) {
7582             return submit(from, into, null, newName);
7583         }
7584 
7585         public T into( Location into ) {
7586             return submit(from, into, null, newName);
7587         }
7588 
7589         public T into( Path into ) {
7590             return submit(from, Location.create(into), newName);
7591         }
7592 
7593         public T into( UUID into ) {
7594             return submit(from, Location.create(into), newName);
7595         }
7596 
7597         public T into( Property firstIdProperty,
7598                        Property... additionalIdProperties ) {
7599             return submit(from, Location.create(firstIdProperty, additionalIdProperties), newName);
7600         }
7601 
7602         public T into( Property into ) {
7603             return submit(from, Location.create(into), newName);
7604         }
7605 
7606         public T into( String into ) {
7607             return submit(from, Location.create(createPath(into)), newName);
7608         }
7609 
7610         public T before( Location before ) {
7611             return submit(from, null, before, newName);
7612         }
7613 
7614         public T before( Path before ) {
7615             return submit(from, null, Location.create(before), newName);
7616         }
7617 
7618         public T before( UUID before ) {
7619             return submit(from, null, Location.create(before), newName);
7620         }
7621 
7622         public T before( Property firstIdProperty,
7623                          Property... additionalIdProperties ) {
7624             return submit(from, null, Location.create(firstIdProperty, additionalIdProperties), newName);
7625         }
7626 
7627         public T before( Property before ) {
7628             return submit(from, null, Location.create(before), newName);
7629         }
7630 
7631         public T before( String before ) {
7632             return submit(from, null, Location.create(createPath(before)), newName);
7633         }
7634     }
7635 
7636     @NotThreadSafe
7637     protected abstract class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
7638         protected Locations from;
7639         protected String fromWorkspaceName;
7640 
7641         /*package*/CopyAction( T afterConjunction,
7642                                 Location from ) {
7643             super(afterConjunction);
7644             this.from = new Locations(from);
7645             this.fromWorkspaceName = Graph.this.getCurrentWorkspaceName();
7646         }
7647 
7648         public Copy<T> and( Location from ) {
7649             this.from.add(from);
7650             return this;
7651         }
7652 
7653         public Copy<T> and( String from ) {
7654             this.from.add(Location.create(createPath(from)));
7655             return this;
7656         }
7657 
7658         public Copy<T> and( Path from ) {
7659             this.from.add(Location.create(from));
7660             return this;
7661         }
7662 
7663         public Copy<T> and( Property firstFrom,
7664                             Property... additionalFroms ) {
7665             this.from.add(Location.create(firstFrom, additionalFroms));
7666             return this;
7667         }
7668 
7669         public Copy<T> and( Iterable<Property> idProperties ) {
7670             this.from.add(Location.create(idProperties));
7671             return this;
7672         }
7673 
7674         public Copy<T> and( Property from ) {
7675             this.from.add(Location.create(from));
7676             return this;
7677         }
7678 
7679         public Copy<T> and( UUID from ) {
7680             this.from.add(Location.create(from));
7681             return this;
7682         }
7683 
7684         /**
7685          * Submit any requests to move the targets into the supplied parent location
7686          * 
7687          * @param fromWorkspaceName the name of the workspace containing the {@code from} locations
7688          * @param from the locations that are being copied
7689          * @param into the parent location
7690          * @param nameForCopy the name that should be used for the copy, or null if the name should be the same as the original
7691          * @return this object, for method chaining
7692          */
7693         protected abstract T submit( String fromWorkspaceName,
7694                                      Locations from,
7695                                      Location into,
7696                                      Name nameForCopy );
7697 
7698         public CopyTarget<T> fromWorkspace( String workspaceName ) {
7699             this.fromWorkspaceName = workspaceName;
7700 
7701             return this;
7702         }
7703 
7704         public T into( Location into ) {
7705             return submit(this.fromWorkspaceName, this.from, into, null);
7706         }
7707 
7708         public T into( Path into ) {
7709             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7710         }
7711 
7712         public T into( UUID into ) {
7713             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7714         }
7715 
7716         public T into( Property firstIdProperty,
7717                        Property... additionalIdProperties ) {
7718             return submit(this.fromWorkspaceName, this.from, Location.create(firstIdProperty, additionalIdProperties), null);
7719         }
7720 
7721         public T into( Property into ) {
7722             return submit(this.fromWorkspaceName, this.from, Location.create(into), null);
7723         }
7724 
7725         public T into( String into ) {
7726             return submit(this.fromWorkspaceName, this.from, Location.create(createPath(into)), null);
7727         }
7728 
7729         public T to( Location desiredLocation ) {
7730             if (!desiredLocation.hasPath()) {
7731                 throw new IllegalArgumentException(GraphI18n.unableToCopyToLocationWithoutAPath.text(this.from, desiredLocation));
7732             }
7733             Path desiredPath = desiredLocation.getPath();
7734             if (desiredPath.isRoot()) {
7735                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredLocation));
7736             }
7737             Path parent = desiredPath.getParent();
7738             return submit(this.fromWorkspaceName, this.from, Location.create(parent), desiredPath.getLastSegment().getName());
7739         }
7740 
7741         public T to( Path desiredPath ) {
7742             if (desiredPath.isRoot()) {
7743                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredPath));
7744             }
7745             Path parent = desiredPath.getParent();
7746             return submit(this.fromWorkspaceName, this.from, Location.create(parent), desiredPath.getLastSegment().getName());
7747         }
7748 
7749         public T to( String desiredPath ) {
7750             return to(createPath(desiredPath));
7751         }
7752     }
7753 
7754     @NotThreadSafe
7755     public abstract class CloneAction<T> extends AbstractAction<T> implements Clone<T> {
7756         protected final Location from;
7757 
7758         /*package*/CloneAction( T afterConjunction,
7759                                  Location from ) {
7760             super(afterConjunction);
7761             this.from = from;
7762         }
7763 
7764         protected abstract T submit( String fromWorkspaceName,
7765                                      Location from,
7766                                      String intoWorkspaceName,
7767                                      Location into,
7768                                      Name desiredName,
7769                                      Segment desiredSegment,
7770                                      boolean removeExisting );
7771 
7772         public AsChild<Into<WithUuids<T>>> fromWorkspace( final String workspaceName ) {
7773             final CloneAction<T> source = this;
7774             return new AsChild<Into<WithUuids<T>>>() {
7775                 public Into<WithUuids<T>> as( final Name name ) {
7776                     return new CloneTargetAction<T>(afterConjunction(), source) {
7777                         @Override
7778                         protected T submit( Location into,
7779                                             boolean removeExisting ) {
7780                             String intoWorkspaceName = getCurrentWorkspaceName();
7781                             return source.submit(workspaceName, from, intoWorkspaceName, into, name, null, removeExisting);
7782                         }
7783                     };
7784 
7785                 }
7786 
7787                 public Into<WithUuids<T>> as( final String name ) {
7788                     return as(getContext().getValueFactories().getNameFactory().create(name));
7789                 }
7790 
7791                 public Into<WithUuids<T>> as( final Segment segment ) {
7792                     return new CloneTargetAction<T>(afterConjunction(), source) {
7793                         @Override
7794                         protected T submit( Location into,
7795                                             boolean removeExisting ) {
7796                             String intoWorkspaceName = getCurrentWorkspaceName();
7797                             return source.submit(workspaceName, from, intoWorkspaceName, into, null, segment, removeExisting);
7798                         }
7799                     };
7800                 }
7801 
7802             };
7803         }
7804     }
7805 
7806     @NotThreadSafe
7807     public abstract class CloneTargetAction<T> extends AbstractAction<T> implements Into<WithUuids<T>> {
7808         protected final CloneAction<T> source;
7809 
7810         /*package*/CloneTargetAction( T afterConjunction,
7811                                        CloneAction<T> source ) {
7812             super(afterConjunction);
7813             this.source = source;
7814         }
7815 
7816         protected abstract T submit( Location into,
7817                                      boolean removeExisting );
7818 
7819         public WithUuids<T> into( final Location into ) {
7820             return new WithUuids<T>() {
7821                 public T failingIfAnyUuidsMatch() {
7822                     submit(into, false);
7823                     return and();
7824                 }
7825 
7826                 public T replacingExistingNodesWithSameUuids() {
7827                     submit(into, true);
7828                     return and();
7829 
7830                 }
7831             };
7832         }
7833 
7834         public WithUuids<T> into( Path into ) {
7835             return into(Location.create(into));
7836         }
7837 
7838         public WithUuids<T> into( UUID into ) {
7839             return into(Location.create(into));
7840         }
7841 
7842         public WithUuids<T> into( Property firstIdProperty,
7843                                   Property... additionalIdProperties ) {
7844             return into(Location.create(firstIdProperty, additionalIdProperties));
7845         }
7846 
7847         public WithUuids<T> into( Property into ) {
7848             return into(Location.create(into));
7849         }
7850 
7851         public WithUuids<T> into( String into ) {
7852             return into(Location.create(createPath(into)));
7853         }
7854     }
7855 
7856     @NotThreadSafe
7857     protected abstract class CreateAction<T> extends AbstractAction<T> implements Create<T> {
7858         private final String workspaceName;
7859         private final Location parent;
7860         private final Name childName;
7861         private final Map<Name, Property> properties = new HashMap<Name, Property>();
7862         private boolean submitted = false;
7863         private NodeConflictBehavior conflictBehavior = NodeConflictBehavior.APPEND;
7864 
7865         /*package*/CreateAction( T afterConjunction,
7866                                   Location parent,
7867                                   String workspaceName,
7868                                   Name childName ) {
7869             super(afterConjunction);
7870             this.parent = parent;
7871             this.workspaceName = workspaceName;
7872             this.childName = childName;
7873         }
7874 
7875         /**
7876          * {@inheritDoc}
7877          * 
7878          * @see org.modeshape.graph.Graph.Create#ifAbsent()
7879          */
7880         public CreateAction<T> ifAbsent() {
7881             conflictBehavior = NodeConflictBehavior.DO_NOT_REPLACE;
7882             return this;
7883         }
7884 
7885         /**
7886          * {@inheritDoc}
7887          * 
7888          * @see org.modeshape.graph.Graph.Create#orReplace()
7889          */
7890         public CreateAction<T> orReplace() {
7891             conflictBehavior = NodeConflictBehavior.REPLACE;
7892             return this;
7893         }
7894 
7895         /**
7896          * {@inheritDoc}
7897          * 
7898          * @see org.modeshape.graph.Graph.Create#orUpdate()
7899          */
7900         public CreateAction<T> orUpdate() {
7901             conflictBehavior = NodeConflictBehavior.UPDATE;
7902             return this;
7903         }
7904 
7905         /**
7906          * {@inheritDoc}
7907          * 
7908          * @see org.modeshape.graph.Graph.Create#byAppending()
7909          */
7910         public CreateAction<T> byAppending() {
7911             conflictBehavior = NodeConflictBehavior.APPEND;
7912             return this;
7913         }
7914 
7915         public Create<T> and( UUID uuid ) {
7916             PropertyFactory factory = getContext().getPropertyFactory();
7917             properties.put(ModeShapeLexicon.UUID, factory.create(ModeShapeLexicon.UUID, uuid));
7918             return this;
7919         }
7920 
7921         public Create<T> and( Property property ) {
7922             properties.put(property.getName(), property);
7923             return this;
7924         }
7925 
7926         public Create<T> and( Iterable<Property> properties ) {
7927             for (Property property : properties) {
7928                 this.properties.put(property.getName(), property);
7929             }
7930             return this;
7931         }
7932 
7933         public Create<T> and( String name,
7934                               Object... values ) {
7935             ExecutionContext context = getContext();
7936             PropertyFactory factory = context.getPropertyFactory();
7937             NameFactory nameFactory = context.getValueFactories().getNameFactory();
7938             Name propertyName = nameFactory.create(name);
7939             properties.put(propertyName, factory.create(propertyName, values));
7940             return this;
7941         }
7942 
7943         public Create<T> and( Name name,
7944                               Object... values ) {
7945             PropertyFactory factory = getContext().getPropertyFactory();
7946             properties.put(name, factory.create(name, values));
7947             return this;
7948         }
7949 
7950         public Create<T> and( Property property,
7951                               Property... additionalProperties ) {
7952             properties.put(property.getName(), property);
7953             for (Property additionalProperty : additionalProperties) {
7954                 properties.put(additionalProperty.getName(), additionalProperty);
7955             }
7956             return this;
7957         }
7958 
7959         public Create<T> with( UUID uuid ) {
7960             return and(uuid);
7961         }
7962 
7963         public Create<T> with( Property property ) {
7964             return and(property);
7965         }
7966 
7967         public Create<T> with( Iterable<Property> properties ) {
7968             return and(properties);
7969         }
7970 
7971         public Create<T> with( Property property,
7972                                Property... additionalProperties ) {
7973             return and(property, additionalProperties);
7974         }
7975 
7976         public Create<T> with( String name,
7977                                Object... values ) {
7978             return and(name, values);
7979         }
7980 
7981         public Create<T> with( Name name,
7982                                Object... values ) {
7983             return and(name, values);
7984         }
7985 
7986         protected abstract T submit( Location parent,
7987                                      String workspaceName,
7988                                      Name childName,
7989                                      Collection<Property> properties,
7990                                      NodeConflictBehavior conflictBehavior );
7991 
7992         @Override
7993         public T and() {
7994             if (!submitted) {
7995                 submit(parent, workspaceName, childName, this.properties.values(), this.conflictBehavior);
7996                 submitted = true;
7997             }
7998             return super.and();
7999         }
8000     }
8001 
8002     @NotThreadSafe
8003     protected abstract class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
8004         private final Location parent;
8005 
8006         protected CreateNodeNamedAction( T afterConjunction,
8007                                          Location parent ) {
8008             super(afterConjunction);
8009             this.parent = parent;
8010         }
8011 
8012         public CreateAction<T> nodeNamed( String name ) {
8013             NameFactory factory = getContext().getValueFactories().getNameFactory();
8014             Name nameObj = factory.create(name);
8015             return createWith(afterConjunction(), parent, nameObj);
8016         }
8017 
8018         public CreateAction<T> nodeNamed( Name name ) {
8019             return createWith(afterConjunction(), parent, name);
8020         }
8021 
8022         protected abstract CreateAction<T> createWith( T afterConjunction,
8023                                                        Location parent,
8024                                                        Name nodeName );
8025     }
8026 
8027     @Immutable
8028     protected static final class GraphWorkspace implements Workspace {
8029         private final String name;
8030         private final Location root;
8031 
8032         GraphWorkspace( String name,
8033                         Location root ) {
8034             assert name != null;
8035             assert root != null;
8036             this.name = name;
8037             this.root = root;
8038         }
8039 
8040         /**
8041          * {@inheritDoc}
8042          * 
8043          * @see org.modeshape.graph.Workspace#getName()
8044          */
8045         public String getName() {
8046             return name;
8047         }
8048 
8049         /**
8050          * {@inheritDoc}
8051          * 
8052          * @see org.modeshape.graph.Workspace#getRoot()
8053          */
8054         public Location getRoot() {
8055             return root;
8056         }
8057 
8058         /**
8059          * {@inheritDoc}
8060          * 
8061          * @see java.lang.Object#hashCode()
8062          */
8063         @Override
8064         public int hashCode() {
8065             return this.name.hashCode();
8066         }
8067 
8068         /**
8069          * {@inheritDoc}
8070          * 
8071          * @see java.lang.Object#equals(java.lang.Object)
8072          */
8073         @Override
8074         public boolean equals( Object obj ) {
8075             if (obj == this) return true;
8076             if (obj instanceof GraphWorkspace) {
8077                 GraphWorkspace that = (GraphWorkspace)obj;
8078                 if (!this.getName().equals(that.getName())) return false;
8079                 // all root nodes should be equivalent, so no need to check
8080                 return true;
8081             }
8082             return false;
8083         }
8084 
8085         /**
8086          * {@inheritDoc}
8087          * 
8088          * @see java.lang.Object#toString()
8089          */
8090         @Override
8091         public String toString() {
8092             return "Workspace \"" + this.name + "\" (root = " + this.root + " )";
8093         }
8094     }
8095 
8096     /**
8097      * A set of nodes returned from a {@link Graph graph}, with methods to access the properties and children of the nodes in the
8098      * result. The {@link #iterator()} method can be used to iterate all over the nodes in the result.
8099      * 
8100      * @param <NodeType> the type of node that tis results deals with
8101      */
8102     @Immutable
8103     public interface BaseResults<NodeType extends Node> extends Iterable<NodeType> {
8104 
8105         /**
8106          * Get the graph containing the node.
8107          * 
8108          * @return the graph
8109          */
8110         Graph getGraph();
8111 
8112         /**
8113          * Get the node at the supplied location.
8114          * 
8115          * @param path the path of the node in these results
8116          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8117          */
8118         NodeType getNode( String path );
8119 
8120         /**
8121          * Get the node at the supplied location.
8122          * 
8123          * @param path the path of the node in these results
8124          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8125          */
8126         NodeType getNode( Path path );
8127 
8128         /**
8129          * Get the node at the supplied location.
8130          * 
8131          * @param location the location of the node
8132          * @return the node, or null if the node is not {@link #includes(Path) included} in these results
8133          */
8134         NodeType getNode( Location location );
8135 
8136         /**
8137          * Return whether these results include a node at the supplied location.
8138          * 
8139          * @param path the path of the node in these results
8140          * @return true if this subgraph includes the supplied location, or false otherwise
8141          */
8142         boolean includes( String path );
8143 
8144         /**
8145          * Return whether this subgraph has a node at the supplied location.
8146          * 
8147          * @param path the path of the node in these results
8148          * @return true if these results includes the supplied location, or false otherwise
8149          */
8150         boolean includes( Path path );
8151 
8152         /**
8153          * Return whether this subgraph has a node at the supplied location.
8154          * 
8155          * @param location the location of the node in these results
8156          * @return true if these results includes the supplied location, or false otherwise
8157          */
8158         boolean includes( Location location );
8159 
8160     }
8161 }