package com.libreecm.opencmis.auxiliars;

import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.*;
import org.apache.chemistry.opencmis.commons.definitions.Choice;
import org.apache.chemistry.opencmis.commons.definitions.MutableDocumentTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutableFolderTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutableItemTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutablePolicyTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutablePropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutablePropertyIdDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutableRelationshipTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.MutableTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
import org.apache.chemistry.opencmis.commons.definitions.TypeMutability;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.chemistry.opencmis.commons.enums.*;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;

import com.libreecm.commonAPI.auxiliars.exceptions.NotDefinedPropertyDataValueException;
import com.libreecm.commonAPI.auxiliars.exceptions.NotSetPropertyDefinitionIdException;
import com.libreecm.commonAPI.model.objects.LibreECMDataObject;
import com.libreecm.commonAPI.model.objects.Uri;

public class Mapper {
	public static RepositoryInfo map(com.libreecm.commonAPI.model.Repository repository) {
		if (repository == null)
			return null;
		RepositoryInfoImpl repoInfo = new RepositoryInfoImpl();
		//System.out.println("id "+repository.getId()+" type "+repository.getTypeId()+" rootid "+repository.getRootFolderId());
		repoInfo.setId(repository.getId());
		repoInfo.setName(repository.getName());
		repoInfo.setDescription(repository.getDescription());
		repoInfo.setCmisVersionSupported(repository.getCmisVersionSupported());
		repoInfo.setRootFolder(repository.getRootFolderId());
		repoInfo.setPrincipalAnonymous(repository.getPrincipalIdAnonymous());
		repoInfo.setPrincipalAnyone(repository.getPrincipalIdAnyone());
		repoInfo.setThinClientUri(repository.getThinClientUri());
		repoInfo.setChangesIncomplete(repository.getChangesIncomplete());
		repoInfo.setChangesOnType(mapBaseTypeList(repository.getChangesOnType()));
		repoInfo.setLatestChangeLogToken(repository.getLatestChangeLogToken());
		repoInfo.setVendorName(repository.getVendorName());
		repoInfo.setProductName(repository.getProductName());
		
		repoInfo.setProductVersion(repository.getProductVersion());
		// set capabilities
		repoInfo.setCapabilities(map(repository.getRepositoryCapabilities()));

		repoInfo.setAclCapabilities(map(repository.getAclCapabilities()));

		repoInfo.setExtensions(mapExtensions(repository.getExtensions()));
		return repoInfo;
	}

	private static AclCapabilities map(com.libreecm.commonAPI.model.AclCapabilities aclCapabilities) {
		if (aclCapabilities==null)
			return null;
		AclCapabilitiesDataImpl aclCaps = new AclCapabilitiesDataImpl();
		aclCaps.setAclPropagation(map(aclCapabilities.getAclPropagation()));
		aclCaps.setPermissionDefinitionData(mapPermissionDefinition(aclCapabilities.getPermissions()));
		aclCaps.setPermissionMappingData(mapPermissionMapping(aclCapabilities.getPermissionMapping()));
		aclCaps.setExtensions(mapExtensions(aclCapabilities.getExtensions()));
		return aclCaps;
	}

	public static RepositoryCapabilitiesImpl map(com.libreecm.commonAPI.model.RepositoryCapabilities element) {
		if (element == null)
			return null;
		RepositoryCapabilitiesImpl caps = new RepositoryCapabilitiesImpl();
		caps.setAllVersionsSearchable(false);
		caps.setCapabilityAcl(map(element.getAclCapability()));
		caps.setCapabilityChanges(map(element.getChangesCapability()));

		caps.setCapabilityContentStreamUpdates(map(element.getContentStreamUpdatesCapability()));
		caps.setCapabilityJoin(map(element.getJoinCapability()));
		caps.setCapabilityQuery(map(element.getQueryCapability()));
		caps.setCapabilityRendition(map(element.getRenditionsCapability()));
		caps.setIsPwcSearchable(element.getIsPwcSearchable());
		caps.setIsPwcUpdatable(element.getIsPwcUpdatable());
		caps.setSupportsGetDescendants(element.getSupportsGetDescendants());
		caps.setSupportsGetFolderTree(element.getSupportsGetFolderTree());
		caps.setSupportsMultifiling(element.getSupportsMultifiling());
		caps.setSupportsUnfiling(element.getSupportsUnfiling());
		caps.setSupportsVersionSpecificFiling(element.getSupportsVersionSpecificFiling());
		caps.setNewTypeSettableAttributes(map(element.getNewTypeSettableAttributes()));
		
		caps.setExtensions(mapExtensions(element.getExtensions()));
		return caps;
	}

	private static NewTypeSettableAttributes map(com.libreecm.commonAPI.model.NewTypeSettableAttributes element) {
		if (element==null) return null;
		NewTypeSettableAttributesImpl result = new NewTypeSettableAttributesImpl();
		result.setCanSetControllableAcl(element.canSetControllableAcl());
		result.setCanSetControllablePolicy(element.canSetControllablePolicy());
		result.setCanSetCreatable(element.canSetCreatable());
		result.setCanSetDescription(element.canSetDescription());
		result.setCanSetDisplayName(element.canSetDisplayName());
		result.setCanSetFileable(element.canSetFileable());
		result.setCanSetFulltextIndexed(element.canSetFulltextIndexed());
		result.setCanSetId(element.canSetId());
		result.setCanSetIncludedInSupertypeQuery(element.canSetIncludedInSupertypeQuery());
		result.setCanSetLocalName(element.canSetLocalName());
		result.setCanSetLocalNamespace(element.canSetLocalNamespace());
		result.setCanSetQueryable(element.canSetQueryable());
		result.setCanSetQueryName(element.canSetQueryName());
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;
	}

	public static List<BaseTypeId> mapBaseTypeList(List<com.libreecm.commonAPI.model.enums.BaseTypeId> list) {
		if (list==null) return null;
		ArrayList<BaseTypeId> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.enums.BaseTypeId typeId : list) {
			result.add(map(typeId));
		}
		return result;
	}

	public static BaseTypeId map(com.libreecm.commonAPI.model.enums.BaseTypeId typeId) {
		if (typeId == null)
			return null;
		//System.out.println("mapping conversion of basetypeid toString "+typeId.toString()+" to value: "+typeId.value()+" and to name "+typeId.name());
		return BaseTypeId.valueOf(typeId.name());
	}

	public static CapabilityAcl map(com.libreecm.commonAPI.model.enums.CapabilityAcl capability) {
		if (capability == null)
			return null;
		return CapabilityAcl.valueOf(capability.toString());
	}

	public static CapabilityChanges map(com.libreecm.commonAPI.model.enums.CapabilityChanges capability) {
		if (capability == null)
			return null;
		return CapabilityChanges.valueOf(capability.toString());
	}

	public static CapabilityContentStreamUpdates map(
			com.libreecm.commonAPI.model.enums.CapabilityContentStreamUpdates capability) {
		if (capability == null)
			return null;
		return CapabilityContentStreamUpdates.valueOf(capability.toString());
	}

	public static CapabilityJoin map(com.libreecm.commonAPI.model.enums.CapabilityJoin capability) {
		if (capability == null)
			return null;
		return CapabilityJoin.valueOf(capability.toString());
	}

	public static CapabilityQuery map(com.libreecm.commonAPI.model.enums.CapabilityQuery capability) {
		if (capability == null)
			return null;
		return CapabilityQuery.valueOf(capability.toString());
	}

	public static CapabilityRenditions map(com.libreecm.commonAPI.model.enums.CapabilityRenditions capability) {
		if (capability == null)
			return null;
		return CapabilityRenditions.valueOf(capability.toString());
	}

	public static AclPropagation map(com.libreecm.commonAPI.model.enums.AclPropagation capability) {
		if (capability == null)
			return null;
		return AclPropagation.valueOf(capability.toString());
	}

	public static PermissionDefinition map(com.libreecm.commonAPI.model.PermissionDefinition element) {
		if (element == null)
			return null;
		PermissionDefinitionDataImpl permission = new PermissionDefinitionDataImpl();
		permission.setDescription(element.getDescription());
		permission.setExtensions(mapExtensions(element.getExtensions()));
		permission.setId(element.getPermission());
		return permission;
	}

	public static CmisExtensionElement map(com.libreecm.commonAPI.model.CmisExtensionElement element) {
		if (element == null)
			return null;
		CmisExtensionElement result;
		if (element.getValue() == null)
			result = new CmisExtensionElementImpl(element.getNamespace(), element.getName(), element.getAttributes(),
					mapExtensions(element.getChildren()));
		else
			result = new CmisExtensionElementImpl(element.getNamespace(), element.getName(), element.getAttributes(),
					element.getValue());
		return result;
	}

	public static List<CmisExtensionElement> mapExtensions(
			List<com.libreecm.commonAPI.model.CmisExtensionElement> list) {
		if (list==null) return null;
		ArrayList<CmisExtensionElement> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.CmisExtensionElement element : list) {
			result.add(map(element));
		}
		return result;
	}

	public static Map<String, PermissionMapping> mapPermissionMapping(
			Map<String, com.libreecm.commonAPI.model.PermissionMapping> list) {
		if (list == null) return null;
		HashMap<String, PermissionMapping> result = new HashMap<>();
		for (String key : list.keySet()) {
			result.put(key, map(list.get(key)));
		}
		return result;
	}

	public static PermissionMapping map(com.libreecm.commonAPI.model.PermissionMapping element) {
		if (element == null)
			return null;
		PermissionMappingDataImpl result = new PermissionMappingDataImpl();
		result.setExtensions(mapExtensions(element.getExtensions()));
		result.setKey(element.getKey());
		result.setPermissions(element.getPermissions());
		return result;
	}

	public static List<PermissionDefinition> mapPermissionDefinition(
			List<com.libreecm.commonAPI.model.PermissionDefinition> list) {
		if (list==null) return null;
		ArrayList<PermissionDefinition> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.PermissionDefinition element : list) {
			result.add(map(element));
		}
		return result;
	}

	public static Acl map(com.libreecm.commonAPI.model.users.Acl element) {
		if (element == null)
			return null;
		Acl result = new AccessControlListImpl(mapAces(element.getAces()));
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;
	}

	public static List<Ace> mapAces(List<com.libreecm.commonAPI.model.users.Ace> list) {
		if (list==null) return null;
		ArrayList<Ace> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.users.Ace element : list) {
			result.add(map(element));
		}
		return result;
	}

	public static Ace map(com.libreecm.commonAPI.model.users.Ace element) {
		if (element == null)
			return null;
		Ace result = new AccessControlEntryImpl(map(element.getPrincipal()), element.getPermissions());
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;
	}

	public static ObjectData map(com.libreecm.commonAPI.model.objects.LibreECMDataObject element) {
		if (element == null)
			return null;
		ObjectDataImpl result = new ObjectDataImpl();
		result.setAcl(map(element.getAcl()));
		//FALTA FER CONVERSIO DELS TIPUS CONCRETS, EXISTEIX OBJECTFACTORY
		result.setAllowableActions(map(element.getAllowableActions()));
		result.setChangeEventInfo(map(element.getChangeEventInfo()));
		result.setExtensions(mapExtensions(element.getExtensions()));
		result.setIsExactAcl(element.getExactAcl());
		result.setPolicyIds(map(element.getPolicyIds()));
		result.setProperties(map(element.getProperties()));
		result.setRelationships(mapRelationships(element.getRelationships()));
		result.setRenditions(mapRenditions(element.getRenditions()));
		return result;
	}
	

	private static List<RenditionData> mapRenditions(
			List<com.libreecm.commonAPI.model.objects.RenditionData> renditions) {
		if (renditions==null)return null;
		ArrayList<RenditionData> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.RenditionData element: renditions){
			result.add(map(element));
		}
		return result;
	}

	private static RenditionData map(com.libreecm.commonAPI.model.objects.RenditionData element) {
		if(element==null)return null;
		RenditionDataImpl result = new RenditionDataImpl();
		result.setBigHeight(element.getBigHeight());
		result.setBigLength(element.getBigLength());
		result.setBigWidth(element.getBigWidth());
		result.setKind(element.getKind());
		result.setMimeType(element.getMimeType());
		result.setRenditionDocumentId(element.getRenditionDocumentId());
		result.setStreamId(element.getStreamId());
		result.setTitle(element.getTitle());
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;		
	}

	private static List<ObjectData> mapRelationships(List<com.libreecm.commonAPI.model.objects.Relationship> relationships) {
		if (relationships==null)return null;
		ArrayList<ObjectData> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.Relationship element: relationships){
			result.add(map(element));
		}
		return result;
	}

	private static Properties map(com.libreecm.commonAPI.model.objects.properties.Properties properties) {
		if (properties==null) return null;
		ArrayList<PropertyData<?>> result=new ArrayList<PropertyData<?>>();
		for (com.libreecm.commonAPI.model.objects.properties.PropertyData<?> element : properties.getPropertyList()) {
			if (element.getValues()==null|| element.getFirstValue()==null)
				throw new NotDefinedPropertyDataValueException(element.toString());
			result.add(map(element));
		}
		PropertiesImpl props=  new PropertiesImpl(result);
		props.setExtensions(mapExtensions(properties.getExtensions()));
		return props;
	}

	private static PolicyIdList map(com.libreecm.commonAPI.model.objects.PolicyIdList policyIds) {
		if (policyIds==null || policyIds.getPoliciesIds()==null) return null;
		PolicyIdListImpl result = new PolicyIdListImpl();
		for (String element : policyIds.getPoliciesIds()) {
			result.getPolicyIds().add(element);
		}
		result.setExtensions(mapExtensions(policyIds.getExtensions()));
		return result;
	}

	public static GregorianCalendar map(Date element) {
		if (element==null) return null;
		GregorianCalendar result = new GregorianCalendar();
		result.setTime(element);
		return result;
	}

	private static ChangeEventInfo map(com.libreecm.commonAPI.model.objects.ChangeEventInfo changeEventInfo) {
		if (changeEventInfo==null) return null;
		ChangeEventInfo result = new ChangeEventInfoDataImpl(
				ChangeType.fromValue(changeEventInfo.getChangeType().toString()),
				map(changeEventInfo.getChangeTime().getTime()));
		result.setExtensions(mapExtensions(changeEventInfo.getExtensions()));
		return result;
	}

	private static AllowableActions map(com.libreecm.commonAPI.model.objects.AllowableActions allowableActions) {
		if (allowableActions==null) return null;
		AllowableActions result = new AllowableActionsImpl();
		for (com.libreecm.commonAPI.model.enums.Action action : allowableActions.getAllowableActions()) {
			result.getAllowableActions().add(Action.valueOf(action.toString()));
		}
		result.setExtensions(mapExtensions(allowableActions.getExtensions()));
		return result;
	}

	public static Principal map(com.libreecm.commonAPI.model.users.Principal element) {
		if (element == null)
			return null;
		Principal result = new AccessControlPrincipalDataImpl(element.getId());
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;
	}

	public static List<ObjectParentData> mapObjectParents(
			com.libreecm.commonAPI.model.objects.LibreECMDataObject element,
			List<com.libreecm.commonAPI.model.objects.LibreECMDataObject> parents) {
		if (element == null || parents==null)
			return null;
		ArrayList<ObjectParentData> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.LibreECMDataObject parent : parents){
			result.add(mapParent(element, parent));
		}
		return result;
	}

	public static ObjectParentData mapParent(
			com.libreecm.commonAPI.model.objects.LibreECMDataObject element,
			com.libreecm.commonAPI.model.objects.LibreECMDataObject parent) {
		if (element == null)
			return null;
		if (parent == null)
			return null;
		ObjectParentDataImpl result = new ObjectParentDataImpl(map(element));
		result.setExtensions(mapExtensions(element.getExtensions()));
		if (parent.getFolders() == null || element.getFolders().isEmpty())
			result.setRelativePathSegment("");
		else{
			Uri folderUri = Uri.parse(element.getFolders().get(0));
			result.setRelativePathSegment(folderUri.getFolderTree());
		}
		result.setExtensions(mapExtensions(parent.getExtensions()));
		return result;
	}

	/*public static List<ObjectParentData> mapObjectParents(com.libreecm.commonAPI.model.objects.LibreECMDataObject element) {
		if (element==null) return null;
		ArrayList<ObjectParentData> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.LibreECMDataObject parent : element.getFolders()) {
			result.add(mapParent(parent, element));
		}
		return result;
	}*/


	public static TypeDefinitionList mapType(List<com.libreecm.commonAPI.model.objects.ObjectType> list) {
		TypeDefinitionListImpl result = new TypeDefinitionListImpl();
		for (com.libreecm.commonAPI.model.objects.ObjectType element : list) {
			result.getList().add(map(element));
		}
		return result;
	}
	


    /**
     * Adds a type definition.
     */
    public static TypeDefinition map(com.libreecm.commonAPI.model.objects.ObjectType type) {
        if (type == null) {
            throw new IllegalArgumentException("Type must be set!");
        }

        if (type.getId() == null || type.getId().trim().length() == 0) {
            throw new IllegalArgumentException("Type must have a valid id!");
        }

        if (type.getId() == null || type.getId().trim().length() == 0) {
            throw new IllegalArgumentException("Type must have a valid id!");
        }

        if (type.getParentTypeId() == null || type.getParentTypeId().trim().length() == 0) {
            throw new IllegalArgumentException("Type must have a valid parent id!");
        }
        
        if (type.getBaseTypeId() == null ){
        	throw new IllegalArgumentException("Type must have a valid base type id!");
        }

        TypeDefinition parentType = getTypeDefinitions().get(type.getParentTypeId());
        if (parentType == null) {
            System.out.println("Error with parent of type "+type);
        	throw new IllegalArgumentException("Parent type doesn't exist!");
        }

        MutableTypeDefinition newType = typeDefinitionFactory.copyLibreECM(type, true);

        // copy parent type property definitions and mark them as inherited
        for (PropertyDefinition<?> propDef : parentType.getPropertyDefinitions().values()) {
            MutablePropertyDefinition<?> basePropDef = typeDefinitionFactory.copy(propDef);
            basePropDef.setIsInherited(true);
            newType.addPropertyDefinition(basePropDef);
        }

        getTypeDefinitions().put(newType.getId(), newType);

        return newType;
    }
    
    private static HashMap<String, TypeDefinition> getTypeDefinitions(){
    	if (typeDefinitions==null)
    		initTypeDefinitions();
    	return typeDefinitions;
    }
 
	private static HashMap<String, TypeDefinition> typeDefinitions;
	private static TypeDefinitionFactory typeDefinitionFactory = TypeDefinitionFactory.newInstance();
    
    private static void initTypeDefinitions(){
    // set up definitions map
    typeDefinitions = new HashMap<String, TypeDefinition>();

    // add base folder type
    MutableFolderTypeDefinition folderType = typeDefinitionFactory
            .createBaseFolderTypeDefinition(CmisVersion.CMIS_1_1);
    ((MutablePropertyIdDefinition) folderType.getPropertyDefinitions().get(PropertyIds.OBJECT_ID))
            .setIsOrderable(Boolean.TRUE);
    ((MutablePropertyIdDefinition) folderType.getPropertyDefinitions().get(PropertyIds.BASE_TYPE_ID))
            .setIsOrderable(Boolean.TRUE);
    typeDefinitions.put(folderType.getId(), folderType);

    // add base document type
    MutableDocumentTypeDefinition documentType = typeDefinitionFactory
            .createBaseDocumentTypeDefinition(CmisVersion.CMIS_1_1);
    ((MutablePropertyIdDefinition) documentType.getPropertyDefinitions().get(PropertyIds.OBJECT_ID))
            .setIsOrderable(Boolean.TRUE);
    ((MutablePropertyIdDefinition) documentType.getPropertyDefinitions().get(PropertyIds.BASE_TYPE_ID))
            .setIsOrderable(Boolean.TRUE);
    typeDefinitions.put(documentType.getId(), documentType);

    // add base item type
    MutableItemTypeDefinition itemType = typeDefinitionFactory.createBaseItemTypeDefinition(CmisVersion.CMIS_1_1);
    ((MutablePropertyIdDefinition) itemType.getPropertyDefinitions().get(PropertyIds.OBJECT_ID))
            .setIsOrderable(Boolean.TRUE);
    ((MutablePropertyIdDefinition) itemType.getPropertyDefinitions().get(PropertyIds.BASE_TYPE_ID))
            .setIsOrderable(Boolean.TRUE);
    typeDefinitions.put(itemType.getId(), itemType);
    
    // add base policy type
    MutablePolicyTypeDefinition policyType = typeDefinitionFactory.createBasePolicyTypeDefinition(CmisVersion.CMIS_1_1);
    ((MutablePropertyIdDefinition) policyType.getPropertyDefinitions().get(PropertyIds.OBJECT_ID))
            .setIsOrderable(Boolean.TRUE);
    ((MutablePropertyIdDefinition) policyType.getPropertyDefinitions().get(PropertyIds.BASE_TYPE_ID))
            .setIsOrderable(Boolean.TRUE);
    typeDefinitions.put(policyType.getId(), policyType);
    

    // add base relationship type
    MutableRelationshipTypeDefinition relationshipType = typeDefinitionFactory.createBaseRelationshipTypeDefinition(CmisVersion.CMIS_1_1);
    ((MutablePropertyIdDefinition) relationshipType.getPropertyDefinitions().get(PropertyIds.OBJECT_ID))
            .setIsOrderable(Boolean.TRUE);
    ((MutablePropertyIdDefinition) relationshipType.getPropertyDefinitions().get(PropertyIds.BASE_TYPE_ID))
            .setIsOrderable(Boolean.TRUE);
    typeDefinitions.put(relationshipType.getId(), relationshipType);
    
    }
    

	private static com.libreecm.commonAPI.model.CmisExtensionElement map(CmisExtensionElement element) {
		if (element == null)
			return null;
		com.libreecm.commonAPI.model.CmisExtensionElement result;
		com.libreecm.commonAPI.model.objects.ExtensionDataImpl extensions = new com.libreecm.commonAPI.model.objects.ExtensionDataImpl();
		result = new com.libreecm.commonAPI.model.CmisExtensionElement(element.getName(), element.getNamespace(),
				element.getValue(), element.getAttributes(), mapCmisExtensions(element.getChildren()).getExtensions());
		return result;
	}

	public static TypeMutability map(com.libreecm.commonAPI.model.objects.TypeMutability element) {
		if (element == null)
			return null;
		TypeMutabilityImpl result = new TypeMutabilityImpl();
		result.setCanCreate(element.canCreate());
		result.setCanDelete(element.canDelete());
		result.setCanUpdate(element.canUpdate());
		result.setExtensions(mapExtensions(element.getExtensions()));
		return result;
	}

	public static Map<String, PropertyDefinition<?>> mapProperities(
			Map<String, com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<?>> list) {
		if (list==null) return null;
		HashMap<String, PropertyDefinition<?>> result = new HashMap<String, PropertyDefinition<?>>();
		for (String key : list.keySet()) {
			result.put(key, (PropertyDefinition<?>) mapPropertyDefinition(list.get(key)));
		}
		return result;
	}
	
	public static <T> List<PropertyDefinition<T>> mapProperities(
			List<com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<T>> list) {
		if (list==null)return null;
		
		ArrayList<PropertyDefinition<T>> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<T> element : list){
			PropertyDefinition<T> property = mapPropertyDefinition(element);
			result.add(property);
		}		
		return result;
	}

	public static <T>  PropertyDefinition<T> mapPropertyDefinition(
			com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<?> element) {
		if (element == null)
			return null;
		class PropertyDefinitionImpl<S> implements PropertyDefinition<S> {
			private static final long serialVersionUID = 1L;

			@Override
			public List<CmisExtensionElement> getExtensions() {
				return mapExtensions(element.getExtensions());
			}

			@Override
			public void setExtensions(List<CmisExtensionElement> extensions) {
			}

			@Override
			public String getId() {
						return element.getId();
			}

			@Override
			public String getLocalName() {
				return element.getLocalName();
			}

			@Override
			public String getLocalNamespace() {
				return element.getLocalNamespace();
			}

			@Override
			public String getDisplayName() {
				return element.getDisplayName();
			}

			@Override
			public String getQueryName() {
				return element.getQueryName();
			}

			@Override
			public String getDescription() {
				return element.getDescription();
			}

			@Override
			public PropertyType getPropertyType() {
				return map(element.getPropertyType());
			}

			@Override
			public Cardinality getCardinality() {
				return map(element.getCardinality());
			}

			@Override
			public Updatability getUpdatability() {
				return map(element.getUpdatibility());
			}

			@Override
			public Boolean isInherited() {
				return element.isInherited();
			}

			@Override
			public Boolean isRequired() {
				return element.isRequired();
			}

			@Override
			public Boolean isQueryable() {
				return element.isQueryable();
			}

			@Override
			public Boolean isOrderable() {
				return element.isOrderable();
			}

			@Override
			public Boolean isOpenChoice() {
				return element.isOpenChoice();
			}

			@Override
			public List<S> getDefaultValue() {
				return (List<S>) element.getDefaultValue();
			}

			@Override
			public List<Choice<S>> getChoices() {
				ArrayList<Choice<S>> array = new ArrayList<>();
				if (element.getChoices()==null) return null;
				Iterator<?> it = element.getChoices().iterator();
				while (it.hasNext())
					array.add(map((com.libreecm.commonAPI.model.objects.Choice<S>) it.next()));
				return array;
			}

		}
		PropertyDefinition<T> result = new PropertyDefinitionImpl<T>();
		return result;
	}

	public static PropertyData<?> map(com.libreecm.commonAPI.model.objects.properties.PropertyData<?> element) {
		PropertyData<?> result;
		if (element.getValues()==null || element.getFirstValue()==null)
			throw new NotDefinedPropertyDataValueException(element.toString());
		if (element.getClass().equals(com.libreecm.commonAPI.model.objects.properties.PropertyBooleanImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyBooleanImpl(element.getId(), (List<Boolean>) element.getValues());
			} else
				result = new PropertyBooleanImpl(
						mapPropertyDefinition((com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<Boolean>) element
								.getPropertyDefinition()),
						(List<Boolean>) element.getValues());
		} else if (element.getClass()
				.equals(com.libreecm.commonAPI.model.objects.properties.PropertyDateTimeImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyDateTimeImpl(element.getId(), (List<GregorianCalendar>) element.getValues());
			} else
				result = new PropertyDateTimeImpl(mapPropertyDefinition(
						(com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<GregorianCalendar>) element
								.getPropertyDefinition()),
						(List<GregorianCalendar>) element.getValues());
		} else if (element.getClass()
				.equals(com.libreecm.commonAPI.model.objects.properties.PropertyDecimalImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyDecimalImpl(element.getId(), (List<BigDecimal>) element.getValues());
			} else
				result = new PropertyDecimalImpl(mapPropertyDefinition(
						(com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<BigDecimal>) element
								.getPropertyDefinition()),
						(List<BigDecimal>) element.getValues());
		} else if (element.getClass().equals(com.libreecm.commonAPI.model.objects.properties.PropertyHtmlImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyHtmlImpl(element.getId(), (List<String>) element.getValues());
			} else
				result = new PropertyHtmlImpl(
						mapPropertyDefinition((com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<String>) element
								.getPropertyDefinition()),
						(List<String>) element.getValues());
		} else if (element.getClass().equals(com.libreecm.commonAPI.model.objects.properties.PropertyIdImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyIdImpl(element.getId(), (List<String>) element.getValues());
			} else
				result = new PropertyIdImpl(
						mapPropertyDefinition((com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<String>) element
								.getPropertyDefinition()),
						(List<String>) element.getValues());
		} else if (element.getClass()
				.equals(com.libreecm.commonAPI.model.objects.properties.PropertyIntegerImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyIntegerImpl(element.getId(), (List<BigInteger>) element.getValues());
			} else{
				com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<BigInteger> property = (com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<BigInteger>) element
						.getPropertyDefinition();
				result = new PropertyIntegerImpl(mapPropertyDefinition(property),(List<BigInteger>) element.getValues());
				}
		} else if (element.getClass().equals(com.libreecm.commonAPI.model.objects.properties.PropertyUriImpl.class)) {
			if (element.getPropertyDefinition() == null) {
				result = new PropertyUriImpl(element.getId(), (List<String>) element.getValues());
			} else
				result = new PropertyUriImpl(
						mapPropertyDefinition((com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<String>) element
								.getPropertyDefinition()),
						(List<String>) element.getValues());
		} else if (element.getPropertyDefinition() == null) {
			result = new PropertyStringImpl(element.getId(), (List<String>) element.getValues());
		} else
			result = new PropertyStringImpl(
					mapPropertyDefinition((com.libreecm.commonAPI.model.objects.properties.PropertyDefinition<String>) element
							.getPropertyDefinition()),
					(List<String>) element.getValues());

		return result;
	}

	public static PropertyType map(com.libreecm.commonAPI.model.enums.PropertyType propertyType) {
		if (propertyType == null)
			return null;
		return PropertyType.valueOf(propertyType.toString());
	}

	public static Cardinality map(com.libreecm.commonAPI.model.enums.Cardinality element) {
		if (element == null)
			return null;
		return Cardinality.valueOf(element.toString());
	}

	public static Updatability map(com.libreecm.commonAPI.model.enums.Updatability element) {
		if (element == null)
			return null;
		return Updatability.valueOf(element.toString());
	}

	public static <T> List<Choice<T>> mapChoices(List<com.libreecm.commonAPI.model.objects.Choice<T>> list) {
		ArrayList<Choice<T>> result = new ArrayList<>();
		for (com.libreecm.commonAPI.model.objects.Choice<T> element : list) {
			result.add(map(element));
		}
		return result;
	}

	public static <T> Choice<T> map(com.libreecm.commonAPI.model.objects.Choice<T> element) {
		if (element == null)
			return null;
		ChoiceImpl<T> result = new ChoiceImpl<T>();
		result.setChoice(mapChoices(element.getChoice()));
		result.setDisplayName(element.getDisplayName());
		result.setValue(element.getValue());
		return result;
	}


	public static com.libreecm.commonAPI.model.objects.ExtensionDataImpl mapCmisExtensions(List<CmisExtensionElement> list) {
		if (list==null) return null;
		com.libreecm.commonAPI.model.objects.ExtensionDataImpl result = new com.libreecm.commonAPI.model.objects.ExtensionDataImpl();
		for (CmisExtensionElement element : list) {
			result.getExtensions().add(map(element));
		}
		return result;
	}
	

	public static com.libreecm.commonAPI.model.CmisExtensionElement mapCmisExtension(CmisExtensionElement element) {
		if (element==null) return null;
		com.libreecm.commonAPI.model.CmisExtensionElement result = new com.libreecm.commonAPI.model.CmisExtensionElement();
		result.setAttributes(element.getAttributes());
		result.setChildren(mapCmisExtensions(element.getChildren()).getExtensions());
		result.setName(element.getName());
		result.setNamespace(element.getNamespace());
		result.setValue(element.getValue());
		return result;
	}

	public static com.libreecm.commonAPI.model.objects.ExtensionDataImpl mapToExtensionDataImpl(List<CmisExtensionElement> list) {
		if (list==null) return null;
		com.libreecm.commonAPI.model.objects.ExtensionDataImpl result = new com.libreecm.commonAPI.model.objects.ExtensionDataImpl();
		for (CmisExtensionElement element : list) {
			result.getExtensions().add(map(element));
		}
		return result;
	}

	public static ObjectInFolderList map(List<LibreECMDataObject> children) {
		ObjectInFolderList result = new ObjectInFolderListImpl();
		result.setExtensions(null);
		for (LibreECMDataObject child: children){
			result.getObjects().add(mapObjectInFolder(child));
		}
		return result;
	}
	
	public static ObjectInFolderData mapObjectInFolder(LibreECMDataObject element){
		ObjectInFolderData result = new ObjectInFolderDataImpl(map(element));
		return result;
	}
}
