1 17 package org.alfresco.repo.model.filefolder; 18 19 import java.io.Serializable ; 20 import java.util.ArrayList ; 21 import java.util.Collections ; 22 import java.util.HashMap ; 23 import java.util.Iterator ; 24 import java.util.List ; 25 import java.util.Map ; 26 27 import org.alfresco.error.AlfrescoRuntimeException; 28 import org.alfresco.model.ContentModel; 29 import org.alfresco.repo.search.QueryParameterDefImpl; 30 import org.alfresco.service.cmr.dictionary.DataTypeDefinition; 31 import org.alfresco.service.cmr.dictionary.DictionaryService; 32 import org.alfresco.service.cmr.model.FileExistsException; 33 import org.alfresco.service.cmr.model.FileFolderService; 34 import org.alfresco.service.cmr.model.FileInfo; 35 import org.alfresco.service.cmr.model.FileNotFoundException; 36 import org.alfresco.service.cmr.repository.ChildAssociationRef; 37 import org.alfresco.service.cmr.repository.ContentData; 38 import org.alfresco.service.cmr.repository.ContentReader; 39 import org.alfresco.service.cmr.repository.ContentService; 40 import org.alfresco.service.cmr.repository.ContentWriter; 41 import org.alfresco.service.cmr.repository.CopyService; 42 import org.alfresco.service.cmr.repository.InvalidNodeRefException; 43 import org.alfresco.service.cmr.repository.MimetypeService; 44 import org.alfresco.service.cmr.repository.NodeRef; 45 import org.alfresco.service.cmr.repository.NodeService; 46 import org.alfresco.service.cmr.repository.Path; 47 import org.alfresco.service.cmr.search.QueryParameterDefinition; 48 import org.alfresco.service.cmr.search.SearchService; 49 import org.alfresco.service.namespace.NamespaceService; 50 import org.alfresco.service.namespace.QName; 51 import org.alfresco.util.SearchLanguageConversion; 52 import org.apache.commons.logging.Log; 53 import org.apache.commons.logging.LogFactory; 54 55 60 public class FileFolderServiceImpl implements FileFolderService 61 { 62 63 private static final String XPATH_QUERY_SHALLOW_FILES = 64 "./*" + 65 "[(subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; 66 67 68 private static final String XPATH_QUERY_SHALLOW_FOLDERS = 69 "./*" + 70 "[not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + 71 " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "'))]"; 72 73 74 private static final String XPATH_QUERY_SHALLOW_ALL = 75 "./*" + 76 "[like(@cm:name, $cm:name, false)" + 77 " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + 78 " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; 79 80 81 private static final String XPATH_QUERY_DEEP_ALL = 82 ".//*" + 83 "[like(@cm:name, $cm:name, false)" + 84 " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + 85 " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; 86 87 88 private static final QueryParameterDefinition[] PARAMS_EMPTY = new QueryParameterDefinition[0]; 89 private static final QueryParameterDefinition[] PARAMS_ANY_NAME = new QueryParameterDefinition[1]; 90 91 private static Log logger = LogFactory.getLog(FileFolderServiceImpl.class); 92 93 private NamespaceService namespaceService; 94 private DictionaryService dictionaryService; 95 private NodeService nodeService; 96 private CopyService copyService; 97 private SearchService searchService; 98 private ContentService contentService; 99 private MimetypeService mimetypeService; 100 101 private List systemPaths; 103 104 107 public FileFolderServiceImpl() 108 { 109 } 110 111 public void setNamespaceService(NamespaceService namespaceService) 112 { 113 this.namespaceService = namespaceService; 114 } 115 116 public void setDictionaryService(DictionaryService dictionaryService) 117 { 118 this.dictionaryService = dictionaryService; 119 } 120 121 public void setNodeService(NodeService nodeService) 122 { 123 this.nodeService = nodeService; 124 } 125 126 public void setCopyService(CopyService copyService) 127 { 128 this.copyService = copyService; 129 } 130 131 public void setSearchService(SearchService searchService) 132 { 133 this.searchService = searchService; 134 } 135 136 public void setContentService(ContentService contentService) 137 { 138 this.contentService = contentService; 139 } 140 141 public void setMimetypeService(MimetypeService mimetypeService) 142 { 143 this.mimetypeService = mimetypeService; 144 } 145 146 public void setSystemPaths(List <String > systemPaths) 148 { 149 this.systemPaths = systemPaths; 150 } 151 152 153 public void init() 154 { 155 PARAMS_ANY_NAME[0] = new QueryParameterDefImpl( 156 ContentModel.PROP_NAME, 157 dictionaryService.getDataType(DataTypeDefinition.TEXT), 158 true, 159 "%"); 160 } 161 162 169 private List <FileInfo> toFileInfo(List <NodeRef> nodeRefs) throws InvalidTypeException 170 { 171 List <FileInfo> results = new ArrayList <FileInfo>(nodeRefs.size()); 172 for (NodeRef nodeRef : nodeRefs) 173 { 174 FileInfo fileInfo = toFileInfo(nodeRef); 175 results.add(fileInfo); 176 } 177 return results; 178 } 179 180 183 private FileInfo toFileInfo(NodeRef nodeRef) throws InvalidTypeException 184 { 185 Map <QName, Serializable > properties = nodeService.getProperties(nodeRef); 187 QName typeQName = nodeService.getType(nodeRef); 189 boolean isFolder = isFolder(typeQName); 190 191 FileInfo fileInfo = new FileInfoImpl(nodeRef, isFolder, properties); 193 return fileInfo; 195 } 196 197 202 private void checkExists(NodeRef parentFolderRef, String name) 203 throws FileExistsException 204 { 205 List <FileInfo> existingFileInfos = this.search(parentFolderRef, name, true, true, false); 207 if (existingFileInfos.size() > 0) 208 { 209 throw new FileExistsException(existingFileInfos.get(0)); 210 } 211 } 212 213 221 private static class InvalidTypeException extends RuntimeException 222 { 223 private static final long serialVersionUID = -310101369475434280L; 224 225 public InvalidTypeException(String msg) 226 { 227 super(msg); 228 } 229 } 230 231 239 private boolean isFolder(QName typeQName) throws InvalidTypeException 240 { 241 if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_FOLDER)) 242 { 243 if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_SYSTEM_FOLDER)) 244 { 245 throw new InvalidTypeException("This service should ignore type " + ContentModel.TYPE_SYSTEM_FOLDER); 246 } 247 return true; 248 } 249 else if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT)) 250 { 251 return false; 253 } 254 else 255 { 256 throw new InvalidTypeException("Type is not handled by this service: " + typeQName); 258 } 259 } 260 261 264 public List <FileInfo> list(NodeRef contextNodeRef) 265 { 266 List <NodeRef> nodeRefs = searchService.selectNodes( 268 contextNodeRef, 269 XPATH_QUERY_SHALLOW_ALL, 270 PARAMS_ANY_NAME, 271 namespaceService, 272 false); 273 List <FileInfo> results = toFileInfo(nodeRefs); 275 if (logger.isDebugEnabled()) 277 { 278 logger.debug("Shallow search for files and folders: \n" + 279 " context: " + contextNodeRef + "\n" + 280 " results: " + results); 281 } 282 return results; 283 } 284 285 288 public List <FileInfo> listFiles(NodeRef contextNodeRef) 289 { 290 List <NodeRef> nodeRefs = searchService.selectNodes( 292 contextNodeRef, 293 XPATH_QUERY_SHALLOW_FILES, 294 PARAMS_EMPTY, 295 namespaceService, 296 false); 297 List <FileInfo> results = toFileInfo(nodeRefs); 299 if (logger.isDebugEnabled()) 301 { 302 logger.debug("Shallow search for files: \n" + 303 " context: " + contextNodeRef + "\n" + 304 " results: " + results); 305 } 306 return results; 307 } 308 309 312 public List <FileInfo> listFolders(NodeRef contextNodeRef) 313 { 314 List <NodeRef> nodeRefs = searchService.selectNodes( 316 contextNodeRef, 317 XPATH_QUERY_SHALLOW_FOLDERS, 318 PARAMS_EMPTY, 319 namespaceService, 320 false); 321 List <FileInfo> results = toFileInfo(nodeRefs); 323 if (logger.isDebugEnabled()) 325 { 326 logger.debug("Shallow search for folders: \n" + 327 " context: " + contextNodeRef + "\n" + 328 " results: " + results); 329 } 330 return results; 331 } 332 333 336 public List <FileInfo> search(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders) 337 { 338 return search(contextNodeRef, namePattern, true, true, includeSubFolders); 339 } 340 341 344 public List <FileInfo> search( 345 NodeRef contextNodeRef, 346 String namePattern, 347 boolean fileSearch, 348 boolean folderSearch, 349 boolean includeSubFolders) 350 { 351 if (!fileSearch && !folderSearch) 353 { 354 return Collections.emptyList(); 355 } 356 357 QueryParameterDefinition[] params = null; 359 if (namePattern != null) 360 { 361 namePattern = SearchLanguageConversion.convert( 363 SearchLanguageConversion.DEF_LUCENE, 364 SearchLanguageConversion.DEF_XPATH_LIKE, 365 namePattern); 366 367 params = new QueryParameterDefinition[1]; 368 params[0] = new QueryParameterDefImpl( 369 ContentModel.PROP_NAME, 370 dictionaryService.getDataType(DataTypeDefinition.TEXT), 371 true, 372 namePattern); 373 } 374 else 375 { 376 params = PARAMS_ANY_NAME; 377 } 378 String query = null; 380 if (includeSubFolders) 381 { 382 query = XPATH_QUERY_DEEP_ALL; 383 } 384 else 385 { 386 query = XPATH_QUERY_SHALLOW_ALL; 387 } 388 List <NodeRef> nodeRefs = searchService.selectNodes( 390 contextNodeRef, 391 query, 392 params, 393 namespaceService, 394 false); 395 List <FileInfo> results = toFileInfo(nodeRefs); 396 Iterator <FileInfo> iterator = results.iterator(); 398 while (iterator.hasNext()) 399 { 400 FileInfo file = iterator.next(); 401 if (file.isFolder() && !folderSearch) 402 { 403 iterator.remove(); 404 } 405 else if (!file.isFolder() && !fileSearch) 406 { 407 iterator.remove(); 408 } 409 } 410 if (logger.isDebugEnabled()) 412 { 413 logger.debug("Deep search: \n" + 414 " context: " + contextNodeRef + "\n" + 415 " pattern: " + namePattern + "\n" + 416 " files: " + fileSearch + "\n" + 417 " folders: " + folderSearch + "\n" + 418 " deep: " + includeSubFolders + "\n" + 419 " results: " + results); 420 } 421 return results; 422 } 423 424 427 public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException 428 { 429 return move(sourceNodeRef, null, newName); 430 } 431 432 435 public FileInfo move(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException 436 { 437 return moveOrCopy(sourceNodeRef, targetParentRef, newName, true); 438 } 439 440 443 public FileInfo copy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException 444 { 445 return moveOrCopy(sourceNodeRef, targetParentRef, newName, false); 446 } 447 448 453 private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException 454 { 455 FileInfo beforeFileInfo = toFileInfo(sourceNodeRef); 457 if (newName == null) 459 { 460 newName = beforeFileInfo.getName(); 461 } 462 463 ChildAssociationRef assocRef = nodeService.getPrimaryParent(sourceNodeRef); 465 if (targetParentRef == null) 466 { 467 targetParentRef = assocRef.getParentRef(); 468 } 469 470 if (targetParentRef.equals(assocRef.getParentRef()) && newName.equals(beforeFileInfo.getName())) 472 { 473 if (logger.isDebugEnabled()) 474 { 475 logger.debug("Doing nothing - neither filename or parent has not changed: \n" + 476 " parent: " + targetParentRef + "\n" + 477 " before: " + beforeFileInfo + "\n" + 478 " new name: " + newName); 479 } 480 return beforeFileInfo; 481 } 482 483 checkExists(targetParentRef, newName); 485 486 QName qname = QName.createQName( 487 NamespaceService.CONTENT_MODEL_1_0_URI, 488 QName.createValidLocalName(newName)); 489 490 NodeRef targetNodeRef = null; 492 if (move) 493 { 494 if (!isSystemPath(sourceNodeRef)) 496 { 497 ChildAssociationRef newAssocRef = nodeService.moveNode( 499 sourceNodeRef, 500 targetParentRef, 501 assocRef.getTypeQName(), 502 qname); 503 targetNodeRef = newAssocRef.getChildRef(); 504 } 505 else 506 { 507 targetNodeRef = sourceNodeRef; 509 } 510 } 511 else 512 { 513 targetNodeRef = copyService.copy( 515 sourceNodeRef, 516 targetParentRef, 517 assocRef.getTypeQName(), 518 qname, 519 true); 520 } 521 nodeService.setProperty(targetNodeRef, ContentModel.PROP_NAME, newName); 523 524 FileInfo afterFileInfo = toFileInfo(targetNodeRef); 526 if (logger.isDebugEnabled()) 528 { 529 logger.debug("" + (move ? "Moved" : "Copied") + " node: \n" + 530 " parent: " + targetParentRef + "\n" + 531 " before: " + beforeFileInfo + "\n" + 532 " after: " + afterFileInfo); 533 } 534 return afterFileInfo; 535 } 536 537 545 private boolean isSystemPath(NodeRef nodeRef) 546 { 547 Path path = nodeService.getPath(nodeRef); 548 String prefixedPath = path.toPrefixString(namespaceService); 549 return systemPaths.contains(prefixedPath); 550 } 551 552 public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException 553 { 554 boolean isFolder = false; 556 try 557 { 558 isFolder = isFolder(typeQName); 559 } 560 catch (InvalidTypeException e) 561 { 562 throw new AlfrescoRuntimeException("The type is not supported by this service: " + typeQName); 563 } 564 565 checkExists(parentNodeRef, name); 567 568 Map <QName, Serializable > properties = new HashMap <QName, Serializable >(11); 570 properties.put(ContentModel.PROP_NAME, (Serializable ) name); 571 if (!isFolder) 572 { 573 String mimetype = mimetypeService.guessMimetype(name); 575 ContentData contentData = new ContentData(null, mimetype, 0L, "UTF-8"); 576 properties.put(ContentModel.PROP_CONTENT, contentData); 577 } 578 579 QName qname = QName.createQName( 581 NamespaceService.CONTENT_MODEL_1_0_URI, 582 QName.createValidLocalName(name)); 583 ChildAssociationRef assocRef = nodeService.createNode( 584 parentNodeRef, 585 ContentModel.ASSOC_CONTAINS, 586 qname, 587 typeQName, 588 properties); 589 NodeRef nodeRef = assocRef.getChildRef(); 590 FileInfo fileInfo = toFileInfo(nodeRef); 591 if (logger.isDebugEnabled()) 593 { 594 FileInfo parentFileInfo = toFileInfo(parentNodeRef); 595 logger.debug("Created: \n" + 596 " parent: " + parentFileInfo + "\n" + 597 " created: " + fileInfo); 598 } 599 return fileInfo; 600 } 601 602 public void delete(NodeRef nodeRef) 603 { 604 nodeService.deleteNode(nodeRef); 605 } 606 607 public FileInfo makeFolders(NodeRef parentNodeRef, List <String > pathElements, QName folderTypeQName) 608 { 609 if (pathElements.size() == 0) 610 { 611 throw new IllegalArgumentException ("Path element list is empty"); 612 } 613 614 boolean isFolder = isFolder(folderTypeQName); 616 if (!isFolder) 617 { 618 throw new IllegalArgumentException ("Type is invalid to make folders with: " + folderTypeQName); 619 } 620 621 NodeRef currentParentRef = parentNodeRef; 622 FileInfo lastFileInfo = null; 624 for (String pathElement : pathElements) 625 { 626 try 627 { 628 FileInfo createdFileInfo = create(currentParentRef, pathElement, folderTypeQName); 630 currentParentRef = createdFileInfo.getNodeRef(); 631 lastFileInfo = createdFileInfo; 632 } 633 catch (FileExistsException e) 634 { 635 List <FileInfo> fileInfos = search(currentParentRef, pathElement, false, true, false); 637 if (fileInfos.size() == 0) 638 { 639 throw new AlfrescoRuntimeException("Path element has just been removed: " + pathElement); 641 } 642 currentParentRef = fileInfos.get(0).getNodeRef(); 643 lastFileInfo = fileInfos.get(0); 644 } 645 } 646 return lastFileInfo; 648 } 649 650 public List <FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException 651 { 652 if (rootNodeRef == null) 654 { 655 rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef()); 656 } 657 try 658 { 659 List <FileInfo> results = new ArrayList <FileInfo>(10); 660 Path path = nodeService.getPath(nodeRef); 662 boolean foundRoot = false; 664 for (Path.Element element : path) 665 { 666 Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element; 668 NodeRef childNodeRef = assocElement.getRef().getChildRef(); 669 if (childNodeRef.equals(rootNodeRef)) 670 { 671 foundRoot = true; 673 continue; 674 } 675 else if (!foundRoot) 676 { 677 continue; 679 } 680 FileInfo pathInfo = toFileInfo(childNodeRef); 682 results.add(pathInfo); 683 } 684 if (!foundRoot || results.size() == 0) 686 { 687 throw new FileNotFoundException(nodeRef); 688 } 689 if (logger.isDebugEnabled()) 691 { 692 logger.debug("Built name path for node: \n" + 693 " root: " + rootNodeRef + "\n" + 694 " node: " + nodeRef + "\n" + 695 " path: " + results); 696 } 697 return results; 698 } 699 catch (InvalidNodeRefException e) 700 { 701 throw new FileNotFoundException(nodeRef); 702 } 703 } 704 705 public FileInfo resolveNamePath(NodeRef rootNodeRef, List <String > pathElements) throws FileNotFoundException 706 { 707 if (pathElements.size() == 0) 708 { 709 throw new IllegalArgumentException ("Path elements list is empty"); 710 } 711 NodeRef parentNodeRef = rootNodeRef; 713 StringBuilder currentPath = new StringBuilder (pathElements.size() * 20); 714 int folderCount = pathElements.size() - 1; 715 for (int i = 0; i < folderCount; i++) 716 { 717 String pathElement = pathElements.get(i); 718 FileInfo pathElementInfo = getPathElementInfo(currentPath, rootNodeRef, parentNodeRef, pathElement, true); 719 parentNodeRef = pathElementInfo.getNodeRef(); 720 } 721 String pathElement = pathElements.get(pathElements.size() - 1); 723 FileInfo result = getPathElementInfo(currentPath, rootNodeRef, parentNodeRef, pathElement, false); 724 if (logger.isDebugEnabled()) 726 { 727 logger.debug("Resoved path element: \n" + 728 " root: " + rootNodeRef + "\n" + 729 " path: " + currentPath + "\n" + 730 " node: " + result); 731 } 732 return result; 733 } 734 735 738 private FileInfo getPathElementInfo( 739 StringBuilder currentPath, 740 NodeRef rootNodeRef, 741 NodeRef parentNodeRef, 742 String pathElement, 743 boolean folderOnly) throws FileNotFoundException 744 { 745 currentPath.append("/").append(pathElement); 746 747 boolean includeFiles = (folderOnly ? false : true); 748 List <FileInfo> pathElementInfos = search(parentNodeRef, pathElement, includeFiles, true, false); 749 if (pathElementInfos.size() == 0) 751 { 752 StringBuilder sb = new StringBuilder (128); 753 sb.append(folderOnly ? "Folder" : "File or folder").append(" not found: \n") 754 .append(" root: ").append(rootNodeRef).append("\n") 755 .append(" path: ").append(currentPath); 756 throw new FileNotFoundException(sb.toString()); 757 } 758 else if (pathElementInfos.size() > 1) 759 { 760 StringBuilder sb = new StringBuilder (128); 762 sb.append("Duplicate file or folder found: \n") 763 .append(" root: ").append(rootNodeRef).append("\n") 764 .append(" path: ").append(currentPath); 765 logger.warn(sb); 766 } 767 FileInfo pathElementInfo = pathElementInfos.get(0); 768 return pathElementInfo; 769 } 770 771 public FileInfo getFileInfo(NodeRef nodeRef) 772 { 773 try 774 { 775 return toFileInfo(nodeRef); 776 } 777 catch (InvalidTypeException e) 778 { 779 return null; 780 } 781 } 782 783 public ContentReader getReader(NodeRef nodeRef) 784 { 785 FileInfo fileInfo = toFileInfo(nodeRef); 786 if (fileInfo.isFolder()) 787 { 788 throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo); 789 } 790 return contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); 791 } 792 793 public ContentWriter getWriter(NodeRef nodeRef) 794 { 795 FileInfo fileInfo = toFileInfo(nodeRef); 796 if (fileInfo.isFolder()) 797 { 798 throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo); 799 } 800 return contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); 801 } 802 } 803 | Popular Tags |