001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.graph;
023    
024    import java.io.File;
025    import java.io.IOException;
026    import java.net.URI;
027    import java.util.ArrayList;
028    import java.util.Collection;
029    import java.util.Collections;
030    import java.util.HashMap;
031    import java.util.Iterator;
032    import java.util.LinkedList;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.UUID;
036    import net.jcip.annotations.Immutable;
037    import net.jcip.annotations.NotThreadSafe;
038    import org.jboss.dna.common.util.CheckArg;
039    import org.jboss.dna.graph.cache.CachePolicy;
040    import org.jboss.dna.graph.connectors.RepositoryConnection;
041    import org.jboss.dna.graph.connectors.RepositoryConnectionFactory;
042    import org.jboss.dna.graph.connectors.RepositorySource;
043    import org.jboss.dna.graph.connectors.RepositorySourceException;
044    import org.jboss.dna.graph.properties.Name;
045    import org.jboss.dna.graph.properties.NameFactory;
046    import org.jboss.dna.graph.properties.Path;
047    import org.jboss.dna.graph.properties.Property;
048    import org.jboss.dna.graph.properties.PropertyFactory;
049    import org.jboss.dna.graph.properties.Path.Segment;
050    import org.jboss.dna.graph.requests.CompositeRequest;
051    import org.jboss.dna.graph.requests.CopyBranchRequest;
052    import org.jboss.dna.graph.requests.CreateNodeRequest;
053    import org.jboss.dna.graph.requests.DeleteBranchRequest;
054    import org.jboss.dna.graph.requests.MoveBranchRequest;
055    import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
056    import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
057    import org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest;
058    import org.jboss.dna.graph.requests.ReadBranchRequest;
059    import org.jboss.dna.graph.requests.ReadNodeRequest;
060    import org.jboss.dna.graph.requests.ReadPropertyRequest;
061    import org.jboss.dna.graph.requests.RemovePropertiesRequest;
062    import org.jboss.dna.graph.requests.Request;
063    import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
064    import org.xml.sax.SAXException;
065    
066    /**
067     * A graph representation of the content within a {@link RepositorySource}, including mechanisms to interact and manipulate that
068     * content. The graph is designed to be an <i><a href="http://en.wikipedia.org/wiki/Domain_Specific_Language">embedded domain
069     * specific language</a></i>, meaning calls to it are designed to read like sentences even though they are really just Java
070     * methods. And to be more readable, methods can be chained together.
071     * 
072     * @author Randall Hauch
073     */
074    @NotThreadSafe
075    public class Graph {
076    
077        /**
078         * Create a graph instance that uses the supplied repository and {@link ExecutionContext context}.
079         * 
080         * @param sourceName the name of the source that should be used
081         * @param connectionFactory the factory of repository connections
082         * @param context the context in which all executions should be performed
083         * @return the new graph
084         * @throws IllegalArgumentException if the source or context parameters are null
085         */
086        public static Graph create( String sourceName,
087                                    RepositoryConnectionFactory connectionFactory,
088                                    ExecutionContext context ) {
089            return new Graph(sourceName, connectionFactory, context);
090        }
091    
092        private final String sourceName;
093        private final RepositoryConnectionFactory connectionFactory;
094        private final ExecutionContext context;
095        private final RequestQueue requestQueue;
096        private final Conjunction<Graph> nextGraph;
097    
098        protected Graph( String sourceName,
099                         RepositoryConnectionFactory connectionFactory,
100                         ExecutionContext context ) {
101            CheckArg.isNotNull(sourceName, "sourceName");
102            CheckArg.isNotNull(connectionFactory, "connectionFactory");
103            CheckArg.isNotNull(context, "context");
104            this.sourceName = sourceName;
105            this.connectionFactory = connectionFactory;
106            this.context = context;
107            this.requestQueue = new GraphRequestQueue();
108            this.nextGraph = new Conjunction<Graph>() {
109                public Graph and() {
110                    return Graph.this;
111                }
112            };
113        }
114    
115        /**
116         * Get the RepositoryConnectionFactory that this graph uses to create {@link RepositoryConnection repository connections}.
117         * 
118         * @return the factory repository connections used by this graph; never null
119         */
120        public RepositoryConnectionFactory getConnectionFactory() {
121            return connectionFactory;
122        }
123    
124        /**
125         * The name of the repository that will be used by this graph. This name is passed to the {@link #getConnectionFactory()
126         * connection factory} when this graph needs to {@link RepositoryConnectionFactory#createConnection(String) obtain} a
127         * {@link RepositoryConnection repository connection}.
128         * 
129         * @return the name of the source
130         */
131        public String getSourceName() {
132            return sourceName;
133        }
134    
135        /**
136         * Get the context of execution within which operations on this graph are performed.
137         * 
138         * @return the execution context; never null
139         */
140        public ExecutionContext getContext() {
141            return context;
142        }
143    
144        /*package*/RequestQueue queue() {
145            return this.requestQueue;
146        }
147    
148        /**
149         * Get the default cache policy for this graph. May be null if such a policy has not been defined for thie
150         * {@link #getSourceName() source}.
151         * 
152         * @return the default cache policy, or null if no such policy has been defined for the source
153         * @throws RepositorySourceException if no repository source with the {@link #getSourceName() name} could be found
154         */
155        public CachePolicy getDefaultCachePolicy() {
156            RepositoryConnection connection = this.connectionFactory.createConnection(getSourceName());
157            if (connection == null) {
158                throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
159            }
160            try {
161                return connection.getDefaultCachePolicy();
162            } finally {
163                connection.close();
164            }
165        }
166    
167        /**
168         * Begin the request to move the specified node into a parent node at a different location, which is specified via the
169         * <code>into(...)</code> method on the returned {@link Move} object.
170         * <p>
171         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
172         * method is called.
173         * </p>
174         * 
175         * @param from the node that is to be moved.
176         * @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
177         *         be moved
178         */
179        public Move<Conjunction<Graph>> move( Node from ) {
180            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, from.getLocation());
181        }
182    
183        /**
184         * Begin the request to move a node at the specified location into a parent node at a different location, which is specified
185         * via the <code>into(...)</code> method on the returned {@link Move} object.
186         * <p>
187         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
188         * method is called.
189         * </p>
190         * 
191         * @param from the location of the node that is to be moved.
192         * @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
193         *         be moved
194         */
195        public Move<Conjunction<Graph>> move( Location from ) {
196            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, from);
197        }
198    
199        /**
200         * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
201         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
202         * <p>
203         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
204         * method is called.
205         * </p>
206         * 
207         * @param fromPath the path to the node that is to be moved.
208         * @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
209         *         be moved
210         */
211        public Move<Conjunction<Graph>> move( String fromPath ) {
212            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, new Location(createPath(fromPath)));
213        }
214    
215        /**
216         * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
217         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
218         * <p>
219         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
220         * method is called.
221         * </p>
222         * 
223         * @param from the path to the node that is to be moved.
224         * @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
225         *         be moved
226         */
227        public Move<Conjunction<Graph>> move( Path from ) {
228            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, new Location(from));
229        }
230    
231        /**
232         * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which is
233         * specified via the <code>into(...)</code> method on the returned {@link Move} object.
234         * <p>
235         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
236         * method is called.
237         * </p>
238         * 
239         * @param from the UUID of the node that is to be moved.
240         * @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
241         *         be moved
242         */
243        public Move<Conjunction<Graph>> move( UUID from ) {
244            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, new Location(from));
245        }
246    
247        /**
248         * Begin the request to move a node with the specified unique identification property into a parent node at a different
249         * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification
250         * property should uniquely identify a single node.
251         * <p>
252         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
253         * method is called.
254         * </p>
255         * 
256         * @param idProperty the unique identification property of the node that is to be moved.
257         * @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
258         *         be moved
259         */
260        public Move<Conjunction<Graph>> move( Property idProperty ) {
261            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, new Location(idProperty));
262        }
263    
264        /**
265         * Begin the request to move a node with the specified identification properties into a parent node at a different location,
266         * which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The identification properties
267         * should uniquely identify a single node.
268         * <p>
269         * Like all other methods on the {@link Graph}, the move request will be performed immediately when the <code>into(...)</code>
270         * method is called.
271         * </p>
272         * 
273         * @param firstIdProperty the first identification property of the node that is to be moved
274         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be moved
275         * @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
276         *         be moved
277         */
278        public Move<Conjunction<Graph>> move( Property firstIdProperty,
279                                              Property... additionalIdProperties ) {
280            return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, new Location(firstIdProperty,
281                                                                                                      additionalIdProperties));
282        }
283    
284        /**
285         * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
286         * <code>into(...)</code> method on the returned {@link Copy} object.
287         * <p>
288         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
289         * method is called.
290         * </p>
291         * 
292         * @param from the node that is to be copied.
293         * @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
294         *         be copied
295         */
296        public Copy<Graph> copy( Node from ) {
297            return new CopyAction<Graph>(this, this.requestQueue, from.getLocation());
298        }
299    
300        /**
301         * Begin the request to copy a node at the specified location into a parent node at a different location, which is specified
302         * via the <code>into(...)</code> method on the returned {@link Copy} object.
303         * <p>
304         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
305         * method is called.
306         * </p>
307         * 
308         * @param from the location of the node that is to be copied.
309         * @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
310         *         be copied
311         */
312        public Copy<Graph> copy( Location from ) {
313            return new CopyAction<Graph>(this, this.requestQueue, from);
314        }
315    
316        /**
317         * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
318         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
319         * <p>
320         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
321         * method is called.
322         * </p>
323         * 
324         * @param fromPath the path to the node that is to be copied.
325         * @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
326         *         be copied
327         */
328        public Copy<Graph> copy( String fromPath ) {
329            return new CopyAction<Graph>(this, this.requestQueue, new Location(createPath(fromPath)));
330        }
331    
332        /**
333         * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
334         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
335         * <p>
336         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
337         * method is called.
338         * </p>
339         * 
340         * @param from the path to the node that is to be copied.
341         * @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
342         *         be copied
343         */
344        public Copy<Graph> copy( Path from ) {
345            return new CopyAction<Graph>(this, this.requestQueue, new Location(from));
346        }
347    
348        /**
349         * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which is
350         * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
351         * <p>
352         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
353         * method is called.
354         * </p>
355         * 
356         * @param from the UUID of the node that is to be copied.
357         * @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
358         *         be copied
359         */
360        public Copy<Graph> copy( UUID from ) {
361            return new CopyAction<Graph>(this, this.requestQueue, new Location(from));
362        }
363    
364        /**
365         * Begin the request to copy a node with the specified unique identification property into a parent node at a different
366         * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification
367         * property should uniquely identify a single node.
368         * <p>
369         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
370         * method is called.
371         * </p>
372         * 
373         * @param idProperty the unique identification property of the node that is to be copied.
374         * @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
375         *         be copied
376         */
377        public Copy<Graph> copy( Property idProperty ) {
378            return new CopyAction<Graph>(this, this.requestQueue, new Location(idProperty));
379        }
380    
381        /**
382         * Begin the request to copy a node with the specified identification properties into a parent node at a different location,
383         * which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The identification properties
384         * should uniquely identify a single node.
385         * <p>
386         * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>into(...)</code>
387         * method is called.
388         * </p>
389         * 
390         * @param firstIdProperty the first identification property of the node that is to be copied
391         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
392         * @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
393         *         be copied
394         */
395        public Copy<Graph> copy( Property firstIdProperty,
396                                 Property... additionalIdProperties ) {
397            return new CopyAction<Graph>(this, this.requestQueue, new Location(firstIdProperty, additionalIdProperties));
398        }
399    
400        /**
401         * Request to delete the specified node. This request is submitted to the repository immediately.
402         * 
403         * @param at the node that is to be deleted
404         * @return an object that may be used to start another request
405         */
406        public Conjunction<Graph> delete( Node at ) {
407            this.requestQueue.submit(new DeleteBranchRequest(at.getLocation()));
408            return nextGraph;
409        }
410    
411        /**
412         * Request to delete the node at the given location. This request is submitted to the repository immediately.
413         * 
414         * @param at the location of the node that is to be deleted
415         * @return an object that may be used to start another request
416         */
417        public Conjunction<Graph> delete( Location at ) {
418            this.requestQueue.submit(new DeleteBranchRequest(at));
419            return nextGraph;
420        }
421    
422        /**
423         * Request to delete the node at the given path. This request is submitted to the repository immediately.
424         * 
425         * @param atPath the path of the node that is to be deleted
426         * @return an object that may be used to start another request
427         */
428        public Conjunction<Graph> delete( String atPath ) {
429            this.requestQueue.submit(new DeleteBranchRequest(new Location(createPath(atPath))));
430            return nextGraph;
431        }
432    
433        /**
434         * Request to delete the node at the given path. This request is submitted to the repository immediately.
435         * 
436         * @param at the path of the node that is to be deleted
437         * @return an object that may be used to start another request
438         */
439        public Conjunction<Graph> delete( Path at ) {
440            this.requestQueue.submit(new DeleteBranchRequest(new Location(at)));
441            return nextGraph;
442        }
443    
444        /**
445         * Request to delete the node with the given UUID. This request is submitted to the repository immediately.
446         * 
447         * @param at the UUID of the node that is to be deleted
448         * @return an object that may be used to start another request
449         */
450        public Conjunction<Graph> delete( UUID at ) {
451            this.requestQueue.submit(new DeleteBranchRequest(new Location(at)));
452            return nextGraph;
453        }
454    
455        /**
456         * Request to delete the node with the given unique identification property. This request is submitted to the repository
457         * immediately.
458         * 
459         * @param idProperty the unique identifying property of the node that is to be deleted
460         * @return an object that may be used to start another request
461         */
462        public Conjunction<Graph> delete( Property idProperty ) {
463            this.requestQueue.submit(new DeleteBranchRequest(new Location(idProperty)));
464            return nextGraph;
465        }
466    
467        /**
468         * Request to delete the node with the given identification properties. The identification properties should uniquely identify
469         * a single node. This request is submitted to the repository immediately.
470         * 
471         * @param firstIdProperty the first identification property of the node that is to be copied
472         * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
473         * @return an object that may be used to start another request
474         */
475        public Conjunction<Graph> delete( Property firstIdProperty,
476                                          Property... additionalIdProperties ) {
477            this.requestQueue.submit(new DeleteBranchRequest(new Location(firstIdProperty, additionalIdProperties)));
478            return nextGraph;
479        }
480    
481        /**
482         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
483         * 
484         * @param atPath the path to the node that is to be created.
485         * @return an object that may be used to start another request
486         */
487        public Conjunction<Graph> create( String atPath ) {
488            this.requestQueue.submit(new CreateNodeRequest(new Location(createPath(atPath))));
489            return nextGraph;
490        }
491    
492        /**
493         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
494         * 
495         * @param atPath the path to the node that is to be created.
496         * @param properties the properties for the new node
497         * @return an object that may be used to start another request
498         */
499        public Conjunction<Graph> create( String atPath,
500                                          Property... properties ) {
501            this.requestQueue.submit(new CreateNodeRequest(new Location(createPath(atPath)), properties));
502            return nextGraph;
503        }
504    
505        /**
506         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
507         * 
508         * @param at the path to the node that is to be created.
509         * @return an object that may be used to start another request
510         */
511        public Conjunction<Graph> create( Path at ) {
512            this.requestQueue.submit(new CreateNodeRequest(new Location(at)));
513            return nextGraph;
514        }
515    
516        /**
517         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
518         * 
519         * @param at the path to the node that is to be created.
520         * @param properties the properties for the new node
521         * @return an object that may be used to start another request
522         */
523        public Conjunction<Graph> create( Path at,
524                                          Property... properties ) {
525            this.requestQueue.submit(new CreateNodeRequest(new Location(at), properties));
526            return nextGraph;
527        }
528    
529        /**
530         * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
531         * 
532         * @param at the path to the node that is to be created.
533         * @param properties the properties for the new node
534         * @return an object that may be used to start another request
535         */
536        public Conjunction<Graph> create( Path at,
537                                          Iterable<Property> properties ) {
538            this.requestQueue.submit(new CreateNodeRequest(new Location(at), properties));
539            return nextGraph;
540        }
541    
542        /**
543         * Set the properties on a node.
544         * 
545         * @param properties the properties to set
546         * @return the remove request object that should be used to specify the node on which the properties are to be set.
547         */
548        public On<Conjunction<Graph>> set( final Property... properties ) {
549            return new On<Conjunction<Graph>>() {
550                @SuppressWarnings( "synthetic-access" )
551                public Conjunction<Graph> on( Location location ) {
552                    UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, properties);
553                    queue().submit(request);
554                    return nextGraph;
555                }
556    
557                public Conjunction<Graph> on( String path ) {
558                    return on(new Location(createPath(path)));
559                }
560    
561                public Conjunction<Graph> on( Path path ) {
562                    return on(new Location(path));
563                }
564    
565                public Conjunction<Graph> on( Property idProperty ) {
566                    return on(new Location(idProperty));
567                }
568    
569                public Conjunction<Graph> on( Property firstIdProperty,
570                                              Property... additionalIdProperties ) {
571                    return on(new Location(firstIdProperty, additionalIdProperties));
572                }
573    
574                public Conjunction<Graph> on( UUID uuid ) {
575                    return on(new Location(uuid));
576                }
577            };
578        }
579    
580        /**
581         * Remove properties from the node at the given location.
582         * 
583         * @param propertyNames the names of the properties to be removed
584         * @return the remove request object that should be used to specify the node from which the properties are to be removed.
585         */
586        public On<Conjunction<Graph>> remove( final Name... propertyNames ) {
587            return new On<Conjunction<Graph>>() {
588                @SuppressWarnings( "synthetic-access" )
589                public Conjunction<Graph> on( Location location ) {
590                    RemovePropertiesRequest request = new RemovePropertiesRequest(location, propertyNames);
591                    queue().submit(request);
592                    return nextGraph;
593                }
594    
595                public Conjunction<Graph> on( String path ) {
596                    return on(new Location(createPath(path)));
597                }
598    
599                public Conjunction<Graph> on( Path path ) {
600                    return on(new Location(path));
601                }
602    
603                public Conjunction<Graph> on( Property idProperty ) {
604                    return on(new Location(idProperty));
605                }
606    
607                public Conjunction<Graph> on( Property firstIdProperty,
608                                              Property... additionalIdProperties ) {
609                    return on(new Location(firstIdProperty, additionalIdProperties));
610                }
611    
612                public Conjunction<Graph> on( UUID uuid ) {
613                    return on(new Location(uuid));
614                }
615            };
616        }
617    
618        /**
619         * Remove properties from the node at the given location.
620         * 
621         * @param propertyNames the names of the properties to be removed
622         * @return the remove request object that should be used to specify the node from which the properties are to be removed.
623         */
624        public On<Conjunction<Graph>> remove( String... propertyNames ) {
625            NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
626            final List<Name> names = new LinkedList<Name>();
627            for (String propertyName : propertyNames) {
628                names.add(nameFactory.create(propertyName));
629            }
630            return new On<Conjunction<Graph>>() {
631                @SuppressWarnings( "synthetic-access" )
632                public Conjunction<Graph> on( Location location ) {
633                    RemovePropertiesRequest request = new RemovePropertiesRequest(location, names);
634                    queue().submit(request);
635                    return nextGraph;
636                }
637    
638                public Conjunction<Graph> on( String path ) {
639                    return on(new Location(createPath(path)));
640                }
641    
642                public Conjunction<Graph> on( Path path ) {
643                    return on(new Location(path));
644                }
645    
646                public Conjunction<Graph> on( Property idProperty ) {
647                    return on(new Location(idProperty));
648                }
649    
650                public Conjunction<Graph> on( Property firstIdProperty,
651                                              Property... additionalIdProperties ) {
652                    return on(new Location(firstIdProperty, additionalIdProperties));
653                }
654    
655                public Conjunction<Graph> on( UUID uuid ) {
656                    return on(new Location(uuid));
657                }
658            };
659        }
660    
661        /**
662         * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
663         * object. Once the location is specified, the {@link Collection collection of properties} are read and then returned.
664         * 
665         * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
666         */
667        public On<Collection<Property>> getProperties() {
668            return new On<Collection<Property>>() {
669                public Collection<Property> on( Location location ) {
670                    ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location);
671                    queue().submit(request);
672                    return request.getProperties();
673                }
674    
675                public Collection<Property> on( String path ) {
676                    return on(new Location(createPath(path)));
677                }
678    
679                public Collection<Property> on( Path path ) {
680                    return on(new Location(path));
681                }
682    
683                public Collection<Property> on( Property idProperty ) {
684                    return on(new Location(idProperty));
685                }
686    
687                public Collection<Property> on( Property firstIdProperty,
688                                                Property... additionalIdProperties ) {
689                    return on(new Location(firstIdProperty, additionalIdProperties));
690                }
691    
692                public Collection<Property> on( UUID uuid ) {
693                    return on(new Location(uuid));
694                }
695            };
696        }
697    
698        /**
699         * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
700         * object. Once the location is specified, the {@link Map map of properties} are read and then returned.
701         * 
702         * @return the object that is used to specified the node whose properties are to be read, and which will return the properties
703         *         as a map keyed by their name
704         */
705        public On<Map<Name, Property>> getPropertiesByName() {
706            return new On<Map<Name, Property>>() {
707                public Map<Name, Property> on( Location location ) {
708                    ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location);
709                    queue().submit(request);
710                    return request.getPropertiesByName();
711                }
712    
713                public Map<Name, Property> on( String path ) {
714                    return on(new Location(createPath(path)));
715                }
716    
717                public Map<Name, Property> on( Path path ) {
718                    return on(new Location(path));
719                }
720    
721                public Map<Name, Property> on( Property idProperty ) {
722                    return on(new Location(idProperty));
723                }
724    
725                public Map<Name, Property> on( Property firstIdProperty,
726                                               Property... additionalIdProperties ) {
727                    return on(new Location(firstIdProperty, additionalIdProperties));
728                }
729    
730                public Map<Name, Property> on( UUID uuid ) {
731                    return on(new Location(uuid));
732                }
733            };
734        }
735    
736        /**
737         * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
738         * object. Once the location is specified, the {@link List list of children} are read and then returned.
739         * 
740         * @return the object that is used to specified the node whose children are to be read, and which will return the children
741         */
742        public Of<List<Location>> getChildren() {
743            return new Of<List<Location>>() {
744                public List<Location> of( String path ) {
745                    return of(new Location(createPath(path)));
746                }
747    
748                public List<Location> of( Path path ) {
749                    return of(new Location(path));
750                }
751    
752                public List<Location> of( Property idProperty ) {
753                    return of(new Location(idProperty));
754                }
755    
756                public List<Location> of( Property firstIdProperty,
757                                          Property... additionalIdProperties ) {
758                    return of(new Location(firstIdProperty, additionalIdProperties));
759                }
760    
761                public List<Location> of( UUID uuid ) {
762                    return of(new Location(uuid));
763                }
764    
765                public List<Location> of( Location at ) {
766                    ReadAllChildrenRequest request = new ReadAllChildrenRequest(at);
767                    queue().submit(request);
768                    return request.getChildren();
769                }
770            };
771        }
772    
773        /**
774         * Request that the children in the specified index range be read on the node defined via the <code>of(...)</code> method on
775         * the returned {@link Of} object. Once the location is specified, the {@link List list of children} are read and then
776         * returned.
777         * 
778         * @param startingIndex the index of the first child to be read
779         * @param endingIndex the index past the last the first child to be read
780         * @return the object that is used to specified the node whose children are to be read, and which will return the children
781         */
782        public Of<List<Location>> getChildrenInRange( final int startingIndex,
783                                                      final int endingIndex ) {
784            CheckArg.isNonNegative(startingIndex, "startingIndex");
785            CheckArg.isPositive(endingIndex, "endingIndex");
786            int count = endingIndex - startingIndex;
787            return getChildrenInBlock(startingIndex, count);
788        }
789    
790        /**
791         * Request that the children in the specified block be read on the node defined via the <code>of(...)</code> method on the
792         * returned {@link Of} object. Once the location is specified, the {@link List list of children} are read and then returned.
793         * 
794         * @param startingIndex the index of the first child to be read
795         * @param blockSize the maximum number of children that should be read
796         * @return the object that is used to specified the node whose children are to be read, and which will return the children
797         */
798        public Of<List<Location>> getChildrenInBlock( final int startingIndex,
799                                                      final int blockSize ) {
800            CheckArg.isNonNegative(startingIndex, "startingIndex");
801            CheckArg.isPositive(blockSize, "blockSize");
802            return new Of<List<Location>>() {
803                public List<Location> of( String path ) {
804                    return of(new Location(createPath(path)));
805                }
806    
807                public List<Location> of( Path path ) {
808                    return of(new Location(path));
809                }
810    
811                public List<Location> of( Property idProperty ) {
812                    return of(new Location(idProperty));
813                }
814    
815                public List<Location> of( Property firstIdProperty,
816                                          Property... additionalIdProperties ) {
817                    return of(new Location(firstIdProperty, additionalIdProperties));
818                }
819    
820                public List<Location> of( UUID uuid ) {
821                    return of(new Location(uuid));
822                }
823    
824                public List<Location> of( Location at ) {
825                    ReadBlockOfChildrenRequest request = new ReadBlockOfChildrenRequest(at, startingIndex, blockSize);
826                    queue().submit(request);
827                    return request.getChildren();
828                }
829            };
830        }
831    
832        /**
833         * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
834         * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
835         * 
836         * @param name the name of the property that is to be read
837         * @return the object that is used to specified the node whose property is to be read, and which will return the property
838         */
839        public On<Property> getProperty( final String name ) {
840            Name nameObj = context.getValueFactories().getNameFactory().create(name);
841            return getProperty(nameObj);
842        }
843    
844        /**
845         * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
846         * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
847         * 
848         * @param name the name of the property that is to be read
849         * @return the object that is used to specified the node whose property is to be read, and which will return the property
850         */
851        public On<Property> getProperty( final Name name ) {
852            return new On<Property>() {
853                public Property on( String path ) {
854                    return on(new Location(createPath(path)));
855                }
856    
857                public Property on( Path path ) {
858                    return on(new Location(path));
859                }
860    
861                public Property on( Property idProperty ) {
862                    return on(new Location(idProperty));
863                }
864    
865                public Property on( Property firstIdProperty,
866                                    Property... additionalIdProperties ) {
867                    return on(new Location(firstIdProperty, additionalIdProperties));
868                }
869    
870                public Property on( UUID uuid ) {
871                    return on(new Location(uuid));
872                }
873    
874                public Property on( Location at ) {
875                    ReadPropertyRequest request = new ReadPropertyRequest(at, name);
876                    queue().submit(request);
877                    return request.getProperty();
878                }
879            };
880        }
881    
882        /**
883         * Request to read the node with the supplied UUID.
884         * 
885         * @param uuid the UUID of the node that is to be read
886         * @return the node that is read from the repository
887         */
888        public Node getNodeAt( UUID uuid ) {
889            return getNodeAt(new Location(uuid));
890        }
891    
892        /**
893         * Request to read the node at the supplied location.
894         * 
895         * @param location the location of the node that is to be read
896         * @return the node that is read from the repository
897         */
898        public Node getNodeAt( Location location ) {
899            ReadNodeRequest request = new ReadNodeRequest(location);
900            this.requestQueue.submit(request);
901            return new GraphNode(request);
902        }
903    
904        /**
905         * Request to read the node at the supplied path.
906         * 
907         * @param path the path of the node that is to be read
908         * @return the node that is read from the repository
909         */
910        public Node getNodeAt( String path ) {
911            return getNodeAt(new Location(createPath(path)));
912        }
913    
914        /**
915         * Request to read the node at the supplied path.
916         * 
917         * @param path the path of the node that is to be read
918         * @return the node that is read from the repository
919         */
920        public Node getNodeAt( Path path ) {
921            return getNodeAt(new Location(path));
922        }
923    
924        /**
925         * Request to read the node with the supplied unique identifier property.
926         * 
927         * @param idProperty the identification property that is unique to the node that is to be read
928         * @return the node that is read from the repository
929         */
930        public Node getNodeAt( Property idProperty ) {
931            return getNodeAt(new Location(idProperty));
932        }
933    
934        /**
935         * Request to read the node with the supplied unique identifier properties.
936         * 
937         * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
938         * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be read
939         * @return the node that is read from the repository
940         */
941        public Node getNodeAt( Property firstIdProperty,
942                               Property... additionalIdProperties ) {
943            return getNodeAt(new Location(firstIdProperty, additionalIdProperties));
944        }
945    
946        /**
947         * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code> in
948         * the resulting {@link At} object. All properties and children of every node in the subgraph will be read and returned in the
949         * {@link Subgraph} object returned from the <code>at(...)</code> methods.
950         * 
951         * @param depth the maximum depth of the subgraph that should be read
952         * @return the component that should be used to specify the location of the node that is the top of the subgraph, and which
953         *         will return the {@link Subgraph} containing the results
954         */
955        public At<Subgraph> getSubgraphOfDepth( final int depth ) {
956            return new At<Subgraph>() {
957                public Subgraph at( Location location ) {
958                    ReadBranchRequest request = new ReadBranchRequest(location, depth);
959                    queue().submit(request);
960                    return new SubgraphResults(request);
961                }
962    
963                public Subgraph at( String path ) {
964                    return at(new Location(createPath(path)));
965                }
966    
967                public Subgraph at( Path path ) {
968                    return at(new Location(path));
969                }
970    
971                public Subgraph at( UUID uuid ) {
972                    return at(new Location(uuid));
973                }
974    
975                public Subgraph at( Property idProperty ) {
976                    return at(new Location(idProperty));
977                }
978    
979                public Subgraph at( Property firstIdProperty,
980                                    Property... additionalIdProperties ) {
981                    return at(new Location(firstIdProperty, additionalIdProperties));
982                }
983            };
984        }
985    
986        /**
987         * Import the content from the XML file at the supplied URI, specifying via the returned {@link ImportInto object} where the
988         * content is to be imported.
989         * 
990         * @param uri the URI where the importer can read the content that is to be imported
991         * @return the object that should be used to specify into which the content is to be imported
992         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
993         */
994        public ImportInto<Conjunction<Graph>> importXmlFrom( final URI uri ) {
995            return new ImportInto<Conjunction<Graph>>() {
996                private boolean skipRootElement = false;
997    
998                public ImportInto<Conjunction<Graph>> skippingRootElement( boolean skipRootElement ) {
999                    this.skipRootElement = skipRootElement;
1000                    return this;
1001                }
1002    
1003                public Conjunction<Graph> into( String path ) throws IOException, SAXException {
1004                    return into(new Location(createPath(path)));
1005                }
1006    
1007                public Conjunction<Graph> into( Path path ) throws IOException, SAXException {
1008                    return into(new Location(path));
1009                }
1010    
1011                public Conjunction<Graph> into( Property idProperty ) throws IOException, SAXException {
1012                    return into(new Location(idProperty));
1013                }
1014    
1015                public Conjunction<Graph> into( Property firstIdProperty,
1016                                                Property... additionalIdProperties ) throws IOException, SAXException {
1017                    return into(new Location(firstIdProperty, additionalIdProperties));
1018                }
1019    
1020                public Conjunction<Graph> into( UUID uuid ) throws IOException, SAXException {
1021                    return into(new Location(uuid));
1022                }
1023    
1024                @SuppressWarnings( "synthetic-access" )
1025                public Conjunction<Graph> into( Location at ) throws IOException, SAXException {
1026                    GraphImporter importer = new GraphImporter(Graph.this);
1027                    importer.importXml(uri, at, skipRootElement).execute(); // 'importXml' creates and uses a new batch
1028                    return Graph.this.nextGraph;
1029                }
1030            };
1031        }
1032    
1033        /**
1034         * Import the content from the XML file at the supplied file location, specifying via the returned {@link ImportInto object}
1035         * where the content is to be imported.
1036         * 
1037         * @param pathToFile the path to the XML file that should be imported.
1038         * @return the object that should be used to specify into which the content is to be imported
1039         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
1040         */
1041        public ImportInto<Conjunction<Graph>> importXmlFrom( String pathToFile ) {
1042            CheckArg.isNotNull(pathToFile, "pathToFile");
1043            return importXmlFrom(new File(pathToFile).toURI());
1044        }
1045    
1046        /**
1047         * Import the content from the XML file at the supplied file, specifying via the returned {@link ImportInto object} where the
1048         * content is to be imported.
1049         * 
1050         * @param file the XML file that should be imported.
1051         * @return the object that should be used to specify into which the content is to be imported
1052         * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
1053         */
1054        public ImportInto<Conjunction<Graph>> importXmlFrom( File file ) {
1055            CheckArg.isNotNull(file, "file");
1056            return importXmlFrom(file.toURI());
1057        }
1058    
1059        /*package*/Path createPath( String path ) {
1060            return getContext().getValueFactories().getPathFactory().create(path);
1061        }
1062    
1063        /*package*/void execute( Request request ) {
1064            RepositoryConnection connection = Graph.this.getConnectionFactory().createConnection(getSourceName());
1065            if (connection == null) {
1066                throw new RepositorySourceException(GraphI18n.unableToFindRepositorySourceWithName.text(getSourceName()));
1067            }
1068            try {
1069                connection.execute(Graph.this.getContext(), request);
1070            } finally {
1071                connection.close();
1072            }
1073            if (request.hasError()) {
1074                Throwable error = request.getError();
1075                if (error instanceof RuntimeException) throw (RuntimeException)error;
1076                throw new RepositorySourceException(getSourceName(), error);
1077            }
1078        }
1079    
1080        /*package*/List<Segment> getSegments( List<Location> locations ) {
1081            List<Segment> segments = new ArrayList<Segment>(locations.size());
1082            for (Location location : locations) {
1083                segments.add(location.getPath().getLastSegment());
1084            }
1085            return segments;
1086        }
1087    
1088        /**
1089         * Begin a batch of requests to perform various operations. Use this approach when multiple operations are to be built and
1090         * then executed with one submission to the underlying {@link #getSourceName() repository source}. The {@link Results results}
1091         * are not available until the {@link Batch#execute()} method is invoked.
1092         * 
1093         * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
1094         * @see Batch#execute()
1095         * @see Results
1096         */
1097        public Batch batch() {
1098            return new Batch();
1099        }
1100    
1101        /**
1102         * Interface for creating multiple requests to perform various operations. Note that all the requests are accumulated until
1103         * the {@link #execute()} method is called. The results of all the operations are then available in the {@link Results} object
1104         * returned by the {@link #execute()}.
1105         * 
1106         * @author Randall Hauch
1107         */
1108        @Immutable
1109        public final class Batch implements Executable {
1110            protected final CompositingRequestQueue requestQueue = new CompositingRequestQueue();
1111            protected final BatchConjunction nextRequests;
1112            protected boolean executed = false;
1113    
1114            /*package*/Batch() {
1115                this.nextRequests = new BatchConjunction() {
1116                    public Batch and() {
1117                        return Batch.this;
1118                    }
1119    
1120                    public Results execute() {
1121                        executed = true;
1122                        return Batch.this.requestQueue.execute();
1123                    }
1124                };
1125            }
1126    
1127            /**
1128             * Obtain the graph that this batch uses.
1129             * 
1130             * @return the graph; never null
1131             */
1132            public Graph getGraph() {
1133                return Graph.this;
1134            }
1135    
1136            protected final void assertNotExecuted() {
1137                if (executed) {
1138                    throw new IllegalStateException(GraphI18n.unableToAddMoreRequestsToAlreadyExecutedBatch.text());
1139                }
1140            }
1141    
1142            /**
1143             * Begin the request to move the specified node into a parent node at a different location, which is specified via the
1144             * <code>into(...)</code> method on the returned {@link Move} object.
1145             * <p>
1146             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1147             * called.
1148             * </p>
1149             * 
1150             * @param from the node that is to be moved.
1151             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1152             *         to be moved
1153             */
1154            public Move<BatchConjunction> move( Node from ) {
1155                assertNotExecuted();
1156                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, from.getLocation());
1157            }
1158    
1159            /**
1160             * Begin the request to move a node at the specified location into a parent node at a different location, which is
1161             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
1162             * <p>
1163             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1164             * called.
1165             * </p>
1166             * 
1167             * @param from the location of the node that is to be moved.
1168             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1169             *         to be moved
1170             */
1171            public Move<BatchConjunction> move( Location from ) {
1172                assertNotExecuted();
1173                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, from);
1174            }
1175    
1176            /**
1177             * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
1178             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
1179             * <p>
1180             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1181             * called.
1182             * </p>
1183             * 
1184             * @param fromPath the path to the node that is to be moved.
1185             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1186             *         to be moved
1187             */
1188            public Move<BatchConjunction> move( String fromPath ) {
1189                assertNotExecuted();
1190                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, new Location(createPath(fromPath)));
1191            }
1192    
1193            /**
1194             * Begin the request to move a node located at the supplied path into a parent node at a different location, which is
1195             * specified via the <code>into(...)</code> method on the returned {@link Move} object.
1196             * <p>
1197             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1198             * called.
1199             * </p>
1200             * 
1201             * @param from the path to the node that is to be moved.
1202             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1203             *         to be moved
1204             */
1205            public Move<BatchConjunction> move( Path from ) {
1206                assertNotExecuted();
1207                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, new Location(from));
1208            }
1209    
1210            /**
1211             * Begin the request to move a node with the specified unique identifier into a parent node at a different location, which
1212             * is specified via the <code>into(...)</code> method on the returned {@link Move} object.
1213             * <p>
1214             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1215             * called.
1216             * </p>
1217             * 
1218             * @param from the UUID of the node that is to be moved.
1219             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1220             *         to be moved
1221             */
1222            public Move<BatchConjunction> move( UUID from ) {
1223                assertNotExecuted();
1224                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, new Location(from));
1225            }
1226    
1227            /**
1228             * Begin the request to move a node with the specified unique identification property into a parent node at a different
1229             * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
1230             * identification property should uniquely identify a single node.
1231             * <p>
1232             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1233             * called.
1234             * </p>
1235             * 
1236             * @param idProperty the unique identification property of the node that is to be moved.
1237             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1238             *         to be moved
1239             */
1240            public Move<BatchConjunction> move( Property idProperty ) {
1241                assertNotExecuted();
1242                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, new Location(idProperty));
1243            }
1244    
1245            /**
1246             * Begin the request to move a node with the specified identification properties into a parent node at a different
1247             * location, which is specified via the <code>into(...)</code> method on the returned {@link Move} object. The
1248             * identification properties should uniquely identify a single node.
1249             * <p>
1250             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1251             * called.
1252             * </p>
1253             * 
1254             * @param firstIdProperty the first identification property of the node that is to be moved
1255             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be moved
1256             * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
1257             *         to be moved
1258             */
1259            public Move<BatchConjunction> move( Property firstIdProperty,
1260                                                Property... additionalIdProperties ) {
1261                assertNotExecuted();
1262                return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, new Location(firstIdProperty,
1263                                                                                                           additionalIdProperties));
1264            }
1265    
1266            /**
1267             * Begin the request to copy the specified node into a parent node at a different location, which is specified via the
1268             * <code>into(...)</code> method on the returned {@link Copy} object.
1269             * <p>
1270             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1271             * called.
1272             * </p>
1273             * 
1274             * @param from the node that is to be copied.
1275             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1276             *         is to be copied
1277             */
1278            public Copy<BatchConjunction> copy( Node from ) {
1279                assertNotExecuted();
1280                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, from.getLocation());
1281            }
1282    
1283            /**
1284             * Begin the request to copy a node at the specified location into a parent node at a different location, which is
1285             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
1286             * <p>
1287             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1288             * called.
1289             * </p>
1290             * 
1291             * @param from the location of the node that is to be copied.
1292             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1293             *         is to be copied
1294             */
1295            public Copy<BatchConjunction> copy( Location from ) {
1296                assertNotExecuted();
1297                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, from);
1298            }
1299    
1300            /**
1301             * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
1302             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
1303             * <p>
1304             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1305             * called.
1306             * </p>
1307             * 
1308             * @param fromPath the path to the node that is to be copied.
1309             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1310             *         is to be copied
1311             */
1312            public Copy<BatchConjunction> copy( String fromPath ) {
1313                assertNotExecuted();
1314                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, new Location(createPath(fromPath)));
1315            }
1316    
1317            /**
1318             * Begin the request to copy a node located at the supplied path into a parent node at a different location, which is
1319             * specified via the <code>into(...)</code> method on the returned {@link Copy} object.
1320             * <p>
1321             * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the
1322             * <code>into(...)</code> method is called.
1323             * </p>
1324             * 
1325             * @param from the path to the node that is to be copied.
1326             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1327             *         is to be copied
1328             */
1329            public Copy<BatchConjunction> copy( Path from ) {
1330                assertNotExecuted();
1331                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, new Location(from));
1332            }
1333    
1334            /**
1335             * Begin the request to copy a node with the specified unique identifier into a parent node at a different location, which
1336             * is specified via the <code>into(...)</code> method on the returned {@link Copy} object.
1337             * <p>
1338             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1339             * called.
1340             * </p>
1341             * 
1342             * @param from the UUID of the node that is to be copied.
1343             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1344             *         is to be copied
1345             */
1346            public Copy<BatchConjunction> copy( UUID from ) {
1347                assertNotExecuted();
1348                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, new Location(from));
1349            }
1350    
1351            /**
1352             * Begin the request to copy a node with the specified unique identification property into a parent node at a different
1353             * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
1354             * identification property should uniquely identify a single node.
1355             * <p>
1356             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1357             * called.
1358             * </p>
1359             * 
1360             * @param idProperty the unique identification property of the node that is to be copied.
1361             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1362             *         is to be copied
1363             */
1364            public Copy<BatchConjunction> copy( Property idProperty ) {
1365                assertNotExecuted();
1366                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, new Location(idProperty));
1367            }
1368    
1369            /**
1370             * Begin the request to copy a node with the specified identification properties into a parent node at a different
1371             * location, which is specified via the <code>into(...)</code> method on the returned {@link Copy} object. The
1372             * identification properties should uniquely identify a single node.
1373             * <p>
1374             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1375             * called.
1376             * </p>
1377             * 
1378             * @param firstIdProperty the first identification property of the node that is to be copied
1379             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
1380             * @return the object that can be used to specify addition nodes to be copied or the location of the node where the node
1381             *         is to be copied
1382             */
1383            public Copy<BatchConjunction> copy( Property firstIdProperty,
1384                                                Property... additionalIdProperties ) {
1385                assertNotExecuted();
1386                return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, new Location(firstIdProperty,
1387                                                                                                      additionalIdProperties));
1388            }
1389    
1390            /**
1391             * Request to delete the specified node.
1392             * <p>
1393             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1394             * called.
1395             * </p>
1396             * 
1397             * @param at the node that is to be deleted
1398             * @return an object that may be used to start another request
1399             */
1400            public BatchConjunction delete( Node at ) {
1401                assertNotExecuted();
1402                this.requestQueue.submit(new DeleteBranchRequest(at.getLocation()));
1403                return nextRequests;
1404            }
1405    
1406            /**
1407             * Request to delete the node at the given location.
1408             * <p>
1409             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1410             * called.
1411             * </p>
1412             * 
1413             * @param at the location of the node that is to be deleted
1414             * @return an object that may be used to start another request
1415             */
1416            public BatchConjunction delete( Location at ) {
1417                assertNotExecuted();
1418                this.requestQueue.submit(new DeleteBranchRequest(at));
1419                return nextRequests;
1420            }
1421    
1422            /**
1423             * Request to delete the node at the given path.
1424             * <p>
1425             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1426             * called.
1427             * </p>
1428             * 
1429             * @param atPath the path of the node that is to be deleted
1430             * @return an object that may be used to start another request
1431             */
1432            public BatchConjunction delete( String atPath ) {
1433                assertNotExecuted();
1434                this.requestQueue.submit(new DeleteBranchRequest(new Location(createPath(atPath))));
1435                return nextRequests;
1436            }
1437    
1438            /**
1439             * Request to delete the node at the given path.
1440             * <p>
1441             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1442             * called.
1443             * </p>
1444             * 
1445             * @param at the path of the node that is to be deleted
1446             * @return an object that may be used to start another request
1447             */
1448            public BatchConjunction delete( Path at ) {
1449                assertNotExecuted();
1450                this.requestQueue.submit(new DeleteBranchRequest(new Location(at)));
1451                return nextRequests;
1452            }
1453    
1454            /**
1455             * Request to delete the node with the given UUID.
1456             * <p>
1457             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1458             * called.
1459             * </p>
1460             * 
1461             * @param at the UUID of the node that is to be deleted
1462             * @return an object that may be used to start another request
1463             */
1464            public BatchConjunction delete( UUID at ) {
1465                assertNotExecuted();
1466                this.requestQueue.submit(new DeleteBranchRequest(new Location(at)));
1467                return nextRequests;
1468            }
1469    
1470            /**
1471             * Request to delete the node with the given unique identification property.
1472             * <p>
1473             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1474             * called.
1475             * </p>
1476             * 
1477             * @param idProperty the unique identifying property of the node that is to be deleted
1478             * @return an object that may be used to start another request
1479             */
1480            public BatchConjunction delete( Property idProperty ) {
1481                assertNotExecuted();
1482                this.requestQueue.submit(new DeleteBranchRequest(new Location(idProperty)));
1483                return nextRequests;
1484            }
1485    
1486            /**
1487             * Request to delete the node with the given identification properties. The identification properties should uniquely
1488             * identify a single node.
1489             * <p>
1490             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1491             * called.
1492             * </p>
1493             * 
1494             * @param firstIdProperty the first identification property of the node that is to be copied
1495             * @param additionalIdProperties the remaining idenficiation properties of the node that is to be copied
1496             * @return an object that may be used to start another request
1497             */
1498            public BatchConjunction delete( Property firstIdProperty,
1499                                            Property... additionalIdProperties ) {
1500                assertNotExecuted();
1501                this.requestQueue.submit(new DeleteBranchRequest(new Location(firstIdProperty, additionalIdProperties)));
1502                return nextRequests;
1503            }
1504    
1505            /**
1506             * Begin the request to create a node located at the supplied path.
1507             * <p>
1508             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1509             * called.
1510             * </p>
1511             * 
1512             * @param atPath the path to the node that is to be created.
1513             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1514             *         node where the node is to be created
1515             */
1516            public Create<BatchConjunction> create( String atPath ) {
1517                assertNotExecuted();
1518                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath)));
1519            }
1520    
1521            /**
1522             * Begin the request to create a node located at the supplied path.
1523             * <p>
1524             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1525             * called.
1526             * </p>
1527             * 
1528             * @param atPath the path to the node that is to be created.
1529             * @param property a property for the new node
1530             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1531             *         node where the node is to be created
1532             */
1533            public Create<BatchConjunction> create( String atPath,
1534                                                    Property property ) {
1535                assertNotExecuted();
1536                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath))).with(property);
1537            }
1538    
1539            /**
1540             * Begin the request to create a node located at the supplied path.
1541             * <p>
1542             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1543             * called.
1544             * </p>
1545             * 
1546             * @param atPath the path to the node that is to be created.
1547             * @param firstProperty a property for the new node
1548             * @param additionalProperties additional properties for the new node
1549             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1550             *         node where the node is to be created
1551             */
1552            public Create<BatchConjunction> create( String atPath,
1553                                                    Property firstProperty,
1554                                                    Property... additionalProperties ) {
1555                assertNotExecuted();
1556                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath))).with(firstProperty,
1557                                                                                                                             additionalProperties);
1558            }
1559    
1560            /**
1561             * Begin the request to create a node located at the supplied path.
1562             * <p>
1563             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1564             * called.
1565             * </p>
1566             * 
1567             * @param at the path to the node that is to be created.
1568             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1569             *         node where the node is to be created
1570             */
1571            public Create<BatchConjunction> create( Path at ) {
1572                assertNotExecuted();
1573                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at));
1574            }
1575    
1576            /**
1577             * Begin the request to create a node located at the supplied path.
1578             * <p>
1579             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1580             * called.
1581             * </p>
1582             * 
1583             * @param at the path to the node that is to be created.
1584             * @param properties the iterator over the properties for the new node
1585             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1586             *         node where the node is to be created
1587             */
1588            public Create<BatchConjunction> create( Path at,
1589                                                    Iterable<Property> properties ) {
1590                assertNotExecuted();
1591                CreateAction<BatchConjunction> action = new CreateAction<BatchConjunction>(nextRequests, requestQueue,
1592                                                                                           new Location(at));
1593                for (Property property : properties) {
1594                    action.and(property);
1595                }
1596                return action;
1597            }
1598    
1599            /**
1600             * Begin the request to create a node located at the supplied path.
1601             * <p>
1602             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1603             * called.
1604             * </p>
1605             * 
1606             * @param at the path to the node that is to be created.
1607             * @param property a property for the new node
1608             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1609             *         node where the node is to be created
1610             */
1611            public Create<BatchConjunction> create( Path at,
1612                                                    Property property ) {
1613                assertNotExecuted();
1614                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at)).with(property);
1615            }
1616    
1617            /**
1618             * Begin the request to create a node located at the supplied path.
1619             * <p>
1620             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1621             * called.
1622             * </p>
1623             * 
1624             * @param at the path to the node that is to be created.
1625             * @param firstProperty a property for the new node
1626             * @param additionalProperties additional properties for the new node
1627             * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
1628             *         node where the node is to be created
1629             */
1630            public Create<BatchConjunction> create( Path at,
1631                                                    Property firstProperty,
1632                                                    Property... additionalProperties ) {
1633                assertNotExecuted();
1634                return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at)).with(firstProperty,
1635                                                                                                             additionalProperties);
1636            }
1637    
1638            /**
1639             * Set the properties on a node.
1640             * 
1641             * @param properties the properties to set
1642             * @return the remove request object that should be used to specify the node on which the properties are to be set.
1643             */
1644            public On<BatchConjunction> set( final Property... properties ) {
1645                return new On<BatchConjunction>() {
1646                    public BatchConjunction on( Location location ) {
1647                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, properties);
1648                        queue().submit(request);
1649                        return nextRequests;
1650                    }
1651    
1652                    public BatchConjunction on( String path ) {
1653                        return on(new Location(createPath(path)));
1654                    }
1655    
1656                    public BatchConjunction on( Path path ) {
1657                        return on(new Location(path));
1658                    }
1659    
1660                    public BatchConjunction on( Property idProperty ) {
1661                        return on(new Location(idProperty));
1662                    }
1663    
1664                    public BatchConjunction on( Property firstIdProperty,
1665                                                Property... additionalIdProperties ) {
1666                        return on(new Location(firstIdProperty, additionalIdProperties));
1667                    }
1668    
1669                    public BatchConjunction on( UUID uuid ) {
1670                        return on(new Location(uuid));
1671                    }
1672                };
1673            }
1674    
1675            /**
1676             * Remove properties from the node at the given location.
1677             * 
1678             * @param propertyNames the names of the properties to be removed
1679             * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1680             */
1681            public On<BatchConjunction> remove( final Name... propertyNames ) {
1682                return new On<BatchConjunction>() {
1683                    public BatchConjunction on( Location location ) {
1684                        RemovePropertiesRequest request = new RemovePropertiesRequest(location, propertyNames);
1685                        queue().submit(request);
1686                        return nextRequests;
1687                    }
1688    
1689                    public BatchConjunction on( String path ) {
1690                        return on(new Location(createPath(path)));
1691                    }
1692    
1693                    public BatchConjunction on( Path path ) {
1694                        return on(new Location(path));
1695                    }
1696    
1697                    public BatchConjunction on( Property idProperty ) {
1698                        return on(new Location(idProperty));
1699                    }
1700    
1701                    public BatchConjunction on( Property firstIdProperty,
1702                                                Property... additionalIdProperties ) {
1703                        return on(new Location(firstIdProperty, additionalIdProperties));
1704                    }
1705    
1706                    public BatchConjunction on( UUID uuid ) {
1707                        return on(new Location(uuid));
1708                    }
1709                };
1710            }
1711    
1712            /**
1713             * Remove properties from the node at the given location.
1714             * 
1715             * @param propertyNames the names of the properties to be removed
1716             * @return the remove request object that should be used to specify the node from which the properties are to be removed.
1717             */
1718            public On<BatchConjunction> remove( String... propertyNames ) {
1719                NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
1720                final List<Name> names = new LinkedList<Name>();
1721                for (String propertyName : propertyNames) {
1722                    names.add(nameFactory.create(propertyName));
1723                }
1724                return new On<BatchConjunction>() {
1725                    public BatchConjunction on( Location location ) {
1726                        RemovePropertiesRequest request = new RemovePropertiesRequest(location, names);
1727                        queue().submit(request);
1728                        return nextRequests;
1729                    }
1730    
1731                    public BatchConjunction on( String path ) {
1732                        return on(new Location(createPath(path)));
1733                    }
1734    
1735                    public BatchConjunction on( Path path ) {
1736                        return on(new Location(path));
1737                    }
1738    
1739                    public BatchConjunction on( Property idProperty ) {
1740                        return on(new Location(idProperty));
1741                    }
1742    
1743                    public BatchConjunction on( Property firstIdProperty,
1744                                                Property... additionalIdProperties ) {
1745                        return on(new Location(firstIdProperty, additionalIdProperties));
1746                    }
1747    
1748                    public BatchConjunction on( UUID uuid ) {
1749                        return on(new Location(uuid));
1750                    }
1751                };
1752            }
1753    
1754            /**
1755             * Request to read the node with the supplied UUID.
1756             * <p>
1757             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1758             * called.
1759             * </p>
1760             * 
1761             * @param uuid the UUID of the node that is to be read
1762             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1763             */
1764            public BatchConjunction read( UUID uuid ) {
1765                return read(new Location(uuid));
1766            }
1767    
1768            /**
1769             * Request to read the node at the supplied location.
1770             * <p>
1771             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1772             * called.
1773             * </p>
1774             * 
1775             * @param location the location of the node that is to be read
1776             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1777             */
1778            public BatchConjunction read( Location location ) {
1779                assertNotExecuted();
1780                ReadNodeRequest request = new ReadNodeRequest(location);
1781                requestQueue.submit(request);
1782                return nextRequests;
1783            }
1784    
1785            /**
1786             * Request to read the node at the supplied path.
1787             * <p>
1788             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1789             * called.
1790             * </p>
1791             * 
1792             * @param path the path of the node that is to be read
1793             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1794             */
1795            public BatchConjunction read( String path ) {
1796                return read(new Location(createPath(path)));
1797            }
1798    
1799            /**
1800             * Request to read the node at the supplied path.
1801             * <p>
1802             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1803             * called.
1804             * </p>
1805             * 
1806             * @param path the path of the node that is to be read
1807             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1808             */
1809            public BatchConjunction read( Path path ) {
1810                return read(new Location(path));
1811            }
1812    
1813            /**
1814             * Request to read the node with the supplied unique identifier property.
1815             * <p>
1816             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1817             * called.
1818             * </p>
1819             * 
1820             * @param idProperty the identification property that is unique to the node that is to be read
1821             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1822             */
1823            public BatchConjunction read( Property idProperty ) {
1824                return read(new Location(idProperty));
1825            }
1826    
1827            /**
1828             * Request to read the node with the supplied unique identifier properties.
1829             * <p>
1830             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1831             * called.
1832             * </p>
1833             * 
1834             * @param firstIdProperty the first of the identification properties that uniquely identify the node that is to be read
1835             * @param additionalIdProperties the remaining identification properties that uniquely identify the node that is to be
1836             *        read
1837             * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
1838             */
1839            public BatchConjunction read( Property firstIdProperty,
1840                                          Property... additionalIdProperties ) {
1841                return read(new Location(firstIdProperty, additionalIdProperties));
1842            }
1843    
1844            /**
1845             * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
1846             * returned {@link On} object.
1847             * <p>
1848             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1849             * called.
1850             * </p>
1851             * 
1852             * @param propertyName the name of the property that is to be read
1853             * @return the object that is used to specified the node whose property is to be read
1854             */
1855            public On<BatchConjunction> readProperty( String propertyName ) {
1856                assertNotExecuted();
1857                Name name = Graph.this.getContext().getValueFactories().getNameFactory().create(propertyName);
1858                return readProperty(name);
1859            }
1860    
1861            /**
1862             * Request that the property with the given name be read on the node defined via the <code>on(...)</code> method on the
1863             * returned {@link On} object.
1864             * <p>
1865             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1866             * called.
1867             * </p>
1868             * 
1869             * @param name the name of the property that is to be read
1870             * @return the object that is used to specified the node whose property is to be read
1871             */
1872            public On<BatchConjunction> readProperty( final Name name ) {
1873                assertNotExecuted();
1874                return new On<BatchConjunction>() {
1875                    public BatchConjunction on( String path ) {
1876                        return on(new Location(createPath(path)));
1877                    }
1878    
1879                    public BatchConjunction on( Path path ) {
1880                        return on(new Location(path));
1881                    }
1882    
1883                    public BatchConjunction on( Property idProperty ) {
1884                        return on(new Location(idProperty));
1885                    }
1886    
1887                    public BatchConjunction on( Property firstIdProperty,
1888                                                Property... additionalIdProperties ) {
1889                        return on(new Location(firstIdProperty, additionalIdProperties));
1890                    }
1891    
1892                    public BatchConjunction on( UUID uuid ) {
1893                        return on(new Location(uuid));
1894                    }
1895    
1896                    public BatchConjunction on( Location at ) {
1897                        ReadPropertyRequest request = new ReadPropertyRequest(at, name);
1898                        queue().submit(request);
1899                        return Batch.this.nextRequests;
1900                    }
1901                };
1902            }
1903    
1904            /**
1905             * Request that the properties be read on the node defined via the <code>on(...)</code> method on the returned {@link On}
1906             * object.
1907             * <p>
1908             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1909             * called.
1910             * </p>
1911             * 
1912             * @return the object that is used to specified the node whose properties are to be read,
1913             */
1914            public On<BatchConjunction> readProperties() {
1915                assertNotExecuted();
1916                return new On<BatchConjunction>() {
1917                    public BatchConjunction on( Location location ) {
1918                        ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location);
1919                        queue().submit(request);
1920                        return Batch.this.nextRequests;
1921                    }
1922    
1923                    public BatchConjunction on( String path ) {
1924                        return on(new Location(createPath(path)));
1925                    }
1926    
1927                    public BatchConjunction on( Path path ) {
1928                        return on(new Location(path));
1929                    }
1930    
1931                    public BatchConjunction on( Property idProperty ) {
1932                        return on(new Location(idProperty));
1933                    }
1934    
1935                    public BatchConjunction on( Property firstIdProperty,
1936                                                Property... additionalIdProperties ) {
1937                        return on(new Location(firstIdProperty, additionalIdProperties));
1938                    }
1939    
1940                    public BatchConjunction on( UUID uuid ) {
1941                        return on(new Location(uuid));
1942                    }
1943                };
1944            }
1945    
1946            /**
1947             * Request that the children be read on the node defined via the <code>of(...)</code> method on the returned {@link Of}
1948             * object.
1949             * <p>
1950             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1951             * called.
1952             * </p>
1953             * 
1954             * @return the object that is used to specified the node whose children are to be read
1955             */
1956            public Of<BatchConjunction> readChildren() {
1957                assertNotExecuted();
1958                return new Of<BatchConjunction>() {
1959                    public BatchConjunction of( String path ) {
1960                        return of(new Location(createPath(path)));
1961                    }
1962    
1963                    public BatchConjunction of( Path path ) {
1964                        return of(new Location(path));
1965                    }
1966    
1967                    public BatchConjunction of( Property idProperty ) {
1968                        return of(new Location(idProperty));
1969                    }
1970    
1971                    public BatchConjunction of( Property firstIdProperty,
1972                                                Property... additionalIdProperties ) {
1973                        return of(new Location(firstIdProperty, additionalIdProperties));
1974                    }
1975    
1976                    public BatchConjunction of( UUID uuid ) {
1977                        return of(new Location(uuid));
1978                    }
1979    
1980                    public BatchConjunction of( Location at ) {
1981                        ReadAllChildrenRequest request = new ReadAllChildrenRequest(at);
1982                        queue().submit(request);
1983                        return Batch.this.nextRequests;
1984                    }
1985                };
1986            }
1987    
1988            /**
1989             * Request to read a subgraph of the specified depth, rooted at a location that will be specified via <code>at(...)</code>
1990             * in the resulting {@link At} object.
1991             * <p>
1992             * Like all other methods on the {@link Batch}, the request will be performed when the {@link #execute()} method is
1993             * called.
1994             * </p>
1995             * 
1996             * @param depth the maximum depth of the subgraph that should be read
1997             * @return the component that should be used to specify the location of the node that is the top of the subgraph
1998             */
1999            public At<BatchConjunction> readSubgraphOfDepth( final int depth ) {
2000                assertNotExecuted();
2001                return new At<BatchConjunction>() {
2002                    public BatchConjunction at( Location location ) {
2003                        ReadBranchRequest request = new ReadBranchRequest(location, depth);
2004                        queue().submit(request);
2005                        return Batch.this.nextRequests;
2006                    }
2007    
2008                    public BatchConjunction at( String path ) {
2009                        return at(new Location(createPath(path)));
2010                    }
2011    
2012                    public BatchConjunction at( Path path ) {
2013                        return at(new Location(path));
2014                    }
2015    
2016                    public BatchConjunction at( UUID uuid ) {
2017                        return at(new Location(uuid));
2018                    }
2019    
2020                    public BatchConjunction at( Property idProperty ) {
2021                        return at(new Location(idProperty));
2022                    }
2023    
2024                    public BatchConjunction at( Property firstIdProperty,
2025                                                Property... additionalIdProperties ) {
2026                        return at(new Location(firstIdProperty, additionalIdProperties));
2027                    }
2028                };
2029            }
2030    
2031            public Results execute() {
2032                return this.requestQueue.execute();
2033            }
2034        }
2035    
2036        /**
2037         * A interface used to execute the accumulated {@link Batch requests}.
2038         * 
2039         * @author Randall Hauch
2040         */
2041        public interface Executable {
2042            /**
2043             * Stop accumulating the requests, submit them to the repository source, and return the results.
2044             * 
2045             * @return the results containing the requested information from the repository.
2046             */
2047            Results execute();
2048        }
2049    
2050        /**
2051         * A interface that can be used to finish the current request and start another.
2052         * 
2053         * @param <Next> the interface that will be used to start another request
2054         * @author Randall Hauch
2055         */
2056        public interface Conjunction<Next> {
2057            /**
2058             * Finish the request and prepare to start another.
2059             * 
2060             * @return the interface that can be used to start another request; never null
2061             */
2062            Next and();
2063        }
2064    
2065        /**
2066         * A component that defines the location into which a node should be copied or moved.
2067         * 
2068         * @param <Next> The interface that is to be returned when this request is completed
2069         * @author Randall Hauch
2070         */
2071        public interface Into<Next> {
2072            /**
2073             * Finish the request by specifying the new location into which the node should be copied/moved.
2074             * 
2075             * @param to the location of the new parent
2076             * @return the interface for additional requests or actions
2077             */
2078            Next into( Location to );
2079    
2080            /**
2081             * Finish the request by specifying the new location into which the node should be copied/moved.
2082             * 
2083             * @param toPath the path of the new parent
2084             * @return the interface for additional requests or actions
2085             */
2086            Next into( String toPath );
2087    
2088            /**
2089             * Finish the request by specifying the new location into which the node should be copied/moved.
2090             * 
2091             * @param to the path of the new parent
2092             * @return the interface for additional requests or actions
2093             */
2094            Next into( Path to );
2095    
2096            /**
2097             * Finish the request by specifying the new location into which the node should be copied/moved.
2098             * 
2099             * @param to the UUID of the new parent
2100             * @return the interface for additional requests or actions
2101             */
2102            Next into( UUID to );
2103    
2104            /**
2105             * Finish the request by specifying the new location into which the node should be copied/moved.
2106             * 
2107             * @param idProperty the property that uniquely identifies the new parent
2108             * @return the interface for additional requests or actions
2109             */
2110            Next into( Property idProperty );
2111    
2112            /**
2113             * Finish the request by specifying the new location into which the node should be copied/moved.
2114             * 
2115             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2116             *        new parent
2117             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2118             *        identifies the new parent
2119             * @return the interface for additional requests or actions
2120             */
2121            Next into( Property firstIdProperty,
2122                       Property... additionalIdProperties );
2123        }
2124    
2125        /**
2126         * A interface that is used to add more locations that are to be copied/moved.
2127         * 
2128         * @param <Next> The interface that is to be returned when this request is completed
2129         * @author Randall Hauch
2130         */
2131        public interface And<Next> {
2132            /**
2133             * Specify that another node should also be copied or moved.
2134             * 
2135             * @param from the location of the node to be copied or moved
2136             * @return the interface for finishing the request
2137             */
2138            Next and( Location from );
2139    
2140            /**
2141             * Specify that another node should also be copied or moved.
2142             * 
2143             * @param fromPath the path of the node to be copied or moved
2144             * @return the interface for finishing the request
2145             */
2146            Next and( String fromPath );
2147    
2148            /**
2149             * Specify that another node should also be copied or moved.
2150             * 
2151             * @param from the path of the node to be copied or moved
2152             * @return the interface for finishing the request
2153             */
2154            Next and( Path from );
2155    
2156            /**
2157             * Specify that another node should also be copied or moved.
2158             * 
2159             * @param from the UUID of the node to be copied or moved
2160             * @return the interface for finishing the request
2161             */
2162            Next and( UUID from );
2163    
2164            /**
2165             * Specify that another node should also be copied or moved.
2166             * 
2167             * @param idProperty the property that uniquely identifies the node to be copied or moved
2168             * @return the interface for finishing the request
2169             */
2170            Next and( Property idProperty );
2171    
2172            /**
2173             * Specify that another node should also be copied or moved.
2174             * 
2175             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2176             *        node to be copied or moved
2177             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2178             *        identifies the node to be copied or moved
2179             * @return the interface for finishing the request
2180             */
2181            Next and( Property firstIdProperty,
2182                      Property... additionalIdProperties );
2183        }
2184    
2185        /**
2186         * The interface for defining additional nodes to be moved and the parent into which the node(s) are to be moved.
2187         * 
2188         * @param <Next> The interface that is to be returned when this request is completed
2189         * @author Randall Hauch
2190         */
2191        public interface Move<Next> extends Into<Next>, And<Move<Next>> {
2192        }
2193    
2194        /**
2195         * The interface for defining additional nodes to be copied and the parent into which the node(s) are to be copied. where the
2196         * node(s) are to be moved.
2197         * 
2198         * @param <Next> The interface that is to be returned when this request is completed
2199         * @author Randall Hauch
2200         */
2201        public interface Copy<Next> extends Into<Next>, And<Copy<Next>> {
2202        }
2203    
2204        /**
2205         * The interface for defining additional properties on a new node.
2206         * 
2207         * @param <Next> The interface that is to be returned when this create request is completed
2208         * @author Randall Hauch
2209         */
2210        public interface Create<Next> extends Conjunction<Next>, Executable {
2211            /**
2212             * Specify the UUID that should the new node should have. This is an alias for {@link #and(UUID)}.
2213             * 
2214             * @param uuid the UUID
2215             * @return this same interface so additional properties may be added
2216             */
2217            Create<Next> with( UUID uuid );
2218    
2219            /**
2220             * Specify a property that should the new node should have. This is an alias for {@link #and(Property)}.
2221             * 
2222             * @param property the property
2223             * @return this same interface so additional properties may be added
2224             */
2225            Create<Next> with( Property property );
2226    
2227            /**
2228             * Specify a property that should the new node should have. This is an alias for {@link #and(String, Object...)}.
2229             * 
2230             * @param propertyName the name of the property
2231             * @param values the property values
2232             * @return this same interface so additional properties may be added
2233             */
2234            Create<Next> with( String propertyName,
2235                               Object... values );
2236    
2237            /**
2238             * Specify a property that should the new node should have. This is an alias for {@link #and(Name, Object...)}.
2239             * 
2240             * @param propertyName the name of the property
2241             * @param values the property values
2242             * @return this same interface so additional properties may be added
2243             */
2244            Create<Next> with( Name propertyName,
2245                               Object... values );
2246    
2247            /**
2248             * Specify properties that should the new node should have. This is an alias for {@link #and(Property, Property...)}.
2249             * 
2250             * @param firstProperty the first property
2251             * @param additionalProperties the additional property
2252             * @return this same interface so additional properties may be added
2253             */
2254            Create<Next> with( Property firstProperty,
2255                               Property... additionalProperties );
2256    
2257            /**
2258             * Specify the UUID that should the new node should have.
2259             * 
2260             * @param uuid the UUID
2261             * @return this same interface so additional properties may be added
2262             */
2263            Create<Next> and( UUID uuid );
2264    
2265            /**
2266             * Specify a property that should the new node should have.
2267             * 
2268             * @param property the property
2269             * @return this same interface so additional properties may be added
2270             */
2271            Create<Next> and( Property property );
2272    
2273            /**
2274             * Specify a property that should the new node should have.
2275             * 
2276             * @param propertyName the name of the property
2277             * @param values the property values
2278             * @return this same interface so additional properties may be added
2279             */
2280            Create<Next> and( String propertyName,
2281                              Object... values );
2282    
2283            /**
2284             * Specify a property that should the new node should have.
2285             * 
2286             * @param propertyName the name of the property
2287             * @param values the property values
2288             * @return this same interface so additional properties may be added
2289             */
2290            Create<Next> and( Name propertyName,
2291                              Object... values );
2292    
2293            /**
2294             * Specify properties that should the new node should have.
2295             * 
2296             * @param firstProperty the first property
2297             * @param additionalProperties the additional property
2298             * @return this same interface so additional properties may be added
2299             */
2300            Create<Next> and( Property firstProperty,
2301                              Property... additionalProperties );
2302        }
2303    
2304        /**
2305         * The interface for defining the node upon which a request operates.
2306         * 
2307         * @param <Next> The interface that is to be returned when the request is completed
2308         * @author Randall Hauch
2309         */
2310        public interface On<Next> {
2311            /**
2312             * Specify the location of the node upon which the request is to operate.
2313             * 
2314             * @param to the location of the new parent
2315             * @return the interface for additional requests or actions
2316             */
2317            Next on( Location to );
2318    
2319            /**
2320             * Specify the path of the node upon which the request is to operate.
2321             * 
2322             * @param toPath the path of the new parent
2323             * @return the interface for additional requests or actions
2324             */
2325            Next on( String toPath );
2326    
2327            /**
2328             * Specify the path of the node upon which the request is to operate.
2329             * 
2330             * @param to the path of the new parent
2331             * @return the interface for additional requests or actions
2332             */
2333            Next on( Path to );
2334    
2335            /**
2336             * Specify the UUID of the node upon which the request is to operate.
2337             * 
2338             * @param to the UUID of the new parent
2339             * @return the interface for additional requests or actions
2340             */
2341            Next on( UUID to );
2342    
2343            /**
2344             * Specify the unique identification property that identifies the node upon which the request is to operate.
2345             * 
2346             * @param idProperty the property that uniquely identifies the new parent
2347             * @return the interface for additional requests or actions
2348             */
2349            Next on( Property idProperty );
2350    
2351            /**
2352             * Specify the unique identification properties that identify the node upon which the request is to operate.
2353             * 
2354             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2355             *        new parent
2356             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2357             *        identifies the new parent
2358             * @return the interface for additional requests or actions
2359             */
2360            Next on( Property firstIdProperty,
2361                     Property... additionalIdProperties );
2362        }
2363    
2364        /**
2365         * The interface for defining the node upon which a request operates.
2366         * 
2367         * @param <Next> The interface that is to be returned when the request is completed
2368         * @author Randall Hauch
2369         */
2370        public interface Of<Next> {
2371            /**
2372             * Specify the location of the node upon which the request is to operate.
2373             * 
2374             * @param to the location of the new parent
2375             * @return the interface for additional requests or actions
2376             */
2377            Next of( Location to );
2378    
2379            /**
2380             * Specify the path of the node upon which the request is to operate.
2381             * 
2382             * @param toPath the path of the new parent
2383             * @return the interface for additional requests or actions
2384             */
2385            Next of( String toPath );
2386    
2387            /**
2388             * Specify the path of the node upon which the request is to operate.
2389             * 
2390             * @param to the path of the new parent
2391             * @return the interface for additional requests or actions
2392             */
2393            Next of( Path to );
2394    
2395            /**
2396             * Specify the UUID of the node upon which the request is to operate.
2397             * 
2398             * @param to the UUID of the new parent
2399             * @return the interface for additional requests or actions
2400             */
2401            Next of( UUID to );
2402    
2403            /**
2404             * Specify the unique identification property that identifies the node upon which the request is to operate.
2405             * 
2406             * @param idProperty the property that uniquely identifies the new parent
2407             * @return the interface for additional requests or actions
2408             */
2409            Next of( Property idProperty );
2410    
2411            /**
2412             * Specify the unique identification properties that identify the node upon which the request is to operate.
2413             * 
2414             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2415             *        new parent
2416             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2417             *        identifies the new parent
2418             * @return the interface for additional requests or actions
2419             */
2420            Next of( Property firstIdProperty,
2421                     Property... additionalIdProperties );
2422        }
2423    
2424        /**
2425         * The interface for defining the node upon which which a request operates.
2426         * 
2427         * @param <Next> The interface that is to be returned when the request is completed
2428         * @author Randall Hauch
2429         */
2430        public interface At<Next> {
2431            /**
2432             * Specify the location of the node upon which the request is to operate.
2433             * 
2434             * @param to the location of the new parent
2435             * @return the interface for additional requests or actions
2436             */
2437            Next at( Location to );
2438    
2439            /**
2440             * Specify the path of the node upon which the request is to operate.
2441             * 
2442             * @param toPath the path of the new parent
2443             * @return the interface for additional requests or actions
2444             */
2445            Next at( String toPath );
2446    
2447            /**
2448             * Specify the path of the node upon which the request is to operate.
2449             * 
2450             * @param to the path of the new parent
2451             * @return the interface for additional requests or actions
2452             */
2453            Next at( Path to );
2454    
2455            /**
2456             * Specify the UUID of the node upon which the request is to operate.
2457             * 
2458             * @param to the UUID of the new parent
2459             * @return the interface for additional requests or actions
2460             */
2461            Next at( UUID to );
2462    
2463            /**
2464             * Specify the unique identification property that identifies the node upon which the request is to operate.
2465             * 
2466             * @param idProperty the property that uniquely identifies the new parent
2467             * @return the interface for additional requests or actions
2468             */
2469            Next at( Property idProperty );
2470    
2471            /**
2472             * Specify the unique identification properties that identify the node upon which the request is to operate.
2473             * 
2474             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2475             *        new parent
2476             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2477             *        identifies the new parent
2478             * @return the interface for additional requests or actions
2479             */
2480            Next at( Property firstIdProperty,
2481                     Property... additionalIdProperties );
2482        }
2483    
2484        /**
2485         * A component that defines the location into which a node should be copied or moved.
2486         * 
2487         * @param <Next> The interface that is to be returned when this request is completed
2488         * @author Randall Hauch
2489         */
2490        public interface ImportInto<Next> {
2491            /**
2492             * Specify whether the root element in the XML document should be skipped (that is, not be represented by a node). By
2493             * default, the root element is not skipped.
2494             * 
2495             * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
2496             * @return the interface used to specify the location where the content should be placed
2497             */
2498            ImportInto<Next> skippingRootElement( boolean skip );
2499    
2500            /**
2501             * Finish the import by specifying the new location into which the node should be copied/moved.
2502             * 
2503             * @param to the location of the new parent
2504             * @return the interface for additional requests or actions
2505             * @throws IOException if there is a problem reading the content being imported
2506             * @throws SAXException if there is a problem with the SAX Parser
2507             */
2508            Next into( Location to ) throws IOException, SAXException;
2509    
2510            /**
2511             * Finish the import by specifying the new location into which the node should be copied/moved.
2512             * 
2513             * @param toPath the path of the new parent
2514             * @return the interface for additional requests or actions
2515             * @throws IOException if there is a problem reading the content being imported
2516             * @throws SAXException if there is a problem with the SAX Parser
2517             */
2518            Next into( String toPath ) throws IOException, SAXException;
2519    
2520            /**
2521             * Finish the import by specifying the new location into which the node should be copied/moved.
2522             * 
2523             * @param to the path of the new parent
2524             * @return the interface for additional requests or actions
2525             * @throws IOException if there is a problem reading the content being imported
2526             * @throws SAXException if there is a problem with the SAX Parser
2527             */
2528            Next into( Path to ) throws IOException, SAXException;
2529    
2530            /**
2531             * Finish the import by specifying the new location into which the node should be copied/moved.
2532             * 
2533             * @param to the UUID of the new parent
2534             * @return the interface for additional requests or actions
2535             * @throws IOException if there is a problem reading the content being imported
2536             * @throws SAXException if there is a problem with the SAX Parser
2537             */
2538            Next into( UUID to ) throws IOException, SAXException;
2539    
2540            /**
2541             * Finish the import by specifying the new location into which the node should be copied/moved.
2542             * 
2543             * @param idProperty the property that uniquely identifies the new parent
2544             * @return the interface for additional requests or actions
2545             * @throws IOException if there is a problem reading the content being imported
2546             * @throws SAXException if there is a problem with the SAX Parser
2547             */
2548            Next into( Property idProperty ) throws IOException, SAXException;
2549    
2550            /**
2551             * Finish the import by specifying the new location into which the node should be copied/moved.
2552             * 
2553             * @param firstIdProperty the first property that, with the <code>additionalIdProperties</code>, uniquely identifies the
2554             *        new parent
2555             * @param additionalIdProperties the additional properties that, with the <code>additionalIdProperties</code>, uniquely
2556             *        identifies the new parent
2557             * @return the interface for additional requests or actions
2558             * @throws IOException if there is a problem reading the content being imported
2559             * @throws SAXException if there is a problem with the SAX Parser
2560             */
2561            Next into( Property firstIdProperty,
2562                       Property... additionalIdProperties ) throws IOException, SAXException;
2563        }
2564    
2565        public interface BatchConjunction extends Conjunction<Batch>, Executable {
2566        }
2567    
2568        // ----------------------------------------------------------------------------------------------------------------
2569        // RequestQueue and the different implementations
2570        // ----------------------------------------------------------------------------------------------------------------
2571    
2572        /**
2573         * A queue to which each each {@link AbstractAction} can submit its {@link Request} objects, either in single or multiple.
2574         * This interface abstracts away from the {@link AbstractAction} what it is to do with its {@link Request} objects, allowing
2575         * the same <code>AbstractAction</code> classes to be used by the {@link Graph} and {@link Graph.Batch} components.
2576         * 
2577         * @author Randall Hauch
2578         */
2579        /*package*/interface RequestQueue extends Executable {
2580            Graph getGraph();
2581    
2582            void submit( Request request );
2583    
2584            void submit( List<Request> requests );
2585        }
2586    
2587        /**
2588         * A RequestQueue that is used by the Graph instance to immediately execute the submitted requests.
2589         * 
2590         * @author Randall Hauch
2591         */
2592        @NotThreadSafe
2593        /*package*/class GraphRequestQueue implements RequestQueue {
2594            public Graph getGraph() {
2595                return Graph.this;
2596            }
2597    
2598            public void submit( Request request ) {
2599                // Execute the request immediately ...
2600                Graph.this.execute(request);
2601            }
2602    
2603            public void submit( List<Request> requests ) {
2604                Request request = CompositeRequest.with(requests);
2605                // Execute the request immediately ...
2606                Graph.this.execute(request);
2607            }
2608    
2609            public Results execute() {
2610                throw new UnsupportedOperationException();
2611            }
2612        }
2613    
2614        /**
2615         * A RequestQueue that is used by the {@link Graph.Batch} component to enqueue {@link Request}s until they are to be submitted
2616         * to the repository connections.
2617         * 
2618         * @author Randall Hauch
2619         */
2620        @NotThreadSafe
2621        /*package*/class CompositingRequestQueue implements RequestQueue {
2622            private final List<Request> requests = new LinkedList<Request>();
2623    
2624            public Graph getGraph() {
2625                return Graph.this;
2626            }
2627    
2628            public List<Request> getRequests() {
2629                return this.requests;
2630            }
2631    
2632            public void submit( Request request ) {
2633                this.requests.add(request);
2634            }
2635    
2636            public void submit( List<Request> requests ) {
2637                this.requests.addAll(requests);
2638            }
2639    
2640            public Results execute() {
2641                if (!requests.isEmpty()) {
2642                    // Execute the requests ...
2643                    Request request = CompositeRequest.with(requests);
2644                    Graph.this.execute(request);
2645                }
2646                return new BatchResults(requests);
2647            }
2648        }
2649    
2650        // ----------------------------------------------------------------------------------------------------------------
2651        // Node Implementation
2652        // ----------------------------------------------------------------------------------------------------------------
2653        @Immutable
2654        protected class GraphNode implements Node {
2655            private final ReadNodeRequest request;
2656    
2657            /*package*/GraphNode( ReadNodeRequest request ) {
2658                this.request = request;
2659            }
2660    
2661            public Location getLocation() {
2662                return request.getActualLocationOfNode();
2663            }
2664    
2665            public Graph getGraph() {
2666                return Graph.this;
2667            }
2668    
2669            public Collection<Property> getProperties() {
2670                return request.getProperties();
2671            }
2672    
2673            public Property getProperty( Name name ) {
2674                return getPropertiesByName().get(name);
2675            }
2676    
2677            public Property getProperty( String nameStr ) {
2678                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
2679                return getPropertiesByName().get(name);
2680            }
2681    
2682            public Map<Name, Property> getPropertiesByName() {
2683                return request.getPropertiesByName();
2684            }
2685    
2686            public List<Location> getChildren() {
2687                return request.getChildren();
2688            }
2689    
2690            public boolean hasChildren() {
2691                return request.getChildren().size() > 0;
2692            }
2693    
2694            public List<Segment> getChildrenSegments() {
2695                return getSegments(getChildren());
2696            }
2697    
2698            public Iterator<Location> iterator() {
2699                return request.getChildren().iterator();
2700            }
2701    
2702            @Override
2703            public int hashCode() {
2704                return getLocation().hashCode();
2705            }
2706    
2707            @Override
2708            public boolean equals( Object obj ) {
2709                if (obj instanceof Node) {
2710                    Node that = (Node)obj;
2711                    return this.getLocation().equals(that.getLocation());
2712                }
2713                return false;
2714            }
2715    
2716            @Override
2717            public String toString() {
2718                return "Node " + getLocation().toString();
2719            }
2720        }
2721    
2722        // ----------------------------------------------------------------------------------------------------------------
2723        // Results implementation for the batched requests
2724        // ----------------------------------------------------------------------------------------------------------------
2725        @Immutable
2726        class BatchResults implements Results {
2727            private final Map<Path, BatchResultsNode> nodes = new HashMap<Path, BatchResultsNode>();
2728    
2729            /*package*/BatchResults( List<Request> requests ) {
2730                for (Request request : requests) {
2731                    if (request instanceof ReadAllPropertiesRequest) {
2732                        ReadAllPropertiesRequest read = (ReadAllPropertiesRequest)request;
2733                        getOrCreateNode(read.getActualLocationOfNode()).setProperties(read.getPropertiesByName());
2734                    } else if (request instanceof ReadPropertyRequest) {
2735                        ReadPropertyRequest read = (ReadPropertyRequest)request;
2736                        getOrCreateNode(read.getActualLocationOfNode()).addProperty(read.getProperty());
2737                    } else if (request instanceof ReadNodeRequest) {
2738                        ReadNodeRequest read = (ReadNodeRequest)request;
2739                        BatchResultsNode node = getOrCreateNode(read.getActualLocationOfNode());
2740                        node.setProperties(read.getPropertiesByName());
2741                        node.setChildren(read.getChildren());
2742                    } else if (request instanceof ReadBlockOfChildrenRequest) {
2743                        throw new IllegalStateException();
2744                    } else if (request instanceof ReadAllChildrenRequest) {
2745                        ReadAllChildrenRequest read = (ReadAllChildrenRequest)request;
2746                        getOrCreateNode(read.getActualLocationOfNode()).setChildren(read.getChildren());
2747                    } else if (request instanceof ReadBranchRequest) {
2748                        ReadBranchRequest read = (ReadBranchRequest)request;
2749                        for (Location location : read) {
2750                            BatchResultsNode node = getOrCreateNode(location);
2751                            node.setProperties(read.getPropertiesFor(location));
2752                            node.setChildren(read.getChildren(location));
2753                        }
2754                    }
2755                }
2756                for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
2757                    entry.getValue().freeze();
2758                }
2759            }
2760    
2761            private BatchResultsNode getOrCreateNode( Location location ) {
2762                BatchResultsNode node = nodes.get(location);
2763                if (node == null) {
2764                    node = new BatchResultsNode(location);
2765                    assert location.getPath() != null;
2766                    nodes.put(location.getPath(), node);
2767                }
2768                return node;
2769            }
2770    
2771            public Graph getGraph() {
2772                return Graph.this;
2773            }
2774    
2775            protected void checkIsAbsolute( Path path ) {
2776                if (!path.isAbsolute()) {
2777                    throw new IllegalArgumentException(GraphI18n.pathIsNotAbsolute.text(path));
2778                }
2779            }
2780    
2781            public Node getNode( String pathStr ) {
2782                Path path = createPath(pathStr);
2783                checkIsAbsolute(path);
2784                return nodes.get(path);
2785            }
2786    
2787            public Node getNode( Path path ) {
2788                CheckArg.isNotNull(path, "path");
2789                checkIsAbsolute(path);
2790                return nodes.get(path);
2791            }
2792    
2793            public Node getNode( Location location ) {
2794                CheckArg.isNotNull(location, "location");
2795                CheckArg.isNotNull(location.getPath(), "location.getPath()");
2796                return nodes.get(location.getPath());
2797            }
2798    
2799            public boolean includes( String path ) {
2800                return getNode(path) != null;
2801            }
2802    
2803            public boolean includes( Path path ) {
2804                return getNode(path) != null;
2805            }
2806    
2807            public boolean includes( Location location ) {
2808                return getNode(location) != null;
2809            }
2810    
2811            public Iterator<Node> iterator() {
2812                List<Path> paths = new ArrayList<Path>(nodes.keySet());
2813                Collections.sort(paths);
2814                final Iterator<Path> pathIter = paths.iterator();
2815                return new Iterator<Node>() {
2816                    public boolean hasNext() {
2817                        return pathIter.hasNext();
2818                    }
2819    
2820                    public Node next() {
2821                        Path nextPath = pathIter.next();
2822                        return getNode(nextPath);
2823                    }
2824    
2825                    public void remove() {
2826                        throw new UnsupportedOperationException();
2827                    }
2828                };
2829            }
2830        }
2831    
2832        @Immutable
2833        class BatchResultsNode implements Node {
2834            private final Location location;
2835            private Map<Name, Property> properties;
2836            private List<Location> children;
2837    
2838            BatchResultsNode( Location location ) {
2839                this.location = location;
2840            }
2841    
2842            void addProperty( Property property ) {
2843                if (this.properties == null) this.properties = new HashMap<Name, Property>();
2844                this.properties.put(property.getName(), property);
2845            }
2846    
2847            void setProperties( Map<Name, Property> properties ) {
2848                this.properties = properties;
2849            }
2850    
2851            void setChildren( List<Location> children ) {
2852                this.children = children;
2853            }
2854    
2855            void freeze() {
2856                if (properties != null) properties = Collections.unmodifiableMap(properties);
2857                else properties = Collections.emptyMap();
2858                if (children != null) children = Collections.unmodifiableList(children);
2859                else children = Collections.emptyList();
2860            }
2861    
2862            public List<Segment> getChildrenSegments() {
2863                return getSegments(getChildren());
2864            }
2865    
2866            public Graph getGraph() {
2867                return Graph.this;
2868            }
2869    
2870            public Location getLocation() {
2871                return location;
2872            }
2873    
2874            public Collection<Property> getProperties() {
2875                return properties.values();
2876            }
2877    
2878            public Map<Name, Property> getPropertiesByName() {
2879                return properties;
2880            }
2881    
2882            public Property getProperty( Name name ) {
2883                return properties.get(name);
2884            }
2885    
2886            public Property getProperty( String nameStr ) {
2887                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
2888                return properties.get(name);
2889            }
2890    
2891            public List<Location> getChildren() {
2892                return children;
2893            }
2894    
2895            public boolean hasChildren() {
2896                return children.size() != 0;
2897            }
2898    
2899            public Iterator<Location> iterator() {
2900                return children.iterator();
2901            }
2902    
2903            @Override
2904            public int hashCode() {
2905                return location.hashCode();
2906            }
2907    
2908            @Override
2909            public boolean equals( Object obj ) {
2910                if (obj instanceof Node) {
2911                    Node that = (Node)obj;
2912                    return this.location.equals(that.getLocation());
2913                }
2914                return false;
2915            }
2916    
2917            @Override
2918            public String toString() {
2919                return "Node " + getLocation().toString();
2920            }
2921    
2922        }
2923    
2924        // ----------------------------------------------------------------------------------------------------------------
2925        // Subgraph and SubgraphNode implementations
2926        // ----------------------------------------------------------------------------------------------------------------
2927        @Immutable
2928        class SubgraphResults implements Subgraph {
2929            private final ReadBranchRequest request;
2930    
2931            SubgraphResults( ReadBranchRequest request ) {
2932                this.request = request;
2933            }
2934    
2935            public Graph getGraph() {
2936                return Graph.this;
2937            }
2938    
2939            public Location getLocation() {
2940                return request.getActualLocationOfNode();
2941            }
2942    
2943            public Node getRoot() {
2944                return getNode(getLocation());
2945            }
2946    
2947            public int getMaximumDepth() {
2948                return request.maximumDepth();
2949            }
2950    
2951            public Iterator<Node> iterator() {
2952                final Iterator<Location> iter = request.iterator();
2953                return new Iterator<Node>() {
2954                    public boolean hasNext() {
2955                        return iter.hasNext();
2956                    }
2957    
2958                    public Node next() {
2959                        return getNode(iter.next());
2960                    }
2961    
2962                    public void remove() {
2963                        throw new UnsupportedOperationException();
2964                    }
2965                };
2966            }
2967    
2968            public boolean includes( Path path ) {
2969                CheckArg.isNotNull(path, "path");
2970                path = getAbsolutePath(path);
2971                return request.includes(path);
2972            }
2973    
2974            public boolean includes( Location location ) {
2975                CheckArg.isNotNull(location, "location");
2976                return request.includes(location);
2977            }
2978    
2979            public boolean includes( String pathStr ) {
2980                Path path = createPath(pathStr);
2981                path = getAbsolutePath(path);
2982                return includes(path);
2983            }
2984    
2985            public Node getNode( Location location ) {
2986                if (!location.hasPath()) return null;
2987                Location actualLocation = request.getLocationFor(location.getPath());
2988                if (actualLocation == null) return null;
2989                return new SubgraphNode(actualLocation, request);
2990            }
2991    
2992            public Node getNode( Path path ) {
2993                path = getAbsolutePath(path);
2994                if (!includes(path)) return null;
2995                Location location = request.getLocationFor(path);
2996                if (location == null) return null;
2997                return new SubgraphNode(location, request);
2998            }
2999    
3000            public Node getNode( String pathStr ) {
3001                CheckArg.isNotEmpty(pathStr, "path");
3002                Path path = createPath(pathStr);
3003                path = getAbsolutePath(path);
3004                return getNode(path);
3005            }
3006    
3007            protected Path getAbsolutePath( Path absoluteOrRelative ) {
3008                Path result = absoluteOrRelative;
3009                if (!result.isAbsolute()) {
3010                    result = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), result);
3011                    result = result.getNormalizedPath();
3012                }
3013                return result;
3014            }
3015    
3016            @Override
3017            public int hashCode() {
3018                return getLocation().hashCode();
3019            }
3020    
3021            @Override
3022            public String toString() {
3023                return "Subgraph " + getLocation().toString();
3024            }
3025        }
3026    
3027        @Immutable
3028        class SubgraphNode implements Node {
3029            private final Location location;
3030            private final ReadBranchRequest request;
3031    
3032            SubgraphNode( Location location,
3033                          ReadBranchRequest request ) {
3034                this.location = location;
3035                this.request = request;
3036            }
3037    
3038            public List<Location> getChildren() {
3039                return request.getChildren(location);
3040            }
3041    
3042            public Graph getGraph() {
3043                return Graph.this;
3044            }
3045    
3046            public Location getLocation() {
3047                return location;
3048            }
3049    
3050            public Collection<Property> getProperties() {
3051                return getPropertiesByName().values();
3052            }
3053    
3054            public Map<Name, Property> getPropertiesByName() {
3055                return request.getPropertiesFor(location);
3056            }
3057    
3058            public Property getProperty( Name name ) {
3059                return getPropertiesByName().get(name);
3060            }
3061    
3062            public Property getProperty( String nameStr ) {
3063                Name name = getContext().getValueFactories().getNameFactory().create(nameStr);
3064                return getPropertiesByName().get(name);
3065            }
3066    
3067            public boolean hasChildren() {
3068                return getChildren().size() != 0;
3069            }
3070    
3071            public List<Segment> getChildrenSegments() {
3072                return getSegments(getChildren());
3073            }
3074    
3075            public Iterator<Location> iterator() {
3076                return getChildren().iterator();
3077            }
3078    
3079            @Override
3080            public int hashCode() {
3081                return location.hashCode();
3082            }
3083    
3084            @Override
3085            public boolean equals( Object obj ) {
3086                if (obj instanceof Node) {
3087                    Node that = (Node)obj;
3088                    return this.location.equals(that.getLocation());
3089                }
3090                return false;
3091            }
3092    
3093            @Override
3094            public String toString() {
3095                return "Node " + getLocation().toString();
3096            }
3097        }
3098    
3099        // ----------------------------------------------------------------------------------------------------------------
3100        // Action Implementations
3101        // ----------------------------------------------------------------------------------------------------------------
3102        @Immutable
3103        static abstract class AbstractAction<T> implements Conjunction<T>, Executable {
3104            private final RequestQueue queue;
3105            private final T afterConjunction;
3106    
3107            /*package*/AbstractAction( T afterConjunction,
3108                                        RequestQueue queue ) {
3109                this.queue = queue;
3110                this.afterConjunction = afterConjunction;
3111            }
3112    
3113            /*package*/RequestQueue queue() {
3114                return this.queue;
3115            }
3116    
3117            public T and() {
3118                return this.afterConjunction;
3119            }
3120    
3121            /*package*/Path createPath( String path ) {
3122                return queue.getGraph().getContext().getValueFactories().getPathFactory().create(path);
3123            }
3124    
3125            public Results execute() {
3126                return queue.execute();
3127            }
3128        }
3129    
3130        @NotThreadSafe
3131        static class MoveAction<T> extends AbstractAction<T> implements Move<T> {
3132            private final Locations from;
3133    
3134            /*package*/MoveAction( T afterConjunction,
3135                                    RequestQueue queue,
3136                                    Location from ) {
3137                super(afterConjunction, queue);
3138                this.from = new Locations(from);
3139            }
3140    
3141            public Move<T> and( Location from ) {
3142                this.from.add(from);
3143                return this;
3144            }
3145    
3146            public Move<T> and( String from ) {
3147                this.from.add(new Location(createPath(from)));
3148                return this;
3149            }
3150    
3151            public Move<T> and( Path from ) {
3152                this.from.add(new Location(from));
3153                return this;
3154            }
3155    
3156            public Move<T> and( Property firstFrom,
3157                                Property... additionalFroms ) {
3158                this.from.add(new Location(firstFrom, additionalFroms));
3159                return this;
3160            }
3161    
3162            public Move<T> and( Property from ) {
3163                this.from.add(new Location(from));
3164                return this;
3165            }
3166    
3167            public Move<T> and( UUID from ) {
3168                this.from.add(new Location(from));
3169                return this;
3170            }
3171    
3172            /**
3173             * Submit any requests to move the targets into the supplied parent location
3174             * 
3175             * @param into the parent location
3176             * @return this object, for method chaining
3177             */
3178            private T submit( Location into ) {
3179                if (this.from.hasNext()) {
3180                    List<Request> requests = new LinkedList<Request>();
3181                    Locations locations = this.from;
3182                    while (locations.hasNext()) {
3183                        Location location = locations.getLocation();
3184                        requests.add(new MoveBranchRequest(location, into));
3185                        locations = locations.next();
3186                    }
3187                    queue().submit(requests);
3188                } else {
3189                    queue().submit(new MoveBranchRequest(this.from.getLocation(), into));
3190                }
3191                return and();
3192            }
3193    
3194            public T into( Location into ) {
3195                return submit(into);
3196            }
3197    
3198            public T into( Path into ) {
3199                return submit(new Location(into));
3200            }
3201    
3202            public T into( UUID into ) {
3203                return submit(new Location(into));
3204            }
3205    
3206            public T into( Property firstIdProperty,
3207                           Property... additionalIdProperties ) {
3208                return submit(new Location(firstIdProperty, additionalIdProperties));
3209            }
3210    
3211            public T into( Property into ) {
3212                return submit(new Location(into));
3213            }
3214    
3215            public T into( String into ) {
3216                return submit(new Location(createPath(into)));
3217            }
3218    
3219            @Override
3220            public Results execute() {
3221                return queue().execute();
3222            }
3223        }
3224    
3225        @NotThreadSafe
3226        static class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
3227            private final Locations from;
3228    
3229            /*package*/CopyAction( T afterConjunction,
3230                                    RequestQueue queue,
3231                                    Location from ) {
3232                super(afterConjunction, queue);
3233                this.from = new Locations(from);
3234            }
3235    
3236            public Copy<T> and( Location from ) {
3237                this.from.add(from);
3238                return this;
3239            }
3240    
3241            public Copy<T> and( String from ) {
3242                this.from.add(new Location(createPath(from)));
3243                return this;
3244            }
3245    
3246            public Copy<T> and( Path from ) {
3247                this.from.add(new Location(from));
3248                return this;
3249            }
3250    
3251            public Copy<T> and( Property firstFrom,
3252                                Property... additionalFroms ) {
3253                this.from.add(new Location(firstFrom, additionalFroms));
3254                return this;
3255            }
3256    
3257            public Copy<T> and( Property from ) {
3258                this.from.add(new Location(from));
3259                return this;
3260            }
3261    
3262            public Copy<T> and( UUID from ) {
3263                this.from.add(new Location(from));
3264                return this;
3265            }
3266    
3267            /**
3268             * Submit any requests to move the targets into the supplied parent location
3269             * 
3270             * @param into the parent location
3271             * @return this object, for method chaining
3272             */
3273            private T submit( Location into ) {
3274                if (this.from.hasNext()) {
3275                    List<Request> requests = new LinkedList<Request>();
3276                    Locations locations = this.from;
3277                    while (locations.hasNext()) {
3278                        Location location = locations.getLocation();
3279                        requests.add(new CopyBranchRequest(location, into));
3280                        locations = locations.next();
3281                    }
3282                    queue().submit(requests);
3283                } else {
3284                    queue().submit(new CopyBranchRequest(this.from.getLocation(), into));
3285                }
3286                return and();
3287            }
3288    
3289            public T into( Location into ) {
3290                return submit(into);
3291            }
3292    
3293            public T into( Path into ) {
3294                return submit(new Location(into));
3295            }
3296    
3297            public T into( UUID into ) {
3298                return submit(new Location(into));
3299            }
3300    
3301            public T into( Property firstIdProperty,
3302                           Property... additionalIdProperties ) {
3303                return submit(new Location(firstIdProperty, additionalIdProperties));
3304            }
3305    
3306            public T into( Property into ) {
3307                return submit(new Location(into));
3308            }
3309    
3310            public T into( String into ) {
3311                return submit(new Location(createPath(into)));
3312            }
3313    
3314            @Override
3315            public Results execute() {
3316                return queue().execute();
3317            }
3318        }
3319    
3320        @NotThreadSafe
3321        static class CreateAction<T> extends AbstractAction<T> implements Create<T> {
3322            private final Location at;
3323            private final List<Property> properties = new LinkedList<Property>();
3324    
3325            /*package*/CreateAction( T afterConjunction,
3326                                      RequestQueue queue,
3327                                      Location at ) {
3328                super(afterConjunction, queue);
3329                this.at = at;
3330            }
3331    
3332            public Create<T> and( UUID uuid ) {
3333                PropertyFactory factory = queue().getGraph().getContext().getPropertyFactory();
3334                properties.add(factory.create(DnaLexicon.UUID, uuid));
3335                return this;
3336            }
3337    
3338            public Create<T> and( Property property ) {
3339                properties.add(property);
3340                return this;
3341            }
3342    
3343            public Create<T> and( String name,
3344                                  Object... values ) {
3345                ExecutionContext context = queue().getGraph().getContext();
3346                PropertyFactory factory = context.getPropertyFactory();
3347                NameFactory nameFactory = context.getValueFactories().getNameFactory();
3348                properties.add(factory.create(nameFactory.create(name), values));
3349                return this;
3350            }
3351    
3352            public Create<T> and( Name name,
3353                                  Object... values ) {
3354                ExecutionContext context = queue().getGraph().getContext();
3355                PropertyFactory factory = context.getPropertyFactory();
3356                properties.add(factory.create(name, values));
3357                return this;
3358            }
3359    
3360            public Create<T> and( Property property,
3361                                  Property... additionalProperties ) {
3362                properties.add(property);
3363                for (Property additionalProperty : additionalProperties) {
3364                    properties.add(additionalProperty);
3365                }
3366                return this;
3367            }
3368    
3369            public Create<T> with( UUID uuid ) {
3370                return and(uuid);
3371            }
3372    
3373            public Create<T> with( Property property ) {
3374                return and(property);
3375            }
3376    
3377            public Create<T> with( Property property,
3378                                   Property... additionalProperties ) {
3379                return and(property, additionalProperties);
3380            }
3381    
3382            public Create<T> with( String name,
3383                                   Object... values ) {
3384                return and(name, values);
3385            }
3386    
3387            public Create<T> with( Name name,
3388                                   Object... values ) {
3389                return and(name, values);
3390            }
3391    
3392            @Override
3393            public T and() {
3394                this.queue().submit(new CreateNodeRequest(this.at, this.properties));
3395                return super.and();
3396            }
3397    
3398            @Override
3399            public Results execute() {
3400                return queue().execute();
3401            }
3402        }
3403    
3404    }