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 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed 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.processor;
025
026 import java.util.Collections;
027 import java.util.LinkedList;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.Queue;
031 import net.jcip.annotations.Immutable;
032 import org.jboss.dna.common.util.CheckArg;
033 import org.jboss.dna.graph.ExecutionContext;
034 import org.jboss.dna.graph.GraphI18n;
035 import org.jboss.dna.graph.Location;
036 import org.jboss.dna.graph.cache.CachePolicy;
037 import org.jboss.dna.graph.connector.RepositorySourceException;
038 import org.jboss.dna.graph.property.DateTime;
039 import org.jboss.dna.graph.property.Name;
040 import org.jboss.dna.graph.property.Path;
041 import org.jboss.dna.graph.property.Property;
042 import org.jboss.dna.graph.property.ReferentialIntegrityException;
043 import org.jboss.dna.graph.request.CacheableRequest;
044 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
045 import org.jboss.dna.graph.request.CompositeRequest;
046 import org.jboss.dna.graph.request.CopyBranchRequest;
047 import org.jboss.dna.graph.request.CreateNodeRequest;
048 import org.jboss.dna.graph.request.CreateWorkspaceRequest;
049 import org.jboss.dna.graph.request.DeleteBranchRequest;
050 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
051 import org.jboss.dna.graph.request.GetWorkspacesRequest;
052 import org.jboss.dna.graph.request.InvalidRequestException;
053 import org.jboss.dna.graph.request.MoveBranchRequest;
054 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
055 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
056 import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
057 import org.jboss.dna.graph.request.ReadBranchRequest;
058 import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
059 import org.jboss.dna.graph.request.ReadNodeRequest;
060 import org.jboss.dna.graph.request.ReadPropertyRequest;
061 import org.jboss.dna.graph.request.RemovePropertyRequest;
062 import org.jboss.dna.graph.request.RenameNodeRequest;
063 import org.jboss.dna.graph.request.Request;
064 import org.jboss.dna.graph.request.SetPropertyRequest;
065 import org.jboss.dna.graph.request.UnsupportedRequestException;
066 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
067 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
068 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
069
070 /**
071 * A component that is used to process and execute {@link Request}s. This class is intended to be subclassed and methods
072 * overwritten to define the behavior for executing the different kinds of requests. Abstract methods must be overridden, but
073 * non-abstract methods all have meaningful default implementations.
074 *
075 * @author Randall Hauch
076 */
077 @Immutable
078 public abstract class RequestProcessor {
079
080 private final ExecutionContext context;
081 private final String sourceName;
082 private final DateTime nowInUtc;
083 private final CachePolicy defaultCachePolicy;
084
085 protected RequestProcessor( String sourceName,
086 ExecutionContext context ) {
087 this(sourceName, context, null, null);
088 }
089
090 protected RequestProcessor( String sourceName,
091 ExecutionContext context,
092 DateTime now ) {
093 this(sourceName, context, now, null);
094 }
095
096 protected RequestProcessor( String sourceName,
097 ExecutionContext context,
098 DateTime now,
099 CachePolicy defaultCachePolicy ) {
100 CheckArg.isNotEmpty(sourceName, "sourceName");
101 CheckArg.isNotNull(context, "context");
102 this.context = context;
103 this.sourceName = sourceName;
104 this.nowInUtc = now != null ? now : context.getValueFactories().getDateFactory().createUtc();
105 this.defaultCachePolicy = defaultCachePolicy;
106 }
107
108 /**
109 * Get the name of the source against which this processor is executing.
110 *
111 * @return the repository source name; never null or empty
112 */
113 public String getSourceName() {
114 return sourceName;
115 }
116
117 /**
118 * The execution context that this process is operating within.
119 *
120 * @return the execution context; never null
121 */
122 public ExecutionContext getExecutionContext() {
123 return this.context;
124 }
125
126 /**
127 * Get the 'current time' for this processor, which is usually a constant during its lifetime.
128 *
129 * @return the current time in UTC; never null
130 */
131 protected DateTime getNowInUtc() {
132 return this.nowInUtc;
133 }
134
135 /**
136 * Set the supplied request to have the default cache policy and the {@link #getNowInUtc() current time in UTC}.
137 *
138 * @param request the cacheable request
139 */
140 protected void setCacheableInfo( CacheableRequest request ) {
141 request.setCachePolicy(defaultCachePolicy);
142 request.setTimeLoaded(nowInUtc);
143 }
144
145 /**
146 * Set the supplied request to have the supplied cache policy and the {@link #getNowInUtc() current time in UTC}.
147 *
148 * @param request the cacheable request
149 * @param cachePolicy the cache policy for the request; may be null if there is to be no cache policy
150 */
151 protected void setCacheableInfo( CacheableRequest request,
152 CachePolicy cachePolicy ) {
153 request.setCachePolicy(cachePolicy);
154 request.setTimeLoaded(nowInUtc);
155 }
156
157 /**
158 * Process a request by determining the type of request and delegating to the appropriate <code>process</code> method for that
159 * type.
160 * <p>
161 * This method does nothing if the request is null.
162 * </p>
163 *
164 * @param request the general request
165 */
166 public void process( Request request ) {
167 if (request == null) return;
168 if (request.isCancelled()) return;
169 if (request instanceof CompositeRequest) {
170 process((CompositeRequest)request);
171 } else if (request instanceof CopyBranchRequest) {
172 process((CopyBranchRequest)request);
173 } else if (request instanceof CreateNodeRequest) {
174 process((CreateNodeRequest)request);
175 } else if (request instanceof DeleteBranchRequest) {
176 process((DeleteBranchRequest)request);
177 } else if (request instanceof MoveBranchRequest) {
178 process((MoveBranchRequest)request);
179 } else if (request instanceof ReadAllChildrenRequest) {
180 process((ReadAllChildrenRequest)request);
181 } else if (request instanceof ReadNextBlockOfChildrenRequest) {
182 process((ReadNextBlockOfChildrenRequest)request);
183 } else if (request instanceof ReadBlockOfChildrenRequest) {
184 process((ReadBlockOfChildrenRequest)request);
185 } else if (request instanceof ReadBranchRequest) {
186 process((ReadBranchRequest)request);
187 } else if (request instanceof ReadNodeRequest) {
188 process((ReadNodeRequest)request);
189 } else if (request instanceof ReadAllPropertiesRequest) {
190 process((ReadAllPropertiesRequest)request);
191 } else if (request instanceof ReadPropertyRequest) {
192 process((ReadPropertyRequest)request);
193 } else if (request instanceof RemovePropertyRequest) {
194 process((RemovePropertyRequest)request);
195 } else if (request instanceof SetPropertyRequest) {
196 process((SetPropertyRequest)request);
197 } else if (request instanceof RenameNodeRequest) {
198 process((RenameNodeRequest)request);
199 } else if (request instanceof UpdatePropertiesRequest) {
200 process((UpdatePropertiesRequest)request);
201 } else if (request instanceof VerifyNodeExistsRequest) {
202 process((VerifyNodeExistsRequest)request);
203 } else if (request instanceof VerifyWorkspaceRequest) {
204 process((VerifyWorkspaceRequest)request);
205 } else if (request instanceof GetWorkspacesRequest) {
206 process((GetWorkspacesRequest)request);
207 } else if (request instanceof CreateWorkspaceRequest) {
208 process((CreateWorkspaceRequest)request);
209 } else if (request instanceof CloneWorkspaceRequest) {
210 process((CloneWorkspaceRequest)request);
211 } else if (request instanceof DestroyWorkspaceRequest) {
212 process((DestroyWorkspaceRequest)request);
213 } else {
214 processUnknownRequest(request);
215 }
216 }
217
218 /**
219 * Process a request that is composed of multiple other (non-composite) requests. If any of the embedded requests
220 * {@link Request#hasError() has an error} after it is processed, the submitted request will be marked with an error.
221 * <p>
222 * This method does nothing if the request is null.
223 * </p>
224 *
225 * @param request the composite request
226 */
227 public void process( CompositeRequest request ) {
228 if (request == null) return;
229 int numberOfErrors = 0;
230 List<Throwable> errors = null;
231 for (Request embedded : request) {
232 assert embedded != null;
233 if (embedded.isCancelled()) return;
234 process(embedded);
235 if (embedded.hasError()) {
236 if (numberOfErrors == 0) {
237 errors = new LinkedList<Throwable>();
238 }
239 assert errors != null;
240 errors.add(embedded.getError());
241 ++numberOfErrors;
242 }
243 }
244 if (numberOfErrors == 0) return;
245 assert errors != null;
246 if (numberOfErrors == 1) {
247 request.setError(errors.get(0));
248 } else {
249 StringBuilder errorString = new StringBuilder();
250 for (Throwable error : errors) {
251 errorString.append("\n");
252 errorString.append("\t" + error.getMessage());
253 }
254 String msg = GraphI18n.multipleErrorsWhileExecutingRequests.text(numberOfErrors,
255 request.size(),
256 errorString.toString());
257 request.setError(new RepositorySourceException(getSourceName(), msg));
258 }
259 }
260
261 /**
262 * Method that is called by {@link #process(Request)} when the request was found to be of a request type that is not known by
263 * this processor. By default this method sets an {@link UnsupportedRequestException unsupported request error} on the
264 * request.
265 *
266 * @param request the unknown request
267 */
268 protected void processUnknownRequest( Request request ) {
269 request.setError(new InvalidRequestException(GraphI18n.unsupportedRequestType.text(request.getClass().getName(), request)));
270 }
271
272 /**
273 * Process a request to verify a named workspace.
274 * <p>
275 * This method does nothing if the request is null.
276 * </p>
277 *
278 * @param request the request
279 */
280 public abstract void process( VerifyWorkspaceRequest request );
281
282 /**
283 * Process a request to get the information about the available workspaces.
284 * <p>
285 * This method does nothing if the request is null.
286 * </p>
287 *
288 * @param request the request
289 */
290 public abstract void process( GetWorkspacesRequest request );
291
292 /**
293 * Process a request to create a new workspace.
294 * <p>
295 * This method does nothing if the request is null.
296 * </p>
297 *
298 * @param request the request
299 */
300 public abstract void process( CreateWorkspaceRequest request );
301
302 /**
303 * Process a request to clone an existing workspace as a new workspace.
304 * <p>
305 * This method does nothing if the request is null.
306 * </p>
307 *
308 * @param request the request
309 */
310 public abstract void process( CloneWorkspaceRequest request );
311
312 /**
313 * Process a request to permanently destroy a workspace.
314 * <p>
315 * This method does nothing if the request is null.
316 * </p>
317 *
318 * @param request the request
319 */
320 public abstract void process( DestroyWorkspaceRequest request );
321
322 /**
323 * Process a request to copy a branch into another location.
324 * <p>
325 * This method does nothing if the request is null.
326 * </p>
327 *
328 * @param request the copy request
329 */
330 public abstract void process( CopyBranchRequest request );
331
332 /**
333 * Process a request to create a node at a specified location.
334 * <p>
335 * This method does nothing if the request is null.
336 * </p>
337 *
338 * @param request the create request
339 */
340 public abstract void process( CreateNodeRequest request );
341
342 /**
343 * Process a request to delete a branch at a specified location.
344 * <p>
345 * This method does nothing if the request is null.
346 * </p>
347 *
348 * @param request the delete request
349 * @throws ReferentialIntegrityException if the delete could not be performed because some references to deleted nodes would
350 * have remained after the delete operation completed
351 */
352 public abstract void process( DeleteBranchRequest request );
353
354 /**
355 * Process a request to move a branch at a specified location into a different location.
356 * <p>
357 * This method does nothing if the request is null.
358 * </p>
359 *
360 * @param request the move request
361 */
362 public abstract void process( MoveBranchRequest request );
363
364 /**
365 * Process a request to read all of the children of a node.
366 * <p>
367 * This method does nothing if the request is null.
368 * </p>
369 *
370 * @param request the read request
371 */
372 public abstract void process( ReadAllChildrenRequest request );
373
374 /**
375 * Process a request to read a block of the children of a node. The block is defined by a
376 * {@link ReadBlockOfChildrenRequest#startingAtIndex() starting index} and a {@link ReadBlockOfChildrenRequest#count() maximum
377 * number of children to include in the block}.
378 * <p>
379 * This method does nothing if the request is null. The default implementation converts the command to a
380 * {@link ReadAllChildrenRequest}, and then finds the children within the block. Obviously for large numbers of children, this
381 * implementation may not be efficient and may need to be overridden.
382 * </p>
383 *
384 * @param request the read request
385 */
386 public void process( ReadBlockOfChildrenRequest request ) {
387 if (request == null) return;
388 // Convert the request to a ReadAllChildrenRequest and execute it ...
389 ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(request.of(), request.inWorkspace());
390 process(readAll);
391 if (readAll.hasError()) {
392 request.setError(readAll.getError());
393 return;
394 }
395 List<Location> allChildren = readAll.getChildren();
396
397 // If there aren't enough children for the block's range ...
398 if (allChildren.size() < request.startingAtIndex()) return;
399
400 // Now, find the children in the block ...
401 int endIndex = Math.min(request.endingBefore(), allChildren.size());
402 for (int i = request.startingAtIndex(); i != endIndex; ++i) {
403 request.addChild(allChildren.get(i));
404 }
405 // Set the actual location ...
406 request.setActualLocationOfNode(readAll.getActualLocationOfNode());
407 setCacheableInfo(request);
408 }
409
410 /**
411 * Process a request to read the next block of the children of a node, starting after a previously-retrieved child.
412 * <p>
413 * This method does nothing if the request is null. The default implementation converts the command to a
414 * {@link ReadAllChildrenRequest}, and then finds the children within the block. Obviously for large numbers of children, this
415 * implementation may not be efficient and may need to be overridden.
416 * </p>
417 *
418 * @param request the read request
419 */
420 public void process( ReadNextBlockOfChildrenRequest request ) {
421 if (request == null) return;
422
423 // Get the parent path ...
424 Location actualSiblingLocation = request.startingAfter();
425 Path path = actualSiblingLocation.getPath();
426 Path parentPath = null;
427 if (path != null) parentPath = path.getParent();
428 if (parentPath == null) {
429 // Need to find the parent path, so get the actual location of the sibling ...
430 VerifyNodeExistsRequest verifySibling = new VerifyNodeExistsRequest(request.startingAfter(), request.inWorkspace());
431 process(verifySibling);
432 actualSiblingLocation = verifySibling.getActualLocationOfNode();
433 parentPath = actualSiblingLocation.getPath().getParent();
434 }
435 assert parentPath != null;
436
437 // Convert the request to a ReadAllChildrenRequest and execute it ...
438 ReadAllChildrenRequest readAll = new ReadAllChildrenRequest(Location.create(parentPath), request.inWorkspace());
439 process(readAll);
440 if (readAll.hasError()) {
441 request.setError(readAll.getError());
442 return;
443 }
444 List<Location> allChildren = readAll.getChildren();
445
446 // Iterate through the children, looking for the 'startingAfter' child ...
447 boolean found = false;
448 int count = 0;
449 for (Location child : allChildren) {
450 if (count > request.count()) break;
451 if (!found) {
452 // Set to true if we find the child we're looking for ...
453 found = child.equals(request.startingAfter());
454 } else {
455 // Add the child to the block ...
456 ++count;
457 request.addChild(child);
458 }
459 }
460
461 // Set the actual location ...
462 request.setActualLocationOfStartingAfterNode(actualSiblingLocation);
463 setCacheableInfo(request);
464 }
465
466 /**
467 * Process a request to read a branch or subgraph that's below a node at a specified location.
468 * <p>
469 * This method does nothing if the request is null. The default implementation processes the branch by submitting the
470 * equivalent requests to {@link ReadNodeRequest read the nodes} and the {@link ReadAllChildrenRequest children}. It starts by
471 * doing this for the top-level node, then proceeds for each of the children of that node, and so forth.
472 * </p>
473 *
474 * @param request the request to read the branch
475 */
476 public void process( ReadBranchRequest request ) {
477 if (request == null) return;
478 // Create a queue for locations that need to be read ...
479 Queue<LocationWithDepth> locationsToRead = new LinkedList<LocationWithDepth>();
480 locationsToRead.add(new LocationWithDepth(request.at(), 1));
481
482 // Now read the locations ...
483 boolean first = true;
484 while (locationsToRead.peek() != null) {
485 if (request.isCancelled()) return;
486 LocationWithDepth read = locationsToRead.poll();
487
488 // Check the depth ...
489 if (read.depth > request.maximumDepth()) break;
490
491 // Read the properties ...
492 ReadNodeRequest readNode = new ReadNodeRequest(read.location, request.inWorkspace());
493 process(readNode);
494 if (readNode.hasError()) {
495 request.setError(readNode.getError());
496 return;
497 }
498 Location actualLocation = readNode.getActualLocationOfNode();
499 if (first) {
500 // Set the actual location on the original request
501 request.setActualLocationOfNode(actualLocation);
502 first = false;
503 }
504
505 // Record in the request the children and properties that were read on this node ...
506 request.setChildren(actualLocation, readNode.getChildren());
507 request.setProperties(actualLocation, readNode.getProperties());
508
509 // Add each of the children to the list of locations that we need to read ...
510 for (Location child : readNode.getChildren()) {
511 locationsToRead.add(new LocationWithDepth(child, read.depth + 1));
512 }
513 }
514 setCacheableInfo(request);
515 }
516
517 /**
518 * Process a request to read the properties of a node at the supplied location.
519 * <p>
520 * This method does nothing if the request is null.
521 * </p>
522 *
523 * @param request the read request
524 */
525 public abstract void process( ReadAllPropertiesRequest request );
526
527 /**
528 * Process a request to read the properties and children of a node at the supplied location.
529 * <p>
530 * This method does nothing if the request is null. Unless overridden, this method converts the single request into a
531 * {@link ReadAllChildrenRequest} and a {@link ReadAllPropertiesRequest}.
532 * </p>
533 *
534 * @param request the read request
535 */
536 public void process( ReadNodeRequest request ) {
537 if (request == null) return;
538 // Read the properties ...
539 ReadAllPropertiesRequest readProperties = new ReadAllPropertiesRequest(request.at(), request.inWorkspace());
540 process(readProperties);
541 if (readProperties.hasError()) {
542 request.setError(readProperties.getError());
543 return;
544 }
545 // Set the actual location ...
546 request.setActualLocationOfNode(readProperties.getActualLocationOfNode());
547
548 // Read the children ...
549 ReadAllChildrenRequest readChildren = new ReadAllChildrenRequest(request.at(), request.inWorkspace());
550 process(readChildren);
551 if (readChildren.hasError()) {
552 request.setError(readChildren.getError());
553 return;
554 }
555 if (request.isCancelled()) return;
556 // Now, copy all of the results into the submitted request ...
557 for (Property property : readProperties) {
558 request.addProperty(property);
559 }
560 for (Location child : readChildren) {
561 request.addChild(child);
562 }
563 setCacheableInfo(request);
564 }
565
566 /**
567 * Process a request to read a single property of a node at the supplied location.
568 * <p>
569 * This method does nothing if the request is null. Unless overridden, this method converts the request that
570 * {@link ReadAllPropertiesRequest reads the node} and simply returns the one property.
571 * </p>
572 *
573 * @param request the read request
574 */
575 public void process( ReadPropertyRequest request ) {
576 if (request == null) return;
577 ReadAllPropertiesRequest readNode = new ReadAllPropertiesRequest(request.on(), request.inWorkspace());
578 process(readNode);
579 if (readNode.hasError()) {
580 request.setError(readNode.getError());
581 return;
582 }
583 Property property = readNode.getPropertiesByName().get(request.named());
584 request.setProperty(property);
585 // Set the actual location ...
586 request.setActualLocationOfNode(readNode.getActualLocationOfNode());
587 setCacheableInfo(request);
588 }
589
590 /**
591 * Process a request to verify that a node exists at the supplied location.
592 * <p>
593 * This method does nothing if the request is null. Unless overridden, this method converts the request that
594 * {@link ReadAllPropertiesRequest reads the node} and uses the result to determine if the node exists.
595 * </p>
596 *
597 * @param request the read request
598 */
599 public void process( VerifyNodeExistsRequest request ) {
600 if (request == null) return;
601 ReadAllPropertiesRequest readNode = new ReadAllPropertiesRequest(request.at(), request.inWorkspace());
602 process(readNode);
603 if (readNode.hasError()) {
604 request.setError(readNode.getError());
605 return;
606 }
607 // Set the actual location ...
608 request.setActualLocationOfNode(readNode.getActualLocationOfNode());
609 setCacheableInfo(request);
610 }
611
612 /**
613 * Process a request to remove the specified property from a node.
614 * <p>
615 * This method does nothing if the request is null. Unless overridden, this method converts this request into a
616 * {@link UpdatePropertiesRequest}.
617 * </p>
618 *
619 * @param request the request to remove the property
620 */
621 public void process( RemovePropertyRequest request ) {
622 if (request == null) return;
623 Map<Name, Property> properties = Collections.singletonMap(request.propertyName(), null);
624 UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.from(), request.inWorkspace(), properties);
625 process(update);
626 if (update.hasError()) {
627 request.setError(update.getError());
628 }
629 // Set the actual location ...
630 request.setActualLocationOfNode(update.getActualLocationOfNode());
631 }
632
633 /**
634 * Process a request to set the specified property on a node.
635 * <p>
636 * This method does nothing if the request is null. Unless overridden, this method converts this request into a
637 * {@link UpdatePropertiesRequest}.
638 * </p>
639 *
640 * @param request the request to set the property
641 */
642 public void process( SetPropertyRequest request ) {
643 if (request == null) return;
644 Property property = request.property();
645 Map<Name, Property> properties = Collections.singletonMap(property.getName(), property);
646 UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.on(), request.inWorkspace(), properties);
647 process(update);
648 if (update.hasError()) {
649 request.setError(update.getError());
650 } else {
651 // Set the actual location ...
652 request.setActualLocationOfNode(update.getActualLocationOfNode());
653 }
654 }
655
656 /**
657 * Process a request to remove the specified properties from a node.
658 * <p>
659 * This method does nothing if the request is null.
660 * </p>
661 *
662 * @param request the remove request
663 */
664 public abstract void process( UpdatePropertiesRequest request );
665
666 /**
667 * Process a request to rename a node specified location into a different location.
668 * <p>
669 * This method does nothing if the request is null. Unless overridden, this method converts the rename into a
670 * {@link MoveBranchRequest move}. However, this only works if the <code>request</code> has a {@link Location#hasPath() path}
671 * for its {@link RenameNodeRequest#at() location}. (If not, this method throws an {@link UnsupportedOperationException} and
672 * must be overridden.)
673 * </p>
674 *
675 * @param request the rename request
676 */
677 public void process( RenameNodeRequest request ) {
678 if (request == null) return;
679 Location from = request.at();
680 if (!from.hasPath()) {
681 throw new UnsupportedOperationException();
682 }
683 Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(from.getPath(), request.toName());
684 Location to = Location.create(newPath);
685 MoveBranchRequest move = new MoveBranchRequest(from, to, request.inWorkspace());
686 process(move);
687 // Set the actual locations ...
688 request.setActualLocations(move.getActualLocationBefore(), move.getActualLocationAfter());
689 }
690
691 /**
692 * Close this processor, allowing it to clean up any open resources.
693 */
694 public void close() {
695 // do nothing
696 }
697
698 /**
699 * A class that represents a location at a known depth
700 *
701 * @author Randall Hauch
702 */
703 @Immutable
704 protected static class LocationWithDepth {
705 protected final Location location;
706 protected final int depth;
707
708 protected LocationWithDepth( Location location,
709 int depth ) {
710 this.location = location;
711 this.depth = depth;
712 }
713
714 @Override
715 public int hashCode() {
716 return location.hashCode();
717 }
718
719 @Override
720 public String toString() {
721 return location.toString() + " at depth " + depth;
722 }
723 }
724
725 }