001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors.
008     *
009     * Unless otherwise indicated, all code in JBoss DNA is licensed
010     * to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     * 
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.graph.request;
025    
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.Map;
029    import org.jboss.dna.graph.Location;
030    import org.jboss.dna.graph.NodeConflictBehavior;
031    import org.jboss.dna.graph.property.Name;
032    import org.jboss.dna.graph.property.Path;
033    import org.jboss.dna.graph.property.Property;
034    import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
035    import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
036    
037    /**
038     * A component that can be used to build requests while allowing different strategies for how requests are handled. Subclasses can
039     * simply override the {@link #process(Request)} method to define what happens with each request.
040     */
041    public abstract class RequestBuilder {
042    
043        /**
044         * Create a new builder.
045         */
046        protected RequestBuilder() {
047        }
048    
049        protected abstract <T extends Request> T process( T request );
050    
051        /**
052         * Add a request to obtain the information about the available workspaces.
053         * 
054         * @return the request; never null
055         */
056        public GetWorkspacesRequest getWorkspaces() {
057            GetWorkspacesRequest request = new GetWorkspacesRequest();
058            process(request);
059            return request;
060        }
061    
062        /**
063         * Add a request to verify the existance of the named workspace.
064         * 
065         * @param workspaceName the desired name of the workspace, or null if the source's default workspace should be used
066         * @return the request; never null
067         */
068        public VerifyWorkspaceRequest verifyWorkspace( String workspaceName ) {
069            return process(new VerifyWorkspaceRequest(workspaceName));
070        }
071    
072        /**
073         * Add a request to create a new workspace, and specify the behavior should a workspace already exists with a name that
074         * matches the desired name for the new workspace.
075         * 
076         * @param desiredNameOfNewWorkspace the desired name of the new workspace
077         * @param createConflictBehavior the behavior if a workspace already exists with the same name, or null if the default
078         *        behavior should be used
079         * @return the request; never null
080         */
081        public CreateWorkspaceRequest createWorkspace( String desiredNameOfNewWorkspace,
082                                                       CreateConflictBehavior createConflictBehavior ) {
083            return process(new CreateWorkspaceRequest(desiredNameOfNewWorkspace, createConflictBehavior));
084        }
085    
086        /**
087         * Add a request to clone an existing workspace to create a new workspace, and specify the behavior should a workspace already
088         * exists with a name that matches the desired name for the new workspace.
089         * 
090         * @param nameOfWorkspaceToBeCloned the name of the existing workspace that is to be cloned
091         * @param desiredNameOfTargetWorkspace the desired name of the target workspace
092         * @param createConflictBehavior the behavior if a workspace already exists with the same name
093         * @param cloneConflictBehavior the behavior if the workspace to be cloned does not exist
094         * @return the request; never null
095         * @throws IllegalArgumentException if the either workspace name is null
096         */
097        public CloneWorkspaceRequest cloneWorkspace( String nameOfWorkspaceToBeCloned,
098                                                     String desiredNameOfTargetWorkspace,
099                                                     CreateConflictBehavior createConflictBehavior,
100                                                     CloneConflictBehavior cloneConflictBehavior ) {
101            return process(new CloneWorkspaceRequest(nameOfWorkspaceToBeCloned, desiredNameOfTargetWorkspace, createConflictBehavior,
102                                                     cloneConflictBehavior));
103        }
104    
105        /**
106         * Add a request to destroy an existing workspace.
107         * 
108         * @param workspaceName the name of the workspace that is to be destroyed
109         * @return the request; never null
110         * @throws IllegalArgumentException if the workspace name is null
111         */
112        public DestroyWorkspaceRequest destroyWorkspace( String workspaceName ) {
113            return process(new DestroyWorkspaceRequest(workspaceName));
114        }
115    
116        /**
117         * Add a request to verify the existance and location of a node at the supplied location.
118         * 
119         * @param at the location of the node to be verified
120         * @param workspaceName the name of the workspace containing the node
121         * @return the request; never null
122         * @throws IllegalArgumentException if the location or workspace name is null
123         */
124        public VerifyNodeExistsRequest verifyNodeExists( Location at,
125                                                         String workspaceName ) {
126            return process(new VerifyNodeExistsRequest(at, workspaceName));
127        }
128    
129        /**
130         * Add a request to read the properties and number of children of a node at the supplied location.
131         * 
132         * @param at the location of the node to be read
133         * @param workspaceName the name of the workspace containing the node
134         * @return the request; never null
135         * @throws IllegalArgumentException if the location or workspace name is null
136         */
137        public ReadNodeRequest readNode( Location at,
138                                         String workspaceName ) {
139            return process(new ReadNodeRequest(at, workspaceName));
140        }
141    
142        /**
143         * Add a request to read the children of a node at the supplied location in the designated workspace.
144         * 
145         * @param of the location of the node whose children are to be read
146         * @param workspaceName the name of the workspace
147         * @return the request; never null
148         * @throws IllegalArgumentException if the location or workspace name is null
149         */
150        public ReadAllChildrenRequest readAllChildren( Location of,
151                                                       String workspaceName ) {
152            return process(new ReadAllChildrenRequest(of, workspaceName));
153        }
154    
155        /**
156         * Add a request to read the properties and number of children of a node at the supplied location.
157         * 
158         * @param of the location of the node whose children are to be read
159         * @param workspaceName the name of the workspace
160         * @return the request; never null
161         * @throws IllegalArgumentException if the location or workspace name is null
162         */
163        public ReadAllPropertiesRequest readAllProperties( Location of,
164                                                           String workspaceName ) {
165            return process(new ReadAllPropertiesRequest(of, workspaceName));
166        }
167    
168        /**
169         * Add a request to read the properties and number of children of a node at the supplied location.
170         * 
171         * @param of the location of the node whose children are to be read
172         * @param workspaceName the name of the workspace
173         * @param propertyName the name of the property to read
174         * @return the request; never null
175         * @throws IllegalArgumentException if the location or workspace name is null
176         */
177        public ReadPropertyRequest readProperty( Location of,
178                                                 String workspaceName,
179                                                 Name propertyName ) {
180            return process(new ReadPropertyRequest(of, workspaceName, propertyName));
181        }
182    
183        /**
184         * Add a request to read the branch at the supplied location, to a maximum depth of 2.
185         * 
186         * @param at the location of the branch
187         * @param workspaceName the name of the workspace containing the branch
188         * @return the request; never null
189         * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
190         */
191        public ReadBranchRequest readBranch( Location at,
192                                             String workspaceName ) {
193            return process(new ReadBranchRequest(at, workspaceName));
194        }
195    
196        /**
197         * Add a request to read the branch (of given depth) at the supplied location.
198         * 
199         * @param at the location of the branch
200         * @param workspaceName the name of the workspace containing the branch
201         * @param maxDepth the maximum depth to read
202         * @return the request; never null
203         * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
204         */
205        public ReadBranchRequest readBranch( Location at,
206                                             String workspaceName,
207                                             int maxDepth ) {
208            return process(new ReadBranchRequest(at, workspaceName, maxDepth));
209        }
210    
211        /**
212         * Add a request to read a block of the children of a node at the supplied location. The block is defined by the starting
213         * index of the first child and the number of children to include. Note that this index is <i>not</i> the
214         * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in
215         * an array.
216         * 
217         * @param of the location of the node whose children are to be read
218         * @param workspaceName the name of the workspace containing the parent
219         * @param startingIndex the zero-based index of the first child to be included in the block
220         * @param count the maximum number of children that should be included in the block
221         * @return the request; never null
222         * @throws IllegalArgumentException if the location or workspace name is null, if <code>startingIndex</code> is negative, or
223         *         if <code>count</count> is less than 1.
224         */
225        public ReadBlockOfChildrenRequest readBlockOfChildren( Location of,
226                                                               String workspaceName,
227                                                               int startingIndex,
228                                                               int count ) {
229            return process(new ReadBlockOfChildrenRequest(of, workspaceName, startingIndex, count));
230        }
231    
232        /**
233         * Add a request to read those children of a node that are immediately after a supplied sibling node.
234         * 
235         * @param startingAfter the location of the previous sibling that was the last child of the previous block of children read
236         * @param workspaceName the name of the workspace containing the node
237         * @param count the maximum number of children that should be included in the block
238         * @return the request; never null
239         * @throws IllegalArgumentException if the workspace name or <code>startingAfter</code> location is null, or if
240         *         <code>count</count> is less than 1.
241         */
242        public ReadNextBlockOfChildrenRequest readNextBlockOfChildren( Location startingAfter,
243                                                                       String workspaceName,
244                                                                       int count ) {
245            return process(new ReadNextBlockOfChildrenRequest(startingAfter, workspaceName, count));
246        }
247    
248        /**
249         * Add a request to create a node with the given properties under the supplied location.
250         * 
251         * @param parentLocation the location of the existing parent node, under which the new child should be created
252         * @param workspaceName the name of the workspace containing the parent
253         * @param childName the name of the new child to create under the existing parent
254         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
255         *        properties} for the new node
256         * @return the request; never null
257         * @throws IllegalArgumentException if the location, workspace name, or child name is null
258         */
259        public CreateNodeRequest createNode( Location parentLocation,
260                                             String workspaceName,
261                                             Name childName,
262                                             Iterator<Property> properties ) {
263            return process(new CreateNodeRequest(parentLocation, workspaceName, childName,
264                                                 CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR, properties));
265        }
266    
267        /**
268         * Add a request to create a node with the given properties under the supplied location.
269         * 
270         * @param parentLocation the location of the existing parent node, under which the new child should be created
271         * @param workspaceName the name of the workspace containing the parent
272         * @param childName the name of the new child to create under the existing parent
273         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
274         *        properties} for the new node
275         * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
276         *        location
277         * @return the request; never null
278         * @throws IllegalArgumentException if the location, workspace name, or child name is null
279         */
280        public CreateNodeRequest createNode( Location parentLocation,
281                                             String workspaceName,
282                                             Name childName,
283                                             Iterator<Property> properties,
284                                             NodeConflictBehavior conflictBehavior ) {
285            if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
286            return process(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
287        }
288    
289        /**
290         * Add a request to create a node with the given properties under the supplied location.
291         * 
292         * @param parentLocation the location of the existing parent node, under which the new child should be created
293         * @param workspaceName the name of the workspace containing the parent
294         * @param childName the name of the new child to create under the existing parent
295         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
296         *        properties} for the new node
297         * @return the request; never null
298         * @throws IllegalArgumentException if the location, workspace name, or child name is null
299         */
300        public CreateNodeRequest createNode( Location parentLocation,
301                                             String workspaceName,
302                                             Name childName,
303                                             Property[] properties ) {
304            return process(new CreateNodeRequest(parentLocation, workspaceName, childName,
305                                                 CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR, properties));
306        }
307    
308        /**
309         * Add a request to create a node with the given properties under the supplied location.
310         * 
311         * @param parentLocation the location of the existing parent node, under which the new child should be created
312         * @param workspaceName the name of the workspace containing the parent
313         * @param childName the name of the new child to create under the existing parent
314         * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
315         *        properties} for the new node
316         * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
317         *        location
318         * @return the request; never null
319         * @throws IllegalArgumentException if the location, workspace name, or child name is null
320         */
321        public CreateNodeRequest createNode( Location parentLocation,
322                                             String workspaceName,
323                                             Name childName,
324                                             Property[] properties,
325                                             NodeConflictBehavior conflictBehavior ) {
326            if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
327            return process(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
328        }
329    
330        /**
331         * Add a request to update the property on the node at the supplied location. This request will create the property if it does
332         * not yet exist.
333         * 
334         * @param on the location of the node to be read
335         * @param workspaceName the name of the workspace containing the node
336         * @param property the new property on the node
337         * @return the request; never null
338         * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
339         */
340        public SetPropertyRequest setProperty( Location on,
341                                               String workspaceName,
342                                               Property property ) {
343            return process(new SetPropertyRequest(on, workspaceName, property));
344        }
345    
346        /**
347         * Add a request to update the properties on the node at the supplied location.
348         * 
349         * @param on the location of the node to be read
350         * @param workspaceName the name of the workspace containing the node
351         * @param properties the new properties on the node
352         * @return the {@link SetPropertyRequest} or {@link UpdatePropertiesRequest} request, depending upon the number of properties
353         *         being set; never null
354         * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
355         */
356        public Request setProperties( Location on,
357                                      String workspaceName,
358                                      Property... properties ) {
359            if (properties.length == 1) {
360                return process(new SetPropertyRequest(on, workspaceName, properties[0]));
361            }
362            Map<Name, Property> propertyMap = new HashMap<Name, Property>();
363            for (Property property : properties) {
364                propertyMap.put(property.getName(), property);
365            }
366            return process(new UpdatePropertiesRequest(on, workspaceName, propertyMap));
367        }
368    
369        /**
370         * Add a request to remove the property with the supplied name from the given node. Supplying a name for a property that does
371         * not exist will not cause an error.
372         * 
373         * @param on the location of the node to be read
374         * @param workspaceName the name of the workspace containing the node
375         * @param propertyName the name of the property that is to be removed
376         * @return the request; never null
377         * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
378         */
379        public RemovePropertyRequest removeProperty( Location on,
380                                                     String workspaceName,
381                                                     Name propertyName ) {
382            return process(new RemovePropertyRequest(on, workspaceName, propertyName));
383        }
384    
385        /**
386         * Add a request to remove from the node the properties with the supplied names. Supplying a name for a property that does not
387         * exist will not cause an error.
388         * 
389         * @param on the location of the node to be read
390         * @param workspaceName the name of the workspace containing the node
391         * @param propertyNames the names of the properties that are to be removed
392         * @return the {@link RemovePropertyRequest} or {@link UpdatePropertiesRequest} request, depending upon the number of
393         *         properties being removed; never null
394         * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
395         */
396        public Request removeProperties( Location on,
397                                         String workspaceName,
398                                         Name... propertyNames ) {
399            if (propertyNames.length == 1) {
400                return process(new RemovePropertyRequest(on, workspaceName, propertyNames[0]));
401            }
402            Map<Name, Property> properties = new HashMap<Name, Property>();
403            for (Name propertyName : propertyNames) {
404                properties.put(propertyName, null);
405            }
406            return process(new UpdatePropertiesRequest(on, workspaceName, properties));
407        }
408    
409        /**
410         * Add a request to rename the node at the supplied location.
411         * 
412         * @param at the location of the node to be read
413         * @param workspaceName the name of the workspace containing the node
414         * @param newName the new name for the node
415         * @return the request; never null
416         * @throws IllegalArgumentException if the location or workspace name is null
417         */
418        public RenameNodeRequest renameNode( Location at,
419                                             String workspaceName,
420                                             Name newName ) {
421            return process(new RenameNodeRequest(at, workspaceName, newName));
422        }
423    
424        /**
425         * Add a request to copy a branch to another.
426         * 
427         * @param from the location of the top node in the existing branch that is to be copied
428         * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
429         * @param into the location of the existing node into which the copy should be placed
430         * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
431         * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
432         *        used
433         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
434         *        location, or null if the default conflict behavior should be used
435         * @return the request; never null
436         * @throws IllegalArgumentException if either of the locations or workspace names are null
437         */
438        public CopyBranchRequest copyBranch( Location from,
439                                             String fromWorkspace,
440                                             Location into,
441                                             String intoWorkspace,
442                                             Name nameForCopy,
443                                             NodeConflictBehavior conflictBehavior ) {
444            if (conflictBehavior == null) conflictBehavior = CopyBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
445            return process(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace, nameForCopy, conflictBehavior));
446        }
447    
448        /**
449         * Create a request to move a branch from one location into another.
450         * 
451         * @param from the location of the top node in the existing branch that is to be moved
452         * @param into the location of the existing node into which the branch should be moved
453         * @param workspaceName the name of the workspace
454         * @return the request; never null
455         * @throws IllegalArgumentException if any of the parameters are null
456         */
457        public MoveBranchRequest moveBranch( Location from,
458                                             Location into,
459                                             String workspaceName ) {
460            return process(new MoveBranchRequest(from, into, workspaceName, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
461        }
462    
463        /**
464         * Create a request to move a branch from one location into another.
465         * 
466         * @param from the location of the top node in the existing branch that is to be moved
467         * @param into the location of the existing node into which the branch should be moved
468         * @param workspaceName the name of the workspace
469         * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
470         * @return the request; never null
471         * @throws IllegalArgumentException if any of the parameters are null
472         */
473        public MoveBranchRequest moveBranch( Location from,
474                                             Location into,
475                                             String workspaceName,
476                                             Name newNameForNode ) {
477            return process(new MoveBranchRequest(from, into, null, workspaceName, newNameForNode, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
478        }
479        
480        /**
481         * Create a request to move a branch from one location into another before the given child node of the new location.
482         * 
483         * @param from the location of the top node in the existing branch that is to be moved
484         * @param into the location of the existing node into which the branch should be moved
485         * @param before the location of the node before which the branch should be moved; may be null
486         * @param workspaceName the name of the workspace
487         * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
488         * @return the request; never null
489         * @throws IllegalArgumentException if any of the parameters are null
490         */
491        public MoveBranchRequest moveBranch( Location from,
492                                             Location into,
493                                             Location before, 
494                                             String workspaceName,
495                                             Name newNameForNode ) {
496            return process(new MoveBranchRequest(from, into, before, workspaceName, newNameForNode, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
497        }    
498        /**
499         * Create a request to move a branch from one location into another.
500         * 
501         * @param from the location of the top node in the existing branch that is to be moved
502         * @param into the location of the existing node into which the branch should be moved
503         * @param workspaceName the name of the workspace
504         * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
505         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
506         *        location
507         * @return the request; never null
508         * @throws IllegalArgumentException if any of the parameters are null
509         */
510        public MoveBranchRequest moveBranch( Location from,
511                                             Location into,
512                                             String workspaceName,
513                                             Name newNameForNode,
514                                             NodeConflictBehavior conflictBehavior ) {
515            if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
516            return process(new MoveBranchRequest(from, into, null, workspaceName, newNameForNode, conflictBehavior));
517        }
518    
519        /**
520         * Create a request to move a branch from one location into another.
521         * 
522         * @param from the location of the top node in the existing branch that is to be moved
523         * @param into the location of the existing node into which the branch should be moved
524         * @param workspaceName the name of the workspace
525         * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
526         *        location
527         * @return the request; never null
528         * @throws IllegalArgumentException if any of the parameters are null
529         */
530        public MoveBranchRequest moveBranch( Location from,
531                                             Location into,
532                                             String workspaceName,
533                                             NodeConflictBehavior conflictBehavior ) {
534            if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
535            return process(new MoveBranchRequest(from, into, workspaceName, conflictBehavior));
536        }
537    
538        /**
539         * Add a request to delete a branch.
540         * 
541         * @param at the location of the top node in the existing branch that is to be deleted
542         * @param workspaceName the name of the workspace containing the parent
543         * @return the request; never null
544         * @throws IllegalArgumentException if the location or workspace name is null
545         */
546        public DeleteBranchRequest deleteBranch( Location at,
547                                                 String workspaceName ) {
548            return process(new DeleteBranchRequest(at, workspaceName));
549        }
550    }