/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.util.vfs;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.commons.modules.bc.vfs.OlatRootFileImpl;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.vfs.callbacks.VFSSecurityCallback;
import org.olat.core.util.vfs.util.ContainerAndFile;
public class VFSManager {
private static final OLog log = Tracing.createLoggerFor(VFSManager.class);
/**
* Make sure we always have a path that starts with a "/".
*
* @param path
* @return
*/
public static String sanitizePath(String path) {
// check for "empty" paths
if (path == null || path.length() == 0) return "/";
// prepend "/" if missing
if (path.charAt(0) != '/') path = "/" + path;
// cut trailing slash if any
if (path.length() > 1 && path.charAt(path.length() - 1) == '/')
path = path.substring(0, path.length() - 1);
return path;
}
/**
* Extract the next subfolder (e.g. /foo/bla/gnu.txt -> "foo"
* PRE: a sanitized path, has a child
* @param path
* @return Next child.
*/
public static String extractChild(String path) {
int slPos = path.indexOf('/', 1);
String childName = null;
if (slPos == -1) { // no subpath
childName = path.substring(1);
} else {
childName = path.substring(1, slPos);
}
return childName;
}
/**
* Check if descendant is indeed a descendant of root..
* @param parent
* @param child
* @return
*/
public static boolean isContainerDescendantOrSelf(VFSContainer descendant, VFSContainer root) {
if (root.isSame(descendant)) return true;
VFSContainer parentContainer = descendant.getParentContainer();
while (parentContainer != null) {
if (parentContainer.isSame(root)) return true;
parentContainer = parentContainer.getParentContainer();
}
return false;
}
/**
* Check if descendant is child of parent or same as parent.
* @param descendant
* @param root
* @return
*/
public static boolean isSelfOrParent(VFSContainer descendant, VFSContainer parent) {
if (parent.isSame(descendant)) return true;
VFSContainer parentContainer = descendant.getParentContainer();
if (parentContainer!=null && parentContainer.isSame(parent)) return true;
return false;
}
public static boolean isDirectoryAndNotEmpty(VFSItem directory){
if(directory instanceof VFSContainer) {
List<VFSItem> children = ((VFSContainer)directory).getItems();
return !children.isEmpty();
}
return false;
}
/**
* @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;
}
/**
* Resolves a directory path in the base container or creates this
* directory. The method creates any missing directories.
*
* @param baseContainer
* The base directory. User must have write permissions on this
* container
* @param relContainerPath
* The path relative to the base container. Must start with a
* '/'. To separate sub directories use '/'
* @return The resolved or created container or NULL if a problem happened
*/
public static VFSContainer resolveOrCreateContainerFromPath(VFSContainer baseContainer, String relContainerPath) {
VFSContainer resultContainer = baseContainer;
if (!VFSConstants.YES.equals(baseContainer.canWrite())) {
log.error("Could not create relPath::" + relContainerPath + ", base container::" + getRealPath(baseContainer) + " not writable", null);
resultContainer = null;
} else if (StringHelper.containsNonWhitespace(relContainerPath)){
// Try to resolve given rel path from current container
VFSItem resolvedPath = baseContainer.resolve(relContainerPath.trim());
if (resolvedPath == null) {
// Does not yet exist - create subdir
String[] pathSegments = relContainerPath.split("/");
for (int i = 0; i < pathSegments.length; i++) {
String segment = pathSegments[i].trim();
if (StringHelper.containsNonWhitespace(segment)) {
resolvedPath = resultContainer.resolve(segment);
if (resolvedPath == null) {
resultContainer = resultContainer.createChildContainer(segment);
if (resultContainer == null) {
log.error("Could not create container with name::" + segment + " in relPath::" + relContainerPath + " in base container::" + getRealPath(baseContainer), null);
break;
}
} else {
if (resolvedPath instanceof VFSContainer) {
resultContainer = (VFSContainer) resolvedPath;
} else {
resultContainer = null;
log.error("Could not create container with name::" + segment + " in relPath::" + relContainerPath + ", a file with this name exists (but not a directory) in base container::" + getRealPath(baseContainer), null);
break;
}
}
}
}
} else {
// Parent dir already exists, make sure this is really a container and not a file!
if (resolvedPath instanceof VFSContainer) {
resultContainer = (VFSContainer) resolvedPath;
} else {
resultContainer = null;
log.error("Could not create relPath::" + relContainerPath + ", a file with this name exists (but not a directory) in base container::" + getRealPath(baseContainer), null);
}
}
}
return resultContainer;
}
/**
* Resolves a file path in the base container or creates this file under the
* given path. The method creates any missing directories.
*
* @param baseContainer
* The base directory. User must have write permissions on this
* container
* @param relFilePath
* The path relative to the base container. Must start with a
* '/'. To separate sub directories use '/'
* @return The resolved or created leaf or NULL if a problem happened
*/
public static VFSLeaf resolveOrCreateLeafFromPath(VFSContainer baseContainer, String relFilePath) {
if (StringHelper.containsNonWhitespace(relFilePath)) {
int lastSlash = relFilePath.lastIndexOf("/");
String relDirPath = relFilePath;
String fileName = null;
if (lastSlash == -1) {
// relFilePath is the file name - no directories involved
relDirPath = null;
fileName = relFilePath;
} else if (lastSlash == 0) {
// Remove start slash from file name
relDirPath = null;
fileName = relFilePath.substring(1, relFilePath.length());
} else {
relDirPath = relFilePath.substring(0, lastSlash);
fileName = relFilePath.substring(lastSlash);
}
// Create missing directories and set parent dir for later file creation
VFSContainer parent = baseContainer;
if (StringHelper.containsNonWhitespace(relDirPath)) {
parent = resolveOrCreateContainerFromPath(baseContainer, relDirPath);
}
// Now create file in that dir
if (StringHelper.containsNonWhitespace(fileName)) {
VFSLeaf leaf = null;
VFSItem resolvedFile = parent.resolve(fileName);
if (resolvedFile == null) {
leaf = parent.createChildLeaf(fileName);
if (leaf == null) {
log.error("Could not create leaf with relPath::" + relFilePath + " in base container::" + getRealPath(baseContainer), null);
}
} else {
if (resolvedFile instanceof VFSLeaf) {
leaf = (VFSLeaf) resolvedFile;
} else {
leaf = null;
log.error("Could not create relPath::" + relFilePath + ", a directory with this name exists (but not a file) in base container::" + getRealPath(baseContainer), null);
}
}
return leaf;
}
}
return null;
}
/**
* Get the security callback which affects this item. This searches up the path
* of parents to see wether it can find any callback. If no callback
* can be found, null is returned.
*
* @param vfsItem
* @return
*/
public static VFSSecurityCallback findInheritedSecurityCallback(VFSItem vfsItem) {
VFSItem inheritingItem = findInheritingSecurityCallbackContainer(vfsItem);
if (inheritingItem != null) return inheritingItem.getLocalSecurityCallback();
return null;
}
/**
* Get the container which security callback affects this item. This searches up the path
* of parents to see wether it can find any container with a callback. If no callback
* can be found, null is returned.
*
* @param vfsItem
* @return
*/
public static VFSContainer findInheritingSecurityCallbackContainer(VFSItem vfsItem) {
if (vfsItem == null) return null;
// first resolve delegates of any NamedContainers to get the actual container (might be a MergeSource)
if (vfsItem instanceof NamedContainerImpl) return findInheritingSecurityCallbackContainer(((NamedContainerImpl)vfsItem).getDelegate());
// special treatment for MergeSource
if (vfsItem instanceof MergeSource) {
MergeSource mergeSource = (MergeSource)vfsItem;
VFSContainer rootWriteContainer = mergeSource.getRootWriteContainer();
if (rootWriteContainer != null && rootWriteContainer.getLocalSecurityCallback() != null) {
// if the root write container has a security callback set, it will always override
// any local securitycallback set on the mergesource
return rootWriteContainer;
} else if (mergeSource.getLocalSecurityCallback() != null) {
return mergeSource;
} else if (mergeSource.getParentContainer() != null) {
return findInheritingSecurityCallbackContainer(mergeSource.getParentContainer());
}
} else {
if ((vfsItem instanceof VFSContainer) && (vfsItem.getLocalSecurityCallback() != null)) return (VFSContainer)vfsItem;
if (vfsItem.getParentContainer() != null) return findInheritingSecurityCallbackContainer(vfsItem.getParentContainer());
}
return null;
}
/**
* Check wether this container has a quota assigned to itself.
*
* @param container
* @return Quota if this container has a Quota assigned, null otherwise.
*/
public static Quota isTopLevelQuotaContainer(VFSContainer container) {
VFSSecurityCallback callback = container.getLocalSecurityCallback();
if (callback != null && callback.getQuota() != null) return callback.getQuota();
// extract delegate if this is a NamedContainer instance...
if (container instanceof NamedContainerImpl) container = ((NamedContainerImpl)container).getDelegate();
// check if this is a MergeSource with a root write container
if (container instanceof MergeSource) {
VFSContainer rwContainer = ((MergeSource)container).getRootWriteContainer();
if (rwContainer != null && rwContainer.getLocalSecurityCallback() != null
&& rwContainer.getLocalSecurityCallback().getQuota() != null)
return rwContainer.getLocalSecurityCallback().getQuota();
}
return null;
}
/**
* Check the quota usage on this VFSContainer. If no security callback
* is provided, this returns -1 (meaning no quota on this folder).
* Similarly, if no quota is defined, VFSSecurityCallback.NO_QUOTA_DEFINED
* will be returned to signal no quota
* on this container.
*
* @param securityCallback
* @param container
* @return
*/
public static long getQuotaLeftKB(VFSContainer container) {
VFSContainer inheritingItem = findInheritingSecurityCallbackContainer(container);
if (inheritingItem == null || inheritingItem.getLocalSecurityCallback().getQuota() == null)
return Quota.UNLIMITED;
long usageKB = getUsageKB(inheritingItem);
return inheritingItem.getLocalSecurityCallback().getQuota().getQuotaKB().longValue() - usageKB;
}
/**
* Recursively traverse the container and sum up all leafs' sizes.
*
* @param container
* @return
*/
public static long getUsageKB(VFSItem vfsItem) {
if (vfsItem instanceof VFSContainer) {
// VFSContainer
if (vfsItem instanceof LocalFolderImpl)
return FileUtils.getDirSize(((LocalFolderImpl)vfsItem).getBasefile()) / 1024;
long usageKB = 0;
List<VFSItem> children = ((VFSContainer)vfsItem).getItems();
for (VFSItem child:children) {
usageKB += getUsageKB(child);
}
return usageKB;
} else {
// VFSLeaf
return ((VFSLeaf)vfsItem).getSize() / 1024;
}
}
/**
* Returns the real path of the given VFS container. If the container is a
* named container, the delegate container is used. If the container is a
* merge source with a writable root container, then this one is used. In
* other cases the method returns null since the given container is not
* writable to any real file.
*
* @param container
* @return String representing an absolute path for this container
*/
public static String getRealPath(VFSContainer container) {
File file = getRealFile(container);
if(file == null)
return null;
return file.getPath();
}
public static File getRealFile(VFSContainer container) {
File realFile = null;
LocalFolderImpl localFolder = null;
if (container instanceof NamedContainerImpl) {
container = ((NamedContainerImpl)container).getDelegate();
}
if (container instanceof MergeSource) {
container = ((MergeSource)container).getRootWriteContainer();
}
if (container != null && container instanceof LocalFolderImpl) {
localFolder = (LocalFolderImpl) container;
realFile = localFolder.getBasefile();
}
return realFile;
}
/**
* 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;
}
/**
* This method takes a VFSContainer and a relative path to a file that exists
* within this container. The method checks if the given container is a
* writable container that can be used e.g. by the HTML editor as a base
* directory where to store some things. If the method detects that this is
* not the case it works against the relative file path and checks each
* directory in the path. <br>
* The result will be an object array that contains the corrected container
* and the new relative path. If no writable container could be found NULL is
* returned. <br>
* Limitations: the method stops at least after 20 iterations returning NULL
*
* @param rootDir the container that should be checked
* @param relFilePath The valid file path within this container
* @return Object array that contains 1) a writable rootDir and 2) the
* corrected relFilePath that mathes to the new rootDir. Can be NULL
* if no writable root folder could be found.
*/
public static ContainerAndFile findWritableRootFolderFor(VFSContainer rootDir, String relFilePath){
int level = 0;
return findWritableRootFolderForRecursion(rootDir, relFilePath, level);
}
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);
}
}
/**
* Returns a similar but non existing file name in root based on the given
* name.
*
*
* @param root
* @param name
* @return A non existing name based on the given name in the root directory
*/
public static String similarButNonExistingName(VFSContainer root,
String name) {
VFSItem existingItem = null;
String newName = name;
existingItem = root.resolve(newName);
int i = 1;
while (existingItem != null) {
newName = FileUtils.appendNumberAtTheEndOfFilename(name, i++);
existingItem = root.resolve(newName);
}
return newName;
}
public static VFSContainer getOrCreateContainer(VFSContainer parent, String name) {
VFSItem item = parent.resolve(name);
if(item instanceof VFSContainer) {
return (VFSContainer)item;
} else if(item != null) {
return null;//problem
} else {
return parent.createChildContainer(name);
}
}
/**
* Copies the content of the source to the target leaf.
*
* @param source
* @param target
* @return True on success, false on failure
*/
public static boolean copyContent(VFSLeaf source, VFSLeaf target) {
boolean successful;
if (source != null && target != null) {
InputStream in = new BufferedInputStream(source.getInputStream());
OutputStream out = new BufferedOutputStream(target.getOutputStream(false));
// write the input to the output
try {
byte[] buf = new byte[FileUtils.BSIZE];
int i = 0;
while ((i = in.read(buf)) != -1) {
out.write(buf, 0, i);
}
successful = true;
} catch (IOException e) {
// something went wrong.
successful = false;
log.error("Error while copying content from source: " + source.getName() + " to target: " + target.getName(), e);
} finally {
// Close streams
try {
if (out != null) {
out.flush();
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
log.error("Error while closing/cleaning up in- and output streams", ex);
}
}
} else {
// source or target is null
successful = false;
if (log.isDebug()) log.debug("Either the source or the target is null. Content of leaf cannot be copied.");
}
return successful;
}
/**
* Copy the content of the source container to the target container.
* @param source
* @param target
* @return
*/
public static boolean copyContent(VFSContainer source, VFSContainer target) {
if(!source.exists()) {
return false;
}
if(isSelfOrParent(source, target)) {
return false;
}
if(source instanceof NamedContainerImpl) {
source = ((NamedContainerImpl)source).getDelegate();
}
if(target instanceof NamedContainerImpl) {
target = ((NamedContainerImpl)target).getDelegate();
}
if(source instanceof LocalImpl && target instanceof LocalImpl) {
LocalImpl localSource = (LocalImpl)source;
LocalImpl localTarget = (LocalImpl)target;
File localSourceFile = localSource.getBasefile();
File localTargetFile = localTarget.getBasefile();
return FileUtils.copyDirContentsToDir(localSourceFile, localTargetFile, false, "VFScopyDir");
}
return false;
}
/**
* Copy the content of the file in the target leaf.
* @param source A file
* @param target The target leaf
* @return
*/
public static boolean copyContent(File source, VFSLeaf target) {
try(InputStream inStream = new FileInputStream(source)) {
return copyContent(inStream, target, true);
} catch(IOException ex) {
log.error("", ex);
return false;
}
}
/**
* Copies the stream to the target leaf.
*
* @param source
* @param target
* @return True on success, false on failure
*/
public static boolean copyContent(InputStream inStream, VFSLeaf target) {
return copyContent(inStream, target, true);
}
/**
* Copies the stream to the target leaf.
*
* @param source
* @param target
* @param closeInput set to false if it's a ZipInputStream
* @return True on success, false on failure
*/
public static boolean copyContent(InputStream inStream, VFSLeaf target, boolean closeInput) {
boolean successful;
if (inStream != null && target != null) {
InputStream in = new BufferedInputStream(inStream);
OutputStream out = new BufferedOutputStream(target.getOutputStream(false));
// write the input to the output
try {
byte[] buf = new byte[FileUtils.BSIZE];
int i = 0;
while ((i = in.read(buf)) != -1) {
out.write(buf, 0, i);
}
successful = true;
} catch (IOException e) {
// something went wrong.
successful = false;
log.error("Error while copying content from source: " + inStream + " to target: " + target.getName(), e);
} finally {
// Close streams
try {
if (out != null) {
out.flush();
out.close();
}
if (closeInput && in != null) {
in.close();
}
} catch (IOException ex) {
log.error("Error while closing/cleaning up in- and output streams", ex);
}
}
} else {
// source or target is null
successful = false;
if (log.isDebug()) log.debug("Either the source or the target is null. Content of leaf cannot be copied.");
}
return successful;
}
/**
*
* @param container
* @param filename
* @return
*/
public static String rename(VFSContainer container, String filename) {
String newName = filename;
VFSItem newFile = container.resolve(newName);
for(int count=0; newFile != null && count < 999 ; ) {
count++;
newName = FileUtils.appendNumberAtTheEndOfFilename(filename, count);
newFile = container.resolve(newName);
}
if(newFile == null) {
return newName;
}
return null;
}
/**
* Check if the file exist or not
* @param item
* @return
*/
public static boolean exists(VFSItem item) {
if (item instanceof NamedContainerImpl) {
item = ((NamedContainerImpl)item).getDelegate();
}
if (item instanceof MergeSource) {
MergeSource source = (MergeSource)item;
item = source.getRootWriteContainer();
if(item == null) {
//no write container, but the virtual container exist
return true;
}
}
if(item instanceof LocalImpl) {
LocalImpl localFile = (LocalImpl)item;
return localFile.getBasefile() != null && localFile.getBasefile().exists();
}
return false;
}
}