OpenCMIS client: more convenience around versioning

git-svn-id: https://svn.apache.org/repos/asf/chemistry/opencmis/trunk@1754492 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Document.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Document.java
index 519d6b8..899baad 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Document.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Document.java
@@ -35,6 +35,28 @@
  */
 public interface Document extends FileableCmisObject, DocumentProperties {
 
+    /**
+     * Returns the object type as a document type.
+     * 
+     * @return the document type
+     * 
+     * @throws ClassCastException
+     *             if the object type is not a document type
+     * 
+     * @cmis 1.0
+     */
+    DocumentType getDocumentType();
+
+    /**
+     * Returns whether the document is versionable or not.
+     * 
+     * @return {@code true} if the document is versionable, {@code false} if the
+     *         document is not versionable or if it's unknown
+     * 
+     * @cmis 1.0
+     */
+    boolean isVersionable();
+
     // object service
 
     /**
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Folder.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Folder.java
index 2a1bc9d..91881d4 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Folder.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Folder.java
@@ -33,6 +33,18 @@
  */
 public interface Folder extends FileableCmisObject, FolderProperties {
 
+    /**
+     * Returns the object type as a folder type.
+     * 
+     * @return the folder type
+     * 
+     * @throws ClassCastException
+     *             if the object type is not a folder type
+     * 
+     * @cmis 1.0
+     */
+    FolderType getFolderType();
+
     // object service
 
     /**
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Item.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Item.java
index 7ce1cf2..e2aa6b2 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Item.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Item.java
@@ -25,4 +25,16 @@
  */
 public interface Item extends FileableCmisObject {
 
+    /**
+     * Returns the object type as an item type.
+     * 
+     * @return the item type
+     * 
+     * @throws ClassCastException
+     *             if the object type is not an item type
+     * 
+     * @cmis 1.1
+     */
+    ItemType getItemType();
+
 }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Policy.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Policy.java
index 09baa0a..5419922 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Policy.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Policy.java
@@ -25,4 +25,16 @@
  */
 public interface Policy extends FileableCmisObject, PolicyProperties {
 
+    /**
+     * Returns the object type as a policy type.
+     * 
+     * @return the policy type
+     * 
+     * @throws ClassCastException
+     *             if the object type is not a policy type
+     * 
+     * @cmis 1.0
+     */
+    PolicyType getPolicyType();
+
 }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Relationship.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Relationship.java
index 2c117e2..d472512 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Relationship.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Relationship.java
@@ -26,26 +26,46 @@
 public interface Relationship extends CmisObject, RelationshipProperties {
 
     /**
-     * Gets the source object. If the source object ID is invalid,
-     * {@code null} will be returned.
+     * Returns the object type as a relationship type.
+     * 
+     * @return the relationship type
+     * 
+     * @throws ClassCastException
+     *             if the object type is not a relationship type
+     * 
+     * @cmis 1.0
+     */
+    RelationshipType getRelationshipType();
+
+    /**
+     * Gets the source object. If the source object ID is invalid, {@code null}
+     * will be returned.
+     * 
+     * @cmis 1.0
      */
     CmisObject getSource();
 
     /**
      * Gets the source object using the given {@link OperationContext}. If the
      * source object ID is invalid, {@code null} will be returned.
+     * 
+     * @cmis 1.0
      */
     CmisObject getSource(OperationContext context);
 
     /**
-     * Gets the target object. If the target object ID is invalid,
-     * {@code null} will be returned.
+     * Gets the target object. If the target object ID is invalid, {@code null}
+     * will be returned.
+     * 
+     * @cmis 1.0
      */
     CmisObject getTarget();
 
     /**
      * Gets the target object using the given {@link OperationContext}. If the
      * target object ID is invalid, {@code null} will be returned.
+     * 
+     * @cmis 1.0
      */
     CmisObject getTarget(OperationContext context);
 }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Session.java b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Session.java
index 02fb9c7..e28e383 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Session.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/api/Session.java
@@ -544,6 +544,10 @@
     /**
      * Returns the latest version in a version series.
      * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
+     * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
      * 
@@ -556,6 +560,10 @@
     /**
      * Returns the latest version in a version series.
      * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
+     * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
      * @param context
@@ -570,6 +578,14 @@
     /**
      * Returns the latest version in a version series.
      * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
+     * 
+     * If {@code major} == {@code true} and the version series doesn't contain a
+     * major version, the repository is supposed to throw a
+     * {@link CmisObjectNotFoundException}.
+     * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
      * @param major
@@ -585,7 +601,11 @@
     Document getLatestDocumentVersion(ObjectId objectId, boolean major, OperationContext context);
 
     /**
-     * /** Returns the latest version in a version series.
+     * Returns the latest version in a version series.
+     * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
      * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
@@ -599,6 +619,10 @@
     /**
      * Returns the latest version in a version series.
      * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
+     * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
      * @param context
@@ -613,6 +637,14 @@
     /**
      * Returns the latest version in a version series.
      * 
+     * Some repositories throw an exception if the document is not versionable;
+     * others just return the unversioned document. To avoid surprises, check
+     * first whether the document is versionable or not.
+     * 
+     * If {@code major} == {@code true} and the version series doesn't contain a
+     * major version, the repository is supposed to throw a
+     * {@link CmisObjectNotFoundException}.
+     * 
      * @param objectId
      *            the document ID of an arbitrary version in the version series
      * @param major
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/DocumentImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/DocumentImpl.java
index 0de73bd..491b5fd 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/DocumentImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/DocumentImpl.java
@@ -33,6 +33,7 @@
 
 import org.apache.chemistry.opencmis.client.api.CmisObject;
 import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.DocumentType;
 import org.apache.chemistry.opencmis.client.api.ObjectFactory;
 import org.apache.chemistry.opencmis.client.api.ObjectId;
 import org.apache.chemistry.opencmis.client.api.ObjectType;
@@ -67,6 +68,21 @@
         initialize(session, objectType, objectData, context);
     }
 
+    @Override
+    public DocumentType getDocumentType() {
+        ObjectType objectType = super.getType();
+        if (objectType instanceof DocumentType) {
+            return (DocumentType) objectType;
+        } else {
+            throw new ClassCastException("Object type is not a document type.");
+        }
+    }
+
+    @Override
+    public boolean isVersionable() {
+        return Boolean.TRUE.equals(getDocumentType().isVersionable());
+    }
+
     // properties
 
     @Override
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/FolderImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/FolderImpl.java
index 790cbd1..f6bd578 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/FolderImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/FolderImpl.java
@@ -30,6 +30,7 @@
 import org.apache.chemistry.opencmis.client.api.Document;
 import org.apache.chemistry.opencmis.client.api.FileableCmisObject;
 import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.client.api.FolderType;
 import org.apache.chemistry.opencmis.client.api.Item;
 import org.apache.chemistry.opencmis.client.api.ItemIterable;
 import org.apache.chemistry.opencmis.client.api.ObjectFactory;
@@ -70,6 +71,16 @@
     }
 
     @Override
+    public FolderType getFolderType() {
+        ObjectType objectType = super.getType();
+        if (objectType instanceof FolderType) {
+            return (FolderType) objectType;
+        } else {
+            throw new ClassCastException("Object type is not a folder type.");
+        }
+    }
+
+    @Override
     public Document createDocument(Map<String, ?> properties, ContentStream contentStream,
             VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces,
             OperationContext context) {
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/ItemImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/ItemImpl.java
index f512628..0119e0f 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/ItemImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/ItemImpl.java
@@ -19,6 +19,7 @@
 package org.apache.chemistry.opencmis.client.runtime;
 
 import org.apache.chemistry.opencmis.client.api.Item;
+import org.apache.chemistry.opencmis.client.api.ItemType;
 import org.apache.chemistry.opencmis.client.api.ObjectType;
 import org.apache.chemistry.opencmis.client.api.OperationContext;
 import org.apache.chemistry.opencmis.commons.data.ObjectData;
@@ -34,4 +35,14 @@
         initialize(session, objectType, objectData, context);
     }
 
+    @Override
+    public ItemType getItemType() {
+        ObjectType objectType = super.getType();
+        if (objectType instanceof ItemType) {
+            return (ItemType) objectType;
+        } else {
+            throw new ClassCastException("Object type is not an item type.");
+        }
+    }
+
 }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/PolicyImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/PolicyImpl.java
index c4a0c32..a1df32a 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/PolicyImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/PolicyImpl.java
@@ -21,6 +21,7 @@
 import org.apache.chemistry.opencmis.client.api.ObjectType;
 import org.apache.chemistry.opencmis.client.api.OperationContext;
 import org.apache.chemistry.opencmis.client.api.Policy;
+import org.apache.chemistry.opencmis.client.api.PolicyType;
 import org.apache.chemistry.opencmis.commons.PropertyIds;
 import org.apache.chemistry.opencmis.commons.data.ObjectData;
 
@@ -36,8 +37,17 @@
     }
 
     @Override
+    public PolicyType getPolicyType() {
+        ObjectType objectType = super.getType();
+        if (objectType instanceof PolicyType) {
+            return (PolicyType) objectType;
+        } else {
+            throw new ClassCastException("Object type is not a policy type.");
+        }
+    }
+
+    @Override
     public String getPolicyText() {
         return getPropertyValue(PropertyIds.POLICY_TEXT);
     }
-
 }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/RelationshipImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/RelationshipImpl.java
index eea4fab..7fdde86 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/RelationshipImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/RelationshipImpl.java
@@ -23,6 +23,7 @@
 import org.apache.chemistry.opencmis.client.api.ObjectType;
 import org.apache.chemistry.opencmis.client.api.OperationContext;
 import org.apache.chemistry.opencmis.client.api.Relationship;
+import org.apache.chemistry.opencmis.client.api.RelationshipType;
 import org.apache.chemistry.opencmis.commons.PropertyIds;
 import org.apache.chemistry.opencmis.commons.data.ObjectData;
 
@@ -38,6 +39,16 @@
     }
 
     @Override
+    public RelationshipType getRelationshipType() {
+        ObjectType objectType = super.getType();
+        if (objectType instanceof RelationshipType) {
+            return (RelationshipType) objectType;
+        } else {
+            throw new ClassCastException("Object type is not a relationship type.");
+        }
+    }
+
+    @Override
     public CmisObject getSource() {
         return getSource(getSession().getDefaultContext());
     }
diff --git a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionImpl.java b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionImpl.java
index db2bf54..5d3cfd2 100644
--- a/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionImpl.java
+++ b/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionImpl.java
@@ -37,6 +37,7 @@
 import org.apache.chemistry.opencmis.client.api.ChangeEvents;
 import org.apache.chemistry.opencmis.client.api.CmisObject;
 import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.DocumentType;
 import org.apache.chemistry.opencmis.client.api.Folder;
 import org.apache.chemistry.opencmis.client.api.ItemIterable;
 import org.apache.chemistry.opencmis.client.api.ObjectFactory;
@@ -646,16 +647,30 @@
         // first attempt: if we got a Document object, try getting the version
         // series ID from it
         if (objectId instanceof Document) {
-            versionSeriesId = ((Document) objectId).getVersionSeriesId();
+            Document sourceDoc = (Document) objectId;
+
+            if (!sourceDoc.isVersionable()) {
+                // if it is not versionable, a getObject() is sufficient
+                return (Document) getObject(sourceDoc, context);
+            }
+
+            versionSeriesId = sourceDoc.getVersionSeriesId();
         }
 
         // second attempt: if we have a Document object in the cache, retrieve
         // the version series ID form there
         if (versionSeriesId == null) {
             if (context.isCacheEnabled()) {
-                CmisObject sourceDoc = cache.getById(objectId.getId(), context.getCacheKey());
-                if (sourceDoc instanceof Document) {
-                    versionSeriesId = ((Document) sourceDoc).getVersionSeriesId();
+                CmisObject sourceObj = cache.getById(objectId.getId(), context.getCacheKey());
+                if (sourceObj instanceof Document) {
+                    Document sourceDoc = (Document) sourceObj;
+
+                    if (!sourceDoc.isVersionable()) {
+                        // if it is not versionable, a getObject() is sufficient
+                        return (Document) getObject(sourceDoc, context);
+                    }
+
+                    versionSeriesId = sourceDoc.getVersionSeriesId();
                 }
             }
         }
@@ -670,11 +685,20 @@
 
                 // get the document to find the version series ID
                 ObjectData sourceObjectData = binding.getObjectService().getObject(getRepositoryId(), objectId.getId(),
-                        PropertyIds.OBJECT_ID + "," + PropertyIds.VERSION_SERIES_ID, false, IncludeRelationships.NONE,
-                        "cmis:none", false, false, null);
+                        PropertyIds.OBJECT_ID + "," + PropertyIds.OBJECT_TYPE_ID + "," + PropertyIds.VERSION_SERIES_ID,
+                        false, IncludeRelationships.NONE, "cmis:none", false, false, null);
+
+                String objectTypeId = null;
 
                 if (sourceObjectData.getProperties() != null
                         && sourceObjectData.getProperties().getProperties() != null) {
+
+                    PropertyData<?> objectTypeIdProp = sourceObjectData.getProperties().getProperties()
+                            .get(PropertyIds.OBJECT_TYPE_ID);
+                    if (objectTypeIdProp != null && objectTypeIdProp.getFirstValue() instanceof String) {
+                        objectTypeId = (String) objectTypeIdProp.getFirstValue();
+                    }
+
                     PropertyData<?> verionsSeriesIdProp = sourceObjectData.getProperties().getProperties()
                             .get(PropertyIds.VERSION_SERIES_ID);
                     if (verionsSeriesIdProp != null && verionsSeriesIdProp.getFirstValue() instanceof String) {
@@ -682,8 +706,16 @@
                     }
                 }
 
-                // the Web Services binding needs the version series ID -> fail
+                // the Web Services binding needs the version series ID
                 if (versionSeriesId == null) {
+
+                    ObjectType type = getTypeDefinition(objectTypeId);
+                    if (type instanceof DocumentType && Boolean.FALSE.equals(((DocumentType) type).isVersionable())) {
+                        // if the document is not versionable, we don't need a
+                        // version series ID
+                        return (Document) getObject(objectId, context);
+                    }
+
                     throw new IllegalArgumentException("Object is not a document or not versionable!");
                 }
             }