All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.annotated.AnnoCollectionResource Maven / Gradle / Ivy

Go to download

Milton Community Edition: Supports DAV level 1 and is available on Apache2 license

The newest version!
/*
 *
 * Copyright 2014 McEvoy Software Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.milton.http.annotated;

import io.milton.common.Path;
import io.milton.http.LockInfo;
import io.milton.http.LockTimeout;
import io.milton.http.LockToken;
import io.milton.http.Request;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.exceptions.NotFoundException;
import io.milton.resource.CollectionResource;
import io.milton.resource.DeletableCollectionResource;
import io.milton.resource.ExtMakeCalendarResource;
import io.milton.resource.LockingCollectionResource;
import io.milton.resource.MakeCollectionableResource;
import io.milton.resource.PutableResource;
import io.milton.resource.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import org.slf4j.LoggerFactory;

/**
 * Placeholder object to represent a node in an annotations hierachy acting as a
 * collection
 *
 * A source object (ie your pojo) is considered a collection if it can have
 * children , ie if there exists at least one @ChildOf or @ChildrenOf method
 * which has that object as its source type. Note this is keyed on the class.
 *
 * This class includes methods suitable for use in page templating logic for
 * navigating through the hierarchy.
 *
 * @author brad
 */
public class AnnoCollectionResource extends AnnoResource implements CollectionResource, PutableResource, MakeCollectionableResource, LockingCollectionResource, DeletableCollectionResource, ExtMakeCalendarResource {

	private static final org.slf4j.Logger log = LoggerFactory.getLogger(AnnoCollectionResource.class);

	/**
	 * lazy loaded list of all children of this collection
	 */
	private ResourceList children;
	/**
	 * this holds child items located single prior to the children list being
	 * populated
	 */
	private ResourceList singlyLoadedChildItems;

	public AnnoCollectionResource(final AnnotationResourceFactory outer, Object source, AnnoCollectionResource parent) {
		super(outer, source, parent);
	}

	@Override
	public Resource child(String childName) throws NotAuthorizedException, BadRequestException {

		// attempt to locate singly, ie without loading entire list of children
		// first check if it has already been loaded singly
		if (singlyLoadedChildItems != null && singlyLoadedChildItems.hasChild(childName)) {
			return singlyLoadedChildItems.get(childName);
		}

		// if children list has already been loaded then look for child in there
		if (children != null) {
			for (Resource r : children) {
				if (r.getName().equals(childName)) {
					return r;
				}
			}
		}

		// try to load singly using ChildOf annotation, if present
		// childTriValue can be null, the child source object, or a special value indicating no search
		Object childTriValue = null;
		try {
			childTriValue = annoFactory.childOfAnnotationHandler.execute(this, childName);
		} catch (NotFoundException ex) {
			log.warn("Failed to lookup child", ex);
		}
		if (childTriValue == null) {
			//return null; // definitely not found
			// well, actually. ChildOf can just apply to a certain sort of child, so if its not found
			// that doesnt mean that there might not be some othe child
			// so we can't assume in any circumstance that a null means not found. Must always fall through to ChildrenOf
		} else if (childTriValue.equals(ChildOfAnnotationHandler.NOT_ATTEMPTED)) {
			// there is no ChildOf method, so fall through to iterating over all children
		} else {
			// got one!
			AnnoResource r = (AnnoResource) childTriValue;
			if (singlyLoadedChildItems == null) {
				singlyLoadedChildItems = new ResourceList();
			}
			singlyLoadedChildItems.add(r);
			return r;
		}

		// Previously we checked in children list if it was loaded to avoid double-instantiating
		// childOf objects. Since we've got here without finding a child, we can load the
		// list of children and iterate over it.
		// We can end up iterating over the list twice, but thats because there
		// is no guarantee that getChildren is the same as children
		for (Resource r : getChildren(true)) {
			if (r.getName().equals(childName)) {
				return r;
			}
		}

		return null;
	}

	@Override
	public List getChildren() throws NotAuthorizedException, BadRequestException {
		return getResourceList();
	}

	public List getChildren(boolean isChildLookup) throws NotAuthorizedException, BadRequestException {
		return findChildren(isChildLookup);
	}

	public ResourceList getResourceList() throws NotAuthorizedException, BadRequestException {
		return findChildren(false);
	}

	public ResourceList getSubFolders() throws NotAuthorizedException, BadRequestException {
		return getResourceList().getDirs();
	}

	public ResourceList getFiles() throws NotAuthorizedException, BadRequestException {
		return getResourceList().getFiles();
	}

	protected ResourceList findChildren(boolean isChildLookup) throws NotAuthorizedException, BadRequestException {
		if (children == null) {
			initChildren(isChildLookup);
		}
		return children;
	}

	/**
	 * Called when the children list is first accessed
	 *
	 * This will create the children ResourceList and populate it with known
	 * child resources
	 *
	 * @param isChildLookup - indicates this is being called in the context of a
	 * call to load a single child by name
	 *
	 * @throws NotAuthorizedException
	 * @throws BadRequestException
	 */
	protected void initChildren(boolean isChildLookup) throws NotAuthorizedException, BadRequestException {
		children = new ResourceList();
		Set set;
		try {
			set = annoFactory.childrenOfAnnotationHandler.execute(this, isChildLookup);
		} catch (NotAuthorizedException e) {
			throw e;
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
		children.addAll(set);

		// Now add any temp lock resources
		for (LockHolder holder : annoFactory.getTempResourcesForParent(this)) {
			CommonResource cr = annoFactory.instantiate(holder, this);
			children.add(cr);
		}

		// if there are singly loaded items we must replace their dopple-ganger in children
		// because there might already be references to those resource objects elsewhere in code
		// and having two objects representing the same resource causes unpredictable chaos!!!
		if (singlyLoadedChildItems != null) {
			for (CommonResource r : singlyLoadedChildItems) {
				children.remove(r.getName());
				children.add(r);
			}
		}
	}

	public Map getChildrenMap() throws NotAuthorizedException, BadRequestException {
		return getResourceList().getMap();
	}

	public Map getChildrenOfType() throws NotAuthorizedException, BadRequestException {
		return getResourceList().getOfType();
	}

	@Override
	public CollectionResource createCollection(String newName) throws NotAuthorizedException, ConflictException, BadRequestException {
		Object newlyCreatedSource = annoFactory.makCollectionAnnotationHandler.execute(this, newName);
		AnnoCollectionResource r = new AnnoCollectionResource(annoFactory, newlyCreatedSource, this);
		if (children != null) {
			children.add(r);
		}
		return r;
	}

	@Override
	public CollectionResource createCalendar(String newName, Map fieldsToSet) throws NotAuthorizedException, ConflictException, BadRequestException {
		Object newlyCreatedSource = annoFactory.makeCalendarAnnotationHandler.execute(this, newName, fieldsToSet);
		AnnoCollectionResource r = new AnnoCalendarResource(annoFactory, newlyCreatedSource, this);
		if (children != null) {
			children.add(r);
		}
		return r;
	}

	@Override
	public Resource createNew(String newName, InputStream inputStream, Long length, String contentType) throws IOException, ConflictException, NotAuthorizedException, BadRequestException {
		Object newChildSource = annoFactory.putChildAnnotationHandler.execute(this, newName, inputStream, length, contentType);
		AnnoCollectionResource newRes = new AnnoCollectionResource(annoFactory, newChildSource, this);
		if (children != null) {
			CommonResource oldRes = children.get(newName);
			if (oldRes != null) {
				children.remove(oldRes);
			}
			children.add(newRes);
		}
		return newRes;
	}

	@Override
	public AnnoCollectionResource getRoot() {
		if (parent == null) {
			return this;
		} else {
			return parent.getRoot();
		}
	}

	@Override
	public LockToken createAndLock(String name, LockTimeout timeout, LockInfo lockInfo) throws NotAuthorizedException {
		LockHolder r = annoFactory.createLockHolder(this, name, timeout, lockInfo);
		if (children != null) {
			CommonResource cr = annoFactory.instantiate(r, parent);
			children.add(cr);
		}
		return new LockToken(r.getId().toString(), lockInfo, timeout);
	}

	void removeLockHolder(String name) {
		if (children != null) {
			children.removeIf(r -> r instanceof LockNullResource && r.getName().equals(name));
		}
	}

	@Override
	public boolean isLockedOutRecursive(Request request) {
		return false; // TODO
	}

	public Resource find(String s) throws NotAuthorizedException, BadRequestException {
		Path p = Path.path(s);
		return findPath(p);
	}

	/**
	 * Locate a resource from the given path evaluated relative to this
	 * resource.
	 *
	 * Supports ".." and "." segments, any other strings are considered file
	 * names
	 *
	 * @param p
	 * @return
	 * @throws NotAuthorizedException
	 * @throws BadRequestException
	 */
	public Resource findPath(Path p) throws NotAuthorizedException, BadRequestException {
		Resource r = this;
		for (String segment : p.getParts()) {
			if (segment.equals("..")) {
				if (r instanceof AnnoResource) {
					AnnoResource ar = (AnnoResource) r;
					r = ar.getParent();
				} else {
					log.warn("Couldnt get parent of resource type: " + r.getClass());
					return null;
				}
			} else if (segment.equals(".")) {
				// do nothing
			} else {
				if (r instanceof CollectionResource) {
					CollectionResource col = (CollectionResource) r;
					r = col.child(segment);
				} else {
					log.warn("Couldnt find child: " + segment + " of parent: " + r.getName() + " because the parent is not actually a collection");
					return null;
				}
			}
		}
		return r;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy