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