/** @see org.olat.core.util.vfs.VFSItem#resolveFile(java.lang.String) */ public static VFSItem resolveFile(VFSContainer rootContainer, String path) { path = VFSManager.sanitizePath(path); if (path.equals("/")) { // slash or empty path -> return this vfsitem return rootContainer; } // The following code block eliminates directory scans on file-systems, // which are done in the original code in the next block, which is left // there as a fall-back in case there are non-file-system implementations // of OLAT-VFS. // OLAT file-systems can be very large and directories can contain // quite numerous files. Scanning these can take a lot of time. // Just put together the paths of both arguments // and ask the file-system whether such an entry // exists. If yes, this entry must be exactly what is // to be returned as, the proper type of, VFSItem. if (rootContainer instanceof LocalFolderImpl) { String childName = extractChild(path); LocalFolderImpl l = (LocalFolderImpl) rootContainer; File t = new File(l.getBasefile().getAbsolutePath(), childName); if (t.exists()) { String bcroot = FolderConfig.getCanonicalRoot(); String fsPath = t.getAbsolutePath(); if (t.isDirectory()) { VFSContainer subContainer; if (fsPath.startsWith(bcroot)) { fsPath = fsPath.substring(bcroot.length(), fsPath.length()); subContainer = new OlatRootFolderImpl(fsPath, rootContainer); } else { subContainer = new LocalFolderImpl(t, rootContainer); } String subPath = path.substring(childName.length() + 1); return resolveFile(subContainer, subPath); } else { if (fsPath.startsWith(bcroot)) { fsPath = fsPath.replace(bcroot, ""); return new OlatRootFileImpl(fsPath, rootContainer); } else { return new LocalFileImpl(t, rootContainer); } } } else { return null; } } // leave original code block as fall-back for non-file-system-based implementations String childName = extractChild(path); List<VFSItem> children = rootContainer.getItems(); for (VFSItem child : children) { String curName = child.getName(); if (childName.equals(curName)) { // found , let child further resolve if needed return child.resolve(path.substring(childName.length() + 1)); } } return null; }
private static ContainerAndFile findWritableRootFolderForRecursion( VFSContainer rootDir, String relFilePath, int recursionLevel) { recursionLevel++; if (recursionLevel > 20) { // Emergency exit condition: a directory hierarchy that has more than 20 // levels? Probably not.. log.warn( "Reached recursion level while finding writable root Folder - most likely a bug. rootDir::" + rootDir + " relFilePath::" + relFilePath); return null; } if (rootDir instanceof NamedContainerImpl) { rootDir = ((NamedContainerImpl) rootDir).getDelegate(); } if (rootDir instanceof MergeSource) { MergeSource mergedDir = (MergeSource) rootDir; // first check if the next level is not a second MergeSource int stop = relFilePath.indexOf("/", 1); if (stop > 0) { String nextLevel = extractChild(relFilePath); VFSItem item = mergedDir.resolve(nextLevel); if (item instanceof NamedContainerImpl) { item = ((NamedContainerImpl) item).getDelegate(); } if (item instanceof MergeSource) { rootDir = (MergeSource) item; relFilePath = relFilePath.substring(stop); return findWritableRootFolderForRecursion(rootDir, relFilePath, recursionLevel); } } VFSContainer rootWriteContainer = mergedDir.getRootWriteContainer(); if (rootWriteContainer == null) { // we have a merge source without a write container, try it one higher, // go through all children of this one and search the correct child in // the path List<VFSItem> children = rootDir.getItems(); if (children.isEmpty()) { // ups, a merge source without children, no good, return null return null; } String nextChildName = relFilePath.substring(1, relFilePath.indexOf("/", 1)); for (VFSItem child : children) { // look up for the next child in the path if (child.getName().equals(nextChildName)) { // use this child as new root and remove the child name from the rel // path if (child instanceof VFSContainer) { rootDir = (VFSContainer) child; relFilePath = relFilePath.substring(relFilePath.indexOf("/", 1)); break; } else { // ups, a merge source with a child that is not a VFSContainer - // no good, return null return null; } } } } else { // ok, we found a merge source with a write container rootDir = rootWriteContainer; } } if (rootDir != null && rootDir instanceof LocalFolderImpl) { // finished, we found a local folder we can use to write return new ContainerAndFile(rootDir, relFilePath); } else { // do recursion return findWritableRootFolderForRecursion(rootDir, relFilePath, recursionLevel); } }
/** * Get the path as string of the given item relative to the root container and the relative base * path * * @param item the item for which the relative path should be returned * @param rootContainer The root container for which the relative path should be calculated * @param relativeBasePath when NULL, the path will be calculated relative to the rootContainer; * when NOT NULL, the relativeBasePath must represent a relative path within the root * container that serves as the base. In this case, the calculated relative item path will * start from this relativeBasePath * @return */ public static String getRelativeItemPath( VFSItem item, VFSContainer rootContainer, String relativeBasePath) { // 1) Create path absolute to the root container if (item == null) return null; String absPath = ""; VFSItem tmpItem = item; // Check for merged containers to fix problems with named containers, see OLAT-3848 List<NamedContainerImpl> namedRootChilds = new ArrayList<NamedContainerImpl>(); for (VFSItem rootItem : rootContainer.getItems()) { if (rootItem instanceof NamedContainerImpl) { namedRootChilds.add((NamedContainerImpl) rootItem); } } // Check if root container is the same as the item and vice versa. It is // necessary to perform the check on both containers to catch all potential // cases with MergedSource and NamedContainer where the check in one // direction is not necessarily the same as the opposite check while (tmpItem != null && !rootContainer.isSame(tmpItem) && !tmpItem.isSame(rootContainer)) { String itemFileName = tmpItem.getName(); // fxdiff FXOLAT-125: virtual file system for CP if (tmpItem instanceof NamedLeaf) { itemFileName = ((NamedLeaf) tmpItem).getDelegate().getName(); } // Special case: check if this is a named container, see OLAT-3848 for (NamedContainerImpl namedRootChild : namedRootChilds) { if (namedRootChild.isSame(tmpItem)) { itemFileName = namedRootChild.getName(); } } absPath = "/" + itemFileName + absPath; tmpItem = tmpItem.getParentContainer(); if (tmpItem != null) { // test if this this is a merge source child container, see OLAT-5726 VFSContainer grandParent = tmpItem.getParentContainer(); if (grandParent instanceof MergeSource) { MergeSource mergeGrandParent = (MergeSource) grandParent; if (mergeGrandParent.isContainersChild((VFSContainer) tmpItem)) { // skip this parent container and use the merge grand-parent // instead, otherwise path contains the container twice tmpItem = mergeGrandParent; } } } } if (relativeBasePath == null) { return absPath; } // 2) Compute rel path to base dir of the current file // selpath = /a/irwas/subsub/nochsub/note.html 5 // filenam = /a/irwas/index.html 3 // --> subsub/nochsub/note.gif // or /a/irwas/bla/index.html // to /a/other/b/gugus.gif // --> ../../ other/b/gugus.gif // or /a/other/b/main.html // to /a/irwas/bla/goto.html // --> ../../ other/b/gugus.gif String base = relativeBasePath; // assume "/" is here if (!(base.indexOf("/") == 0)) { base = "/" + base; } String[] baseA = base.split("/"); String[] targetA = absPath.split("/"); int sp = 1; for (; sp < Math.min(baseA.length, targetA.length); sp++) { if (!baseA[sp].equals(targetA[sp])) { break; } } // special case: self-reference if (absPath.equals(base)) { sp = 1; } StringBuilder buffer = new StringBuilder(); for (int i = sp; i < baseA.length - 1; i++) { buffer.append("../"); } for (int i = sp; i < targetA.length; i++) { buffer.append(targetA[i] + "/"); } buffer.deleteCharAt(buffer.length() - 1); String path = buffer.toString(); String trimmed = path; // selectedPath.substring(1); return trimmed; }