/*
* Copyright (c) 2006, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.registry.cmis;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.Updatability;
import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.registry.cmis.util.CMISConstants;
import org.wso2.carbon.registry.cmis.util.CommonUtil;
import org.wso2.carbon.registry.cmis.util.PropertyHelper;
import org.wso2.carbon.registry.core.Collection;
import org.wso2.carbon.registry.core.CollectionImpl;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.cmis.impl.DocumentTypeHandler;
import org.wso2.carbon.registry.cmis.impl.FolderTypeHandler;
import java.util.*;
/**
* Instances of this class represent a cmis:folder backed by an underlying Registry <code>Node</code>.
*/
public class RegistryFolder extends RegistryObject {
private static final Logger log = LoggerFactory.getLogger(RegistryFolder.class);
public RegistryFolder(Registry repository, Resource node, RegistryTypeManager typeManager, PathManager pathManager) {
super(repository, node, typeManager, pathManager);
}
/**
* See CMIS 1.0 section 2.2.3.1 getChildren
*
* @return Iterator of <code>GregNode</code>. Children which are created in the checked out
* state are left out from the iterator.???????????
* @throws CmisRuntimeException
*
* TODO
* Not sure about the spec
* Do I have to give every resource (incl. Collections) except the checked out resources???
*/
public Iterator<RegistryObject> getNodes() {
try {
String[] children = getNode().getChildren();
List<String> list = new ArrayList<String>();
for(String child:children){
Resource resource = null;
try{
resource = getRepository().get(child);
} catch (RegistryException e){
String msg = "Failed to get the child " + child;
log.error(msg, e);
throw new CmisObjectNotFoundException(msg, e);
}
if (hasProperty(resource, CMISConstants.GREG_IS_CHECKED_OUT)){
if(resource.getProperty(CMISConstants.GREG_IS_CHECKED_OUT).equals("true")){
String property = resource.getProperty(CMISConstants.GREG_CREATED_AS_PWC);
if(property != null && property.equals("true")){
list.add(child);
}
} else{
list.add(child);
}
} else {
list.add(child); //if property doesn't exist, still add it to the list
}
}
//Sort by name
Collections.sort(list);
//Collections.reverse(list);
final Iterator<String> newListIterator = list.iterator();
Iterator<RegistryObject> gregObjects = new Iterator<RegistryObject>() {
public boolean hasNext() {
return newListIterator.hasNext();
}
public RegistryObject next() {
try {
return create(getRepository().get(newListIterator.next()));
} catch (RegistryException e) {
String msg = "Error while iterating the node list ";
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
return gregObjects;
/*
* Lots of GREG code was here. Check with GregFolder.java in the future
*
*/
}
catch (RegistryException e) {
String msg = "Failed to get the nodes ";
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.4.2 createDocumentFromSource
*
* @throws CmisStorageException
*/
public RegistryObject addNodeFromSource(RegistryDocument source, Properties properties) {
try {
String filename = source.getNodeName();
String destPath = getRepository().copy(source.getNode().getPath(), getNode().getPath() + "/" + filename);
RegistryObject gregObject = create(getRepository().get(destPath));
// overlay new properties
if (properties != null && properties.getProperties() != null) {
updateProperties(gregObject.getNode(), gregObject.getTypeId(), properties);
}
//session.save();
return gregObject;
}
catch (RegistryException e) {
String msg = "Failed to add the node " + source.getId();
log.error(msg, e);
throw new CmisStorageException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.4.14 deleteObject
*
* @throws CmisRuntimeException
*/
@Override
public void delete(boolean allVersions, boolean isPwc) {
try {
if (getNode().getChildCount()>0) {
throw new CmisConstraintException("Folder is not empty!");
} else {
super.delete(allVersions, isPwc);
}
}
catch (RegistryException e) {
String msg = "Failed to delete the object " + getNode().getPath();
log.error(msg, e);
throw new CmisRuntimeException(msg, e);
}
}
/**
* See CMIS 1.0 section 2.2.4.15 deleteTree
*
* In Greg, if we delete a collection it gets deleted. No worries.
* Nothing will fail to delete.
* TODO
* Check whether checkedOut resources are deleted or not
*/
public FailedToDeleteDataImpl deleteTree() {
FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
result.setIds(Collections.<String>emptyList());
String id = getId();
try {
String path = getNode().getPath();
getRepository().delete(path);
} catch (RegistryException e) {
log.error("Failed to delete the node with path " + getNode().getPath() , e);
}
return result;
}
@Override
protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
throws RegistryException {
super.compileProperties(properties, filter, objectInfo);
objectInfo.setHasContent(false);
objectInfo.setSupportsDescendants(true);
objectInfo.setSupportsFolderTree(true);
String typeId = getTypeIdInternal();
addPropertyString(properties, typeId, filter, PropertyIds.PATH, getNode().getPath());
// folder properties
/*
* TODO
*
* */
//The PARENT_ID is "not set" since all my types are base types. See spec 2.1.3.2.1
//if (pathManager.isRoot(getNode())) {
// objectInfo.setHasParent(false);
//}
//else {
// objectInfo.setHasParent(true);
//}
//ParentId must be set for all folder objects except for root folder
if(pathManager.isRoot(getNode())){
addPropertyId(properties, typeId, filter, PropertyIds.PARENT_ID, CMISConstants.GREG_PROPERTY_NOT_SET);
} else{
addPropertyId(properties, typeId, filter, PropertyIds.PARENT_ID, getNode().getPath());
}
//Allowable child object type ids
List<String> allowableChildObjectTypeIds = new ArrayList<String>();
allowableChildObjectTypeIds.add(RegistryTypeManager.FOLDER_TYPE_ID);
allowableChildObjectTypeIds.add(RegistryTypeManager.DOCUMENT_TYPE_ID);
addPropertyId(properties, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, allowableChildObjectTypeIds);
}
@Override
protected Set<Action> compileAllowableActions(Set<Action> aas) {
Set<Action> result = super.compileAllowableActions(aas);
setAction(result, Action.CAN_GET_DESCENDANTS, true);
setAction(result, Action.CAN_GET_CHILDREN, true);
setAction(result, Action.CAN_GET_FOLDER_PARENT, !pathManager.isRoot(getNode()));
setAction(result, Action.CAN_GET_OBJECT_PARENTS, !pathManager.isRoot(getNode()));
setAction(result, Action.CAN_GET_FOLDER_TREE, true);
setAction(result, Action.CAN_CREATE_DOCUMENT, true);
setAction(result, Action.CAN_CREATE_FOLDER, true);
if(getNode().getPath().equals("/")) {
setAction(result, Action.CAN_DELETE_TREE, false);
} else {
setAction(result, Action.CAN_DELETE_TREE, true);
}
return result;
}
@Override
protected Collection getContextNode() {
return getNode();
}
@Override
protected String getObjectId() throws RegistryException {
return isRoot() ? PathManager.CMIS_ROOT_ID : super.getObjectId();
}
@Override
protected BaseTypeId getBaseTypeId() {
return BaseTypeId.CMIS_FOLDER;
}
@Override
protected String getTypeIdInternal() {
return RegistryTypeManager.FOLDER_TYPE_ID;
}
@Override
public Collection getNode(){
return (Collection)(super.getNode());
}
public static void setProperties(Registry repository, Resource node, TypeDefinition type, Properties properties) {
if (properties == null || properties.getProperties() == null) {
throw new CmisConstraintException("No properties!");
}
Set<String> addedProps = new HashSet<String>();
try {
// check if all required properties are there
for (PropertyData<?> prop : properties.getProperties().values()) {
PropertyDefinition<?> propDef = type.getPropertyDefinitions().get(prop.getId());
// do we know that property?
if (propDef == null) {
throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
}
// skip type id
if (propDef.getId().equals(PropertyIds.OBJECT_TYPE_ID)) {
log.warn("Cannot set " + PropertyIds.OBJECT_TYPE_ID + ". Ignoring");
addedProps.add(prop.getId());
continue;
}
// skip content stream file name
if (propDef.getId().equals(PropertyIds.CONTENT_STREAM_FILE_NAME)) {
log.warn("Cannot set " + PropertyIds.CONTENT_STREAM_FILE_NAME + ". Ignoring");
addedProps.add(prop.getId());
continue;
}
// can it be set?
if (propDef.getUpdatability() == Updatability.READONLY) {
throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
}
// empty properties are invalid
if (PropertyHelper.isPropertyEmpty(prop)) {
throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
}
// add it
CommonUtil.setProperty(repository, node, prop);
addedProps.add(prop.getId());
}
// check if required properties are missing and try to add default values if defined
for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
if (!addedProps.contains(propDef.getId()) && propDef.getUpdatability() != Updatability.READONLY) {
PropertyData<?> prop = PropertyHelper.getDefaultValue(propDef);
if (prop == null && propDef.isRequired()) {
throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
} else if (prop != null) {
CommonUtil.setProperty(repository, node, prop);
}
}
}
}
catch (RegistryException e) {
String msg = "Failed to set the properties ";
log.error(msg, e);
throw new CmisStorageException(msg, e);
}
}
@Override
protected RegistryObject create(Resource resource) {
if (resource instanceof CollectionImpl){
FolderTypeHandler handler = new FolderTypeHandler(getRepository(), pathManager, typeManager);
return handler.getGregNode(resource);
} else{
DocumentTypeHandler documentTypeHandler = new DocumentTypeHandler(getRepository(),pathManager,typeManager);
try {
return documentTypeHandler.getGregNode(resource);
} catch (RegistryException e) {
log.error("Unable to create the resource ", e);
}
}
return null;
}
}