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 }