org.eclipse.ecr.opencmis.impl.server.NuxeoPropertyData.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ecr.opencmis.impl.server.NuxeoPropertyData.java

Source

/*
 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 */
package org.eclipse.ecr.opencmis.impl.server;

import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.PropertyBoolean;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.data.PropertyDateTime;
import org.apache.chemistry.opencmis.commons.data.PropertyDecimal;
import org.apache.chemistry.opencmis.commons.data.PropertyHtml;
import org.apache.chemistry.opencmis.commons.data.PropertyId;
import org.apache.chemistry.opencmis.commons.data.PropertyInteger;
import org.apache.chemistry.opencmis.commons.data.PropertyString;
import org.apache.chemistry.opencmis.commons.data.PropertyUri;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.enums.Cardinality;
import org.apache.chemistry.opencmis.commons.enums.PropertyType;
import org.apache.chemistry.opencmis.commons.enums.Updatability;
import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.api.Blob;
import org.eclipse.ecr.core.api.ClientException;
import org.eclipse.ecr.core.api.CoreSession;
import org.eclipse.ecr.core.api.DocumentModel;
import org.eclipse.ecr.core.api.DocumentRef;
import org.eclipse.ecr.core.api.IdRef;
import org.eclipse.ecr.core.api.blobholder.BlobHolder;
import org.eclipse.ecr.core.api.impl.blob.InputStreamBlob;
import org.eclipse.ecr.core.api.model.Property;
import org.eclipse.ecr.core.api.model.PropertyNotFoundException;
import org.eclipse.ecr.core.query.sql.NXQL;
import org.eclipse.ecr.core.schema.types.Type;

/**
 * Nuxeo implementation of an object's property, backed by a property of a
 * {@link DocumentModel}.
 */
public abstract class NuxeoPropertyData<T> extends NuxeoPropertyDataBase<T> {

    protected final String name;

    protected final boolean readOnly;

    // TODO unused
    public static final Map<String, String> propertyNameToNXQL;
    static {
        Map<String, String> map = new HashMap<String, String>();
        map.put(PropertyIds.OBJECT_ID, NXQL.ECM_UUID);
        map.put(PropertyIds.OBJECT_TYPE_ID, NXQL.ECM_PRIMARYTYPE);
        map.put(PropertyIds.PARENT_ID, NXQL.ECM_PARENTID);
        map.put(PropertyIds.NAME, NXQL.ECM_NAME);
        map.put(PropertyIds.CREATED_BY, NuxeoTypeHelper.NX_DC_CREATOR);
        map.put(PropertyIds.CREATION_DATE, NuxeoTypeHelper.NX_DC_CREATED);
        map.put(PropertyIds.LAST_MODIFIED_BY, "dc:contributors");
        map.put(PropertyIds.LAST_MODIFICATION_DATE, NuxeoTypeHelper.NX_DC_MODIFIED);
        propertyNameToNXQL = Collections.unmodifiableMap(map);
    }

    public NuxeoPropertyData(PropertyDefinition<T> propertyDefinition, DocumentModel doc, String name,
            boolean readOnly) {
        super(propertyDefinition, doc);
        this.name = name;
        this.readOnly = readOnly;
    }

    /**
     * Factory for a new Property.
     */
    @SuppressWarnings("unchecked")
    public static <U> PropertyData<U> construct(NuxeoObjectData data, PropertyDefinition<U> pd,
            CallContext callContext) {
        DocumentModel doc = data.doc;
        String name = pd.getId();
        if (PropertyIds.OBJECT_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdDataFixed((PropertyDefinition<String>) pd, doc.getId());
        } else if (PropertyIds.OBJECT_TYPE_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdDataFixed((PropertyDefinition<String>) pd,
                    NuxeoTypeHelper.mappedId(doc.getType()));
        } else if (PropertyIds.BASE_TYPE_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdDataFixed((PropertyDefinition<String>) pd,
                    NuxeoTypeHelper.getBaseTypeId(doc).value());
        } else if (PropertyIds.CREATED_BY.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyStringData((PropertyDefinition<String>) pd, doc,
                    NuxeoTypeHelper.NX_DC_CREATOR, true);
        } else if (PropertyIds.CREATION_DATE.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDateTimeData((PropertyDefinition<GregorianCalendar>) pd, doc,
                    NuxeoTypeHelper.NX_DC_CREATED, true);
        } else if (PropertyIds.LAST_MODIFIED_BY.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyStringData((PropertyDefinition<String>) pd, doc,
                    NuxeoTypeHelper.NX_DC_LAST_CONTRIBUTOR, true);
        } else if (PropertyIds.LAST_MODIFICATION_DATE.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDateTimeData((PropertyDefinition<GregorianCalendar>) pd, doc,
                    NuxeoTypeHelper.NX_DC_MODIFIED, true);
        } else if (PropertyIds.CHANGE_TOKEN.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyStringDataFixed((PropertyDefinition<String>) pd, null);
        } else if (PropertyIds.NAME.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataName((PropertyDefinition<String>) pd, doc);
        } else if (PropertyIds.IS_IMMUTABLE.equals(name)) {
            // TODO check write
            return (PropertyData<U>) new NuxeoPropertyBooleanDataFixed((PropertyDefinition<Boolean>) pd,
                    Boolean.FALSE);
        } else if (PropertyIds.IS_LATEST_VERSION.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataIsLatestVersion((PropertyDefinition<Boolean>) pd, doc);
        } else if (PropertyIds.IS_LATEST_MAJOR_VERSION.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataIsLatestMajorVersion((PropertyDefinition<Boolean>) pd,
                    doc);
        } else if (PropertyIds.IS_MAJOR_VERSION.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataIsMajorVersion((PropertyDefinition<Boolean>) pd, doc);
        } else if (PropertyIds.VERSION_LABEL.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataVersionLabel((PropertyDefinition<String>) pd, doc);
        } else if (PropertyIds.VERSION_SERIES_ID.equals(name)) {
            // doesn't change once computed, no need to have a dynamic prop
            String versionSeriesId;
            try {
                versionSeriesId = doc.getVersionSeriesId();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
            return (PropertyData<U>) new NuxeoPropertyIdDataFixed((PropertyDefinition<String>) pd, versionSeriesId);
        } else if (PropertyIds.IS_VERSION_SERIES_CHECKED_OUT.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataIsVersionSeriesCheckedOut(
                    (PropertyDefinition<Boolean>) pd, doc);
        } else if (PropertyIds.VERSION_SERIES_CHECKED_OUT_BY.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataVersionSeriesCheckedOutBy((PropertyDefinition<String>) pd,
                    doc, callContext);
        } else if (PropertyIds.VERSION_SERIES_CHECKED_OUT_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataVersionSeriesCheckedOutId((PropertyDefinition<String>) pd,
                    doc);
        } else if (PropertyIds.CHECKIN_COMMENT.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataCheckInComment((PropertyDefinition<String>) pd, doc);
        } else if (PropertyIds.CONTENT_STREAM_LENGTH.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataContentStreamLength((PropertyDefinition<BigInteger>) pd,
                    doc);
        } else if (PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataContentStreamMimeType((PropertyDefinition<String>) pd,
                    doc);
        } else if (PropertyIds.CONTENT_STREAM_FILE_NAME.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataContentStreamFileName((PropertyDefinition<String>) pd,
                    doc);
        } else if (PropertyIds.CONTENT_STREAM_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdDataFixed((PropertyDefinition<String>) pd, null);
        } else if (PropertyIds.PARENT_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataParentId((PropertyDefinition<String>) pd, doc);
        } else if (PropertyIds.PATH.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyDataPath((PropertyDefinition<String>) pd, doc);
        } else if (PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdMultiDataFixed((PropertyDefinition<String>) pd,
                    Collections.<String>emptyList());
        } else if (PropertyIds.SOURCE_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdData((PropertyDefinition<String>) pd, doc,
                    NuxeoTypeHelper.NX_REL_SOURCE, false);
        } else if (PropertyIds.TARGET_ID.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyIdData((PropertyDefinition<String>) pd, doc,
                    NuxeoTypeHelper.NX_REL_TARGET, false);
        } else if (PropertyIds.POLICY_TEXT.equals(name)) {
            return (PropertyData<U>) new NuxeoPropertyStringDataFixed((PropertyDefinition<String>) pd, null);
        } else {
            boolean readOnly = pd.getUpdatability() != Updatability.READWRITE;
            // TODO WHEN_CHECKED_OUT, ON_CREATE

            switch (pd.getPropertyType()) {
            case BOOLEAN:
                return (PropertyData<U>) new NuxeoPropertyBooleanData((PropertyDefinition<Boolean>) pd, doc, name,
                        readOnly);
            case DATETIME:
                return (PropertyData<U>) new NuxeoPropertyDateTimeData((PropertyDefinition<GregorianCalendar>) pd,
                        doc, name, readOnly);
            case DECIMAL:
                return (PropertyData<U>) new NuxeoPropertyDecimalData((PropertyDefinition<BigDecimal>) pd, doc,
                        name, readOnly);
            case HTML:
                return (PropertyData<U>) new NuxeoPropertyHtmlData((PropertyDefinition<String>) pd, doc, name,
                        readOnly);
            case ID:
                return (PropertyData<U>) new NuxeoPropertyIdData((PropertyDefinition<String>) pd, doc, name,
                        readOnly);
            case INTEGER:
                return (PropertyData<U>) new NuxeoPropertyIntegerData((PropertyDefinition<BigInteger>) pd, doc,
                        name, readOnly);
            case STRING:
                return (PropertyData<U>) new NuxeoPropertyStringData((PropertyDefinition<String>) pd, doc, name,
                        readOnly);
            case URI:
                return (PropertyData<U>) new NuxeoPropertyUriData((PropertyDefinition<String>) pd, doc, name,
                        readOnly);
            default:
                throw new AssertionError(pd.getPropertyType().toString());
            }
        }
    }

    public static ContentStream getContentStream(DocumentModel doc) throws CmisRuntimeException {
        BlobHolder blobHolder = doc.getAdapter(BlobHolder.class);
        if (blobHolder == null) {
            throw new CmisStreamNotSupportedException();
        }
        Blob blob;
        try {
            blob = blobHolder.getBlob();
        } catch (ClientException e) {
            throw new CmisRuntimeException(e.toString(), e);
        }
        return blob == null ? null : new NuxeoContentStream(blob);
    }

    public static void setContentStream(DocumentModel doc, ContentStream contentStream, boolean overwrite)
            throws IOException, CmisContentAlreadyExistsException, CmisRuntimeException {
        BlobHolder blobHolder = doc.getAdapter(BlobHolder.class);
        if (blobHolder == null) {
            throw new CmisContentAlreadyExistsException();
        }
        if (!overwrite) {
            Blob blob;
            try {
                blob = blobHolder.getBlob();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
            if (blob != null) {
                throw new CmisContentAlreadyExistsException();
            }
        }
        Blob blob = contentStream == null ? null
                : new InputStreamBlob(contentStream.getStream(), contentStream.getMimeType(), null,
                        contentStream.getFileName(), null);
        try {
            blobHolder.setBlob(blob);
        } catch (ClientException e) {
            throw new CmisRuntimeException(e.toString(), e);
        }
    }

    /**
     * Conversion from Nuxeo values to CMIS ones.
     *
     * @return either a primitive type or a List of them, or {@code null}
     */
    @Override
    @SuppressWarnings("unchecked")
    public <U> U getValue() {
        try {
            Property prop = doc.getProperty(name);
            Serializable value = prop.getValue();
            if (value == null) {
                return null;
            }
            Type type = prop.getType();
            if (type.isListType()) {
                // array/list
                List<Object> values;
                if (value instanceof Object[]) {
                    values = Arrays.asList((Object[]) value);
                } else if (value instanceof List<?>) {
                    values = (List<Object>) value;
                } else {
                    throw new CmisRuntimeException("Unknown value type: " + value.getClass().getName());
                }
                List<Object> list = new ArrayList<Object>(values);
                for (int i = 0; i < list.size(); i++) {
                    list.set(i, convertToCMIS(list.get(i)));
                }
                return (U) list;
            } else {
                // primitive type
                return (U) convertToCMIS(value);
            }
        } catch (ClientException e) {
            throw new CmisRuntimeException(e.toString(), e);
        }
    }

    // conversion from Nuxeo value types to CMIS ones
    protected static Object convertToCMIS(Object value) {
        if (value instanceof Double) {
            return BigDecimal.valueOf(((Double) value).doubleValue());
        } else if (value instanceof Integer) {
            return BigInteger.valueOf(((Integer) value).intValue());
        } else if (value instanceof Long) {
            return BigInteger.valueOf(((Long) value).longValue());
        } else {
            return value;
        }
    }

    // conversion from CMIS value types to Nuxeo ones
    protected static Object convertToNuxeo(Object value) {
        if (value instanceof BigDecimal) {
            return Double.valueOf(((BigDecimal) value).doubleValue());
        } else if (value instanceof BigInteger) {
            return Long.valueOf(((BigInteger) value).longValue());
        } else {
            return value;
        }
    }

    /**
     * Validates a CMIS value according to a property definition.
     */
    @SuppressWarnings("unchecked")
    public static <T> void validateCMISValue(Object value, PropertyDefinition<T> pd) {
        if (value == null) {
            return;
        }
        List<T> values;
        if (value instanceof List<?>) {
            if (pd.getCardinality() != Cardinality.MULTI) {
                throw new CmisInvalidArgumentException("Property is single-valued: " + pd.getId());
            }
            values = (List<T>) value;
            if (values.isEmpty()) {
                return;
            }
        } else {
            if (pd.getCardinality() != Cardinality.SINGLE) {
                throw new CmisInvalidArgumentException("Property is multi-valued: " + pd.getId());
            }
            values = Collections.singletonList((T) value);
        }
        PropertyType type = pd.getPropertyType();
        for (Object v : values) {
            if (v == null) {
                throw new CmisInvalidArgumentException("Null values not allowed: " + values);
            }
            boolean ok;
            switch (type) {
            case STRING:
            case ID:
            case URI:
            case HTML:
                ok = v instanceof String;
                break;
            case INTEGER:
                ok = v instanceof BigInteger || v instanceof Byte || v instanceof Short || v instanceof Integer
                        || v instanceof Long;
                break;
            case DECIMAL:
                ok = v instanceof BigDecimal;
                break;
            case BOOLEAN:
                ok = v instanceof Boolean;
                break;
            case DATETIME:
                ok = v instanceof GregorianCalendar;
                break;
            default:
                throw new RuntimeException(type.toString());
            }
            if (!ok) {
                throw new CmisInvalidArgumentException("Value does not match property type " + type + ":  " + v);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public T getFirstValue() {
        Object value = getValue();
        if (value == null) {
            return null;
        }
        if (value instanceof List) {
            List<?> list = (List<?>) value;
            if (list.isEmpty()) {
                return null;
            }
            return (T) list.get(0);
        } else {
            return (T) value;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<T> getValues() {
        Object value = getValue();
        if (value == null) {
            return Collections.emptyList();
        }
        if (value instanceof List) {
            return (List<T>) value;
        } else {
            return (List<T>) Collections.singletonList(value);
        }
    }

    @Override
    public void setValue(Object value) {
        try {
            if (readOnly) {
                super.setValue(value);
            } else {
                Object propValue;
                if (value instanceof List<?>) {
                    @SuppressWarnings("unchecked")
                    List<Object> list = new ArrayList<Object>((List<Object>) value);
                    for (int i = 0; i < list.size(); i++) {
                        list.set(i, convertToNuxeo(list.get(i)));
                    }
                    if (list.isEmpty()) {
                        list = null;
                    }
                    propValue = list;
                } else {
                    propValue = convertToNuxeo(value);
                }
                doc.setPropertyValue(name, (Serializable) propValue);
            }
        } catch (ClientException e) {
            throw new CmisRuntimeException(e.toString(), e);
        }
    }

    protected static Blob getBlob(DocumentModel doc) throws CmisRuntimeException {
        BlobHolder blobHolder = doc.getAdapter(BlobHolder.class);
        if (blobHolder == null) {
            return null;
        }
        try {
            return blobHolder.getBlob();
        } catch (ClientException e) {
            throw new CmisRuntimeException(e.toString(), e);
        }
    }

    public static class NuxeoPropertyStringData extends NuxeoPropertyData<String> implements PropertyString {
        public NuxeoPropertyStringData(PropertyDefinition<String> propertyDefinition, DocumentModel doc,
                String name, boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyIdData extends NuxeoPropertyData<String> implements PropertyId {
        public NuxeoPropertyIdData(PropertyDefinition<String> propertyDefinition, DocumentModel doc, String name,
                boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyBooleanData extends NuxeoPropertyData<Boolean> implements PropertyBoolean {
        public NuxeoPropertyBooleanData(PropertyDefinition<Boolean> propertyDefinition, DocumentModel doc,
                String name, boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyIntegerData extends NuxeoPropertyData<BigInteger> implements PropertyInteger {
        public NuxeoPropertyIntegerData(PropertyDefinition<BigInteger> propertyDefinition, DocumentModel doc,
                String name, boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyDecimalData extends NuxeoPropertyData<BigDecimal> implements PropertyDecimal {
        public NuxeoPropertyDecimalData(PropertyDefinition<BigDecimal> propertyDefinition, DocumentModel doc,
                String name, boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyDateTimeData extends NuxeoPropertyData<GregorianCalendar>
            implements PropertyDateTime {
        public NuxeoPropertyDateTimeData(PropertyDefinition<GregorianCalendar> propertyDefinition,
                DocumentModel doc, String name, boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyHtmlData extends NuxeoPropertyData<String> implements PropertyHtml {
        public NuxeoPropertyHtmlData(PropertyDefinition<String> propertyDefinition, DocumentModel doc, String name,
                boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    public static class NuxeoPropertyUriData extends NuxeoPropertyData<String> implements PropertyUri {
        public NuxeoPropertyUriData(PropertyDefinition<String> propertyDefinition, DocumentModel doc, String name,
                boolean readOnly) {
            super(propertyDefinition, doc, name, readOnly);
        }
    }

    /**
     * Property for cmis:contentStreamFileName.
     */
    public static class NuxeoPropertyDataContentStreamFileName extends NuxeoPropertyDataBase<String>
            implements PropertyString {

        protected NuxeoPropertyDataContentStreamFileName(PropertyDefinition<String> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            Blob blob = getBlob(doc);
            return blob == null ? null : blob.getFilename();
        }

        // @Override
        // public void setValue(Serializable value) {
        // BlobHolder blobHolder = docHolder.getDocumentModel().getAdapter(
        // BlobHolder.class);
        // if (blobHolder == null) {
        // throw new StreamNotSupportedException();
        // }
        // Blob blob;
        // try {
        // blob = blobHolder.getBlob();
        // } catch (ClientException e) {
        // throw new CmisRuntimeException(e.toString(), e);
        // }
        // if (blob != null) {
        // blob.setFilename((String) value);
        // }
        // }
    }

    /**
     * Property for cmis:contentStreamLength.
     */
    public static class NuxeoPropertyDataContentStreamLength extends NuxeoPropertyDataBase<BigInteger>
            implements PropertyInteger {

        protected NuxeoPropertyDataContentStreamLength(PropertyDefinition<BigInteger> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public BigInteger getFirstValue() {
            Blob blob = getBlob(doc);
            return blob == null ? null : BigInteger.valueOf(blob.getLength());
        }
    }

    /**
     * Property for cmis:contentMimeTypeLength.
     */
    public static class NuxeoPropertyDataContentStreamMimeType extends NuxeoPropertyDataBase<String>
            implements PropertyString {

        protected NuxeoPropertyDataContentStreamMimeType(PropertyDefinition<String> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            Blob blob = getBlob(doc);
            return blob == null ? null : blob.getMimeType();
        }
    }

    /**
     * Property for cmis:name.
     */
    public static class NuxeoPropertyDataName extends NuxeoPropertyDataBase<String> implements PropertyString {

        private static final Log log = LogFactory.getLog(NuxeoPropertyDataName.class);

        protected NuxeoPropertyDataName(PropertyDefinition<String> propertyDefinition, DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        /**
         * Gets the value for the cmis:name property.
         */
        public static String getValue(DocumentModel doc) {
            if (doc.getPath() == null) {
                // not a real doc (content changes)
                return "";
            }
            if (doc.getPath().isRoot()) {
                return ""; // Nuxeo root
            }
            String name;
            try {
                name = doc.getTitle();
            } catch (ClientException e) {
                name = "";
            }
            return name;
        }

        @Override
        public String getFirstValue() {
            return getValue(doc);
        }

        @Override
        public void setValue(Object value) {
            try {
                doc.setPropertyValue(NuxeoTypeHelper.NX_DC_TITLE, (String) value);
            } catch (PropertyNotFoundException e) {
                // trying to set the name of a type with no dublincore
                // ignore
                log.debug("Cannot set CMIS name on type: " + doc.getType());
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:parentId.
     */
    public static class NuxeoPropertyDataParentId extends NuxeoPropertyDataBase<String> implements PropertyId {

        protected NuxeoPropertyDataParentId(PropertyDefinition<String> propertyDefinition, DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            if (doc.getName() == null) {
                return null;
            } else {
                DocumentRef parentRef = doc.getParentRef();
                if (parentRef instanceof IdRef) {
                    return ((IdRef) parentRef).value;
                } else {
                    try {
                        return doc.getCoreSession().getDocument(parentRef).getId();
                    } catch (ClientException e) {
                        throw new CmisRuntimeException(e.toString(), e);
                    }
                }
            }
        }
    }

    /**
     * Property for cmis:path.
     */
    public static class NuxeoPropertyDataPath extends NuxeoPropertyDataBase<String> implements PropertyString {

        protected NuxeoPropertyDataPath(PropertyDefinition<String> propertyDefinition, DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            String path = doc.getPathAsString();
            return path == null ? "" : path;
        }
    }

    protected static boolean isLiveDocumentMajorVersion(DocumentModel doc) throws ClientException {
        return !doc.isCheckedOut() && doc.getVersionLabel().endsWith(".0");
    }

    /**
     * Property for cmis:isMajorVersion.
     */
    public static class NuxeoPropertyDataIsMajorVersion extends NuxeoPropertyDataBase<Boolean>
            implements PropertyBoolean {

        protected NuxeoPropertyDataIsMajorVersion(PropertyDefinition<Boolean> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public Boolean getFirstValue() {
            try {
                if (doc.isVersion() || doc.isProxy()) {
                    return Boolean.valueOf(doc.isMajorVersion());
                }
                // checked in doc considered latest version
                return Boolean.valueOf(isLiveDocumentMajorVersion(doc));
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:isLatestVersion.
     */
    public static class NuxeoPropertyDataIsLatestVersion extends NuxeoPropertyDataBase<Boolean>
            implements PropertyBoolean {

        protected NuxeoPropertyDataIsLatestVersion(PropertyDefinition<Boolean> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public Boolean getFirstValue() {
            try {
                if (doc.isVersion() || doc.isProxy()) {
                    return Boolean.valueOf(doc.isLatestVersion());
                }
                // checked in doc considered latest version
                return Boolean.valueOf(!doc.isCheckedOut());
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:isLatestMajorVersion.
     */
    public static class NuxeoPropertyDataIsLatestMajorVersion extends NuxeoPropertyDataBase<Boolean>
            implements PropertyBoolean {

        protected NuxeoPropertyDataIsLatestMajorVersion(PropertyDefinition<Boolean> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public Boolean getFirstValue() {
            try {
                if (doc.isVersion() || doc.isProxy()) {
                    return Boolean.valueOf(doc.isLatestMajorVersion());
                }
                // checked in doc considered latest version
                return Boolean.valueOf(isLiveDocumentMajorVersion(doc));
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:isVersionSeriesCheckedOut.
     */
    public static class NuxeoPropertyDataIsVersionSeriesCheckedOut extends NuxeoPropertyDataBase<Boolean>
            implements PropertyBoolean {

        protected NuxeoPropertyDataIsVersionSeriesCheckedOut(PropertyDefinition<Boolean> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public Boolean getFirstValue() {
            try {
                return Boolean.valueOf(doc.isVersionSeriesCheckedOut());
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:versionSeriesCheckedOutId.
     */
    public static class NuxeoPropertyDataVersionSeriesCheckedOutId extends NuxeoPropertyDataBase<String>
            implements PropertyId {

        protected NuxeoPropertyDataVersionSeriesCheckedOutId(PropertyDefinition<String> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            try {
                if (!doc.isVersionSeriesCheckedOut()) {
                    return null;
                }
                DocumentModel pwc = doc.getCoreSession().getWorkingCopy(doc.getRef());
                return pwc == null ? null : pwc.getId();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:versionSeriesCheckedOutBy.
     */
    public static class NuxeoPropertyDataVersionSeriesCheckedOutBy extends NuxeoPropertyDataBase<String>
            implements PropertyString {

        protected final CallContext callContext;

        protected NuxeoPropertyDataVersionSeriesCheckedOutBy(PropertyDefinition<String> propertyDefinition,
                DocumentModel doc, CallContext callContext) {
            super(propertyDefinition, doc);
            this.callContext = callContext;
        }

        @Override
        public String getFirstValue() {
            try {
                if (!doc.isVersionSeriesCheckedOut()) {
                    return null;
                }
                DocumentModel pwc = doc.getCoreSession().getWorkingCopy(doc.getRef());
                // TODO not implemented
                return pwc == null ? null : callContext.getUsername();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:versionLabel.
     */
    public static class NuxeoPropertyDataVersionLabel extends NuxeoPropertyDataBase<String>
            implements PropertyString {

        protected NuxeoPropertyDataVersionLabel(PropertyDefinition<String> propertyDefinition, DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            try {
                if (doc.isVersion() || doc.isProxy()) {
                    return doc.getVersionLabel();
                }
                return doc.isCheckedOut() ? null : doc.getVersionLabel();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

    /**
     * Property for cmis:checkinComment.
     */
    public static class NuxeoPropertyDataCheckInComment extends NuxeoPropertyDataBase<String>
            implements PropertyString {

        protected NuxeoPropertyDataCheckInComment(PropertyDefinition<String> propertyDefinition,
                DocumentModel doc) {
            super(propertyDefinition, doc);
        }

        @Override
        public String getFirstValue() {
            try {
                if (doc.isVersion() || doc.isProxy()) {
                    return doc.getCheckinComment();
                }
                if (doc.isCheckedOut()) {
                    return null;
                }
                CoreSession session = doc.getCoreSession();
                DocumentRef v = session.getBaseVersion(doc.getRef());
                DocumentModel ver = session.getDocument(v);
                return ver.getCheckinComment();
            } catch (ClientException e) {
                throw new CmisRuntimeException(e.toString(), e);
            }
        }
    }

}