/**
* 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.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.olat.core.logging.AssertException;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.vfs.callbacks.VFSSecurityCallback;
import org.olat.core.util.vfs.filters.VFSItemFilter;
/**
* Description: <br>
* TODO: Felix Jost Class Description for MultiSource
* <P>
*
* Initial Date: 23.06.2005 <br>
* @author Felix Jost
*/
public class MergeSource extends AbstractVirtualContainer {
private VFSContainer parentContainer;
private transient List<VFSContainer> mergedContainers;
private transient List<VFSContainer> mergedContainersChildren;
private VFSContainer rootWriteContainer;
private VFSSecurityCallback securityCallback;
/**
*
*/
public MergeSource(VFSContainer parentContainer, String name) {
super(name);
this.parentContainer = parentContainer;
mergedContainers = new ArrayList<VFSContainer>();
mergedContainersChildren = new ArrayList<VFSContainer>();
}
protected void init() {
if(mergedContainers == null) {
mergedContainers = new ArrayList<VFSContainer>();
}
if(mergedContainersChildren == null) {
mergedContainersChildren = new ArrayList<VFSContainer>(2);
}
}
protected void setMergedContainers(List<VFSContainer> mergedContainers) {
this.mergedContainers = mergedContainers;
}
@Override
public boolean exists() {
return true;
}
/**
* Add container to this merge source. container will show up as its name as a childe of MergeSource.
*
* @param container
*/
public void addContainer(VFSContainer container) {
addContainerToList(container, mergedContainers);
}
public void addContainerToList(VFSContainer container, List<VFSContainer> containers) {
VFSContainer newContainer = container;
if (isContainerNameTaken(newContainer.getName(), containers)) {
String newName = newContainer.getName() + "_" + CodeHelper.getRAMUniqueID();
newContainer = new NamedContainerImpl(newName, container);
}
// set default filter if container does not already have its own default filter
if (container.getDefaultItemFilter() != null) {
container.setDefaultItemFilter(defaultFilter);
newContainer.setDefaultItemFilter(defaultFilter);
}
newContainer.setParentContainer(this);
containers.add(newContainer);
}
/**
* Add all children of this container to the root of this MergeSource.
*
* @param container
* @param enableWrite If true, writes to the root of this MergeSource are directed to this container.
*/
public void addContainersChildren(VFSContainer container, boolean enableWrite) {
container.setParentContainer(this);
// set default filter if container does not already have its own default filter
if (container.getDefaultItemFilter() != null) {
container.setDefaultItemFilter(defaultFilter);
}
// add the container to the list of merged sources
mergedContainersChildren.add(container);
if (enableWrite) rootWriteContainer = container;
}
/**
* Check if the given container is semantically not a child but a container
* which items have been merged using the addContainersChildren() method.
*
* @param container
* @return
*/
public boolean isContainersChild(VFSContainer container) {
return mergedContainersChildren.contains(container);
}
/**
* @see org.olat.core.util.vfs.VFSItem#getParent()
*/
@Override
public VFSContainer getParentContainer() {
return parentContainer;
}
/**
* @see org.olat.core.util.vfs.VFSItem#setParentContainer(org.olat.core.util.vfs.VFSContainer)
*/
@Override
public void setParentContainer(VFSContainer parentContainer) {
this.parentContainer = parentContainer;
}
/**
* @see org.olat.core.util.vfs.VFSContainer#getItems()
*/
@Override
public List<VFSItem> getItems() {
return getItems(null);
}
/**
* @see org.olat.core.util.vfs.VFSContainer#getItems(org.olat.core.util.vfs.filters.VFSItemFilter)
*/
@Override
public List<VFSItem> getItems(VFSItemFilter filter) {
// remember: security callback and parent was already set during add to this MergeSource
// and refreshed on any setSecurityCallback() so no need to handle the quota of children here.
List<VFSItem> all = new ArrayList<VFSItem>();
if (filter == null && defaultFilter == null) {
all.addAll(mergedContainers);
} else {
// custom filter or default filter is set
for (VFSContainer mergedContainer : mergedContainers) {
boolean passedFilter = true;
// check for default filter
if (defaultFilter != null && ! defaultFilter.accept(mergedContainer)) passedFilter = false;
// check for custom filter
if (passedFilter && filter != null && ! filter.accept(mergedContainer)) passedFilter = false;
// only add when both filters passed the test
if (passedFilter) all.add(mergedContainer);
}
}
for (VFSContainer container: mergedContainersChildren) {
all.addAll(container.getItems(filter));
}
return all;
}
/**
* @see org.olat.core.util.vfs.VFSItem#canCopyTo()
*/
@Override
public VFSStatus canWrite() {
if (rootWriteContainer == null) return VFSConstants.NO;
return rootWriteContainer.canWrite();
}
/**
* @see org.olat.core.util.vfs.VFSContainer#createChildContainer(java.lang.String)
*/
@Override
public VFSContainer createChildContainer(String name) {
if (canWrite() != VFSConstants.YES) return null;
VFSContainer newContainer = rootWriteContainer.createChildContainer(name);
if (newContainer != null) newContainer.setDefaultItemFilter(defaultFilter);
return newContainer;
}
/**
* @see org.olat.core.util.vfs.VFSContainer#createChildLeaf(java.lang.String)
*/
@Override
public VFSLeaf createChildLeaf(String name) {
if (canWrite() != VFSConstants.YES) return null;
return rootWriteContainer.createChildLeaf(name);
}
/**
* @see org.olat.core.util.vfs.VFSContainer#copyFrom(org.olat.core.util.vfs.VFSItem)
*/
@Override
public VFSStatus copyFrom(VFSItem source) {
if (canWrite() != VFSConstants.YES) throw new AssertException("Cannot create child container in merge source if not writable.");
return rootWriteContainer.copyFrom(source);
}
/**
* @see org.olat.core.util.vfs.VFSItem#resolveFile(java.lang.String)
*/
@Override
public VFSItem resolve(String path) {
path = VFSManager.sanitizePath(path);
if (path.equals("/")) return this;
String childName = VFSManager.extractChild(path);
String nextPath = path.substring(childName.length() + 1);
// simple case
for (VFSContainer container:mergedContainers) {
if (container.getName().equals(childName)) {
VFSItem vfsItem = container.resolve(nextPath);
// set default filter on resolved file if it is a container
if (vfsItem != null && vfsItem instanceof VFSContainer) {
VFSContainer resolvedContainer = (VFSContainer) vfsItem;
resolvedContainer.setDefaultItemFilter(defaultFilter);
}
return vfsItem;
}
}
// check delegates
for (VFSContainer container:mergedContainers) {
// A namedContainer doesn't match with its own getName()! -> work with delegate
boolean nameMatch = container.getName().equals(childName);
if (container instanceof NamedContainerImpl && !nameMatch) {
// Special case: sometimes the path refers to the named containers
// delegate container, so try this one as well
container = ((NamedContainerImpl) container).getDelegate();
if(container == null) {
// in case a corrupted course
continue;
}
String name = container.getName();
if (name == null) {
// FXOLAT-195 The delegate of the named container does not
// have a name, so abort the special case and continue with
// next container
continue;
}
nameMatch = name.equals(childName);
}
if (nameMatch) {
VFSItem vfsItem = container.resolve(nextPath);
// set default filter on resolved file if it is a container
if (vfsItem != null && vfsItem instanceof VFSContainer) {
VFSContainer resolvedContainer = (VFSContainer) vfsItem;
resolvedContainer.setDefaultItemFilter(defaultFilter);
}
return vfsItem;
}
}
for (VFSContainer container : mergedContainersChildren) {
// A namedContainer doesn't match with its own getName()! -> work with delegate
if (container instanceof NamedContainerImpl) {
container = ((NamedContainerImpl) container).getDelegate();
}
VFSItem vfsItem = container.resolve(path);
if (vfsItem != null) {
// set default filter on resolved file if it is a container
if (vfsItem instanceof VFSContainer) {
VFSContainer resolvedContainer = (VFSContainer) vfsItem;
resolvedContainer.setDefaultItemFilter(defaultFilter);
}
return vfsItem;
}
}
return null;
}
/**
* @see org.olat.core.util.vfs.VFSItem#getLocalSecurityCallback()
*/
@Override
public VFSSecurityCallback getLocalSecurityCallback() {
return securityCallback;
}
/**
* @see org.olat.core.util.vfs.VFSItem#setLocalSecurityCallback(org.olat.core.util.vfs.callbacks.VFSSecurityCallback)
*/
@Override
public void setLocalSecurityCallback(VFSSecurityCallback secCallback) {
securityCallback = secCallback;
}
@Override
public boolean isSame(VFSItem vfsItem) {
if (rootWriteContainer == null) {
// Unwriteable merge source (e.g. users private folder), compare on object identity
return this.equals(vfsItem);
}
if (vfsItem instanceof MergeSource) {
// A writeable merge source, compare on writeable root container
return rootWriteContainer.equals(((MergeSource)vfsItem).rootWriteContainer);
}
return rootWriteContainer.equals(vfsItem);
}
public VFSContainer getRootWriteContainer() {
return rootWriteContainer;
}
private boolean isContainerNameTaken(String containerName, List<VFSContainer> containers) {
for (Iterator<VFSContainer> iter = containers.iterator(); iter.hasNext();) {
VFSContainer container = iter.next();
if (container.getName().equals(containerName)) {
return true;
}
}
return false;
}
}