chemistry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From f...@apache.org
Subject svn commit: r1061233 [8/15] - in /incubator/chemistry/dotcmis: branches/ tags/ trunk/ trunk/DotCMIS/ trunk/DotCMIS/Properties/ trunk/DotCMIS/Service References/ trunk/DotCMIS/Service References/CMISWebServicesReference/ trunk/DotCMIS/binding/ trunk/Dot...
Date Thu, 20 Jan 2011 11:38:50 GMT
Added: incubator/chemistry/dotcmis/trunk/DotCMIS/binding/atompub/atompub.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/binding/atompub/atompub.cs?rev=1061233&view=auto
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/binding/atompub/atompub.cs (added)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/binding/atompub/atompub.cs Thu Jan 20 11:38:48 2011
@@ -0,0 +1,2818 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using DotCMIS.Binding.Impl;
+using DotCMIS.Binding.Services;
+using DotCMIS.CMISWebServicesReference;
+using DotCMIS.Data;
+using DotCMIS.Data.Extensions;
+using DotCMIS.Exceptions;
+using DotCMIS.Enums;
+
+namespace DotCMIS.Binding.AtomPub
+{
+    /// <summary>
+    /// AtomPub binding SPI.
+    /// </summary>
+    internal class CmisAtomPubSpi : ICmisSpi
+    {
+        private RepositoryService repositoryService;
+        private NavigationService navigationService;
+        private ObjectService objectService;
+        private VersioningService versioningService;
+        private DiscoveryService discoveryService;
+        private MultiFilingService multiFilingService;
+        private RelationshipService relationshipService;
+        private PolicyService policyService;
+        private AclService aclService;
+
+        public void initialize(BindingSession session)
+        {
+            repositoryService = new RepositoryService(session);
+            navigationService = new NavigationService(session);
+            objectService = new ObjectService(session);
+            versioningService = new VersioningService(session);
+            discoveryService = new DiscoveryService(session);
+            multiFilingService = new MultiFilingService(session);
+            relationshipService = new RelationshipService(session);
+            policyService = new PolicyService(session);
+            aclService = new AclService(session);
+        }
+
+        public IRepositoryService GetRepositoryService()
+        {
+            return repositoryService;
+        }
+
+        public INavigationService GetNavigationService()
+        {
+            return navigationService;
+        }
+
+        public IObjectService GetObjectService()
+        {
+            return objectService;
+        }
+
+        public IVersioningService GetVersioningService()
+        {
+            return versioningService;
+        }
+
+        public IRelationshipService GetRelationshipService()
+        {
+            return relationshipService;
+        }
+
+        public IDiscoveryService GetDiscoveryService()
+        {
+            return discoveryService;
+        }
+
+        public IMultiFilingService GetMultiFilingService()
+        {
+            return multiFilingService;
+        }
+
+        public IAclService GetAclService()
+        {
+            return aclService;
+        }
+
+        public IPolicyService GetPolicyService()
+        {
+            return policyService;
+        }
+
+        public void ClearAllCaches()
+        {
+            // nothing to do
+        }
+
+        public void ClearRepositoryCache(string repositoryId)
+        {
+            // nothing to do
+        }
+
+        public void Close()
+        {
+            // nothing to do
+        }
+    }
+
+    internal abstract class AbstractAtomPubService
+    {
+        protected const string NameCollection = "collection";
+        protected const string NameURITemplate = "uritemplate";
+        protected const string NamePathSegment = "pathSegment";
+        protected const string NameRelativePathSegment = "relativePathSegment";
+        protected const string NameNumItems = "numItems";
+
+        private const string SessionLinkCache = "org.apache.chemistry.dotcmis.binding.atompub.linkcache";
+
+        protected enum IdentifierType
+        {
+            Id, Path
+        };
+
+        protected BindingSession Session { get; set; }
+
+
+        // ---- link cache ----
+
+        protected LinkCache GetLinkCache()
+        {
+            LinkCache linkCache = (LinkCache)Session.GetValue(SessionLinkCache);
+            if (linkCache == null)
+            {
+                linkCache = new LinkCache(Session);
+                Session.PutValue(SessionLinkCache, linkCache);
+            }
+
+            return linkCache;
+        }
+
+        protected string GetLink(string repositoryId, string id, string rel, string type)
+        {
+            if (repositoryId == null)
+            {
+                throw new CmisInvalidArgumentException("Repository id must be set!");
+            }
+
+            if (id == null)
+            {
+                throw new CmisInvalidArgumentException("Object id must be set!");
+            }
+
+            return GetLinkCache().GetLink(repositoryId, id, rel, type);
+        }
+
+        protected string GetLink(string repositoryId, string id, string rel)
+        {
+            return GetLink(repositoryId, id, rel, null);
+        }
+
+        protected string LoadLink(string repositoryId, string id, string rel, string type)
+        {
+            string link = GetLink(repositoryId, id, rel, type);
+            if (link == null)
+            {
+                GetObjectInternal(repositoryId, IdentifierType.Id, id, ReturnVersion.This, null, null, null, null, null, null, null);
+                link = GetLink(repositoryId, id, rel, type);
+            }
+
+            return link;
+        }
+
+        protected void AddLink(string repositoryId, string id, string rel, string type, string link)
+        {
+            GetLinkCache().AddLink(repositoryId, id, rel, type, link);
+        }
+
+        protected void AddLink(string repositoryId, string id, AtomLink link)
+        {
+            GetLinkCache().AddLink(repositoryId, id, link.Rel, link.Type, link.Href);
+        }
+
+        protected void RemoveLinks(string repositoryId, string id)
+        {
+            GetLinkCache().RemoveLinks(repositoryId, id);
+        }
+
+        protected void LockLinks()
+        {
+            GetLinkCache().LockLinks();
+        }
+
+        protected void UnlockLinks()
+        {
+            GetLinkCache().UnlockLinks();
+        }
+
+        protected string GetTypeLink(string repositoryId, string typeId, string rel, string type)
+        {
+            if (repositoryId == null)
+            {
+                throw new CmisInvalidArgumentException("Repository id must be set!");
+            }
+
+            if (typeId == null)
+            {
+                throw new CmisInvalidArgumentException("Type id must be set!");
+            }
+
+            return GetLinkCache().GetTypeLink(repositoryId, typeId, rel, type);
+        }
+
+        protected string GetTypeLink(string repositoryId, string typeId, string rel)
+        {
+            return GetTypeLink(repositoryId, typeId, rel, null);
+        }
+
+        protected string LoadTypeLink(string repositoryId, string typeId, string rel, string type)
+        {
+            string link = GetTypeLink(repositoryId, typeId, rel, type);
+            if (link == null)
+            {
+                GetTypeDefinitionInternal(repositoryId, typeId);
+                link = GetTypeLink(repositoryId, typeId, rel, type);
+            }
+
+            return link;
+        }
+
+        protected void AddTypeLink(string repositoryId, string typeId, string rel, string type, string link)
+        {
+            GetLinkCache().AddTypeLink(repositoryId, typeId, rel, type, link);
+        }
+
+        protected void AddTypeLink(string repositoryId, string typeId, AtomLink link)
+        {
+            GetLinkCache().AddTypeLink(repositoryId, typeId, link.Rel, link.Type, link.Href);
+        }
+
+        protected void RemoveTypeLinks(string repositoryId, string id)
+        {
+            GetLinkCache().RemoveTypeLinks(repositoryId, id);
+        }
+
+        protected void LockTypeLinks()
+        {
+            GetLinkCache().LockTypeLinks();
+        }
+
+        protected void UnlockTypeLinks()
+        {
+            GetLinkCache().UnlockTypeLinks();
+        }
+
+        protected string GetCollection(string repositoryId, string collection)
+        {
+            return GetLinkCache().GetCollection(repositoryId, collection);
+        }
+
+        protected string LoadCollection(string repositoryId, string collection)
+        {
+            string link = GetCollection(repositoryId, collection);
+            if (link == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                link = GetCollection(repositoryId, collection);
+            }
+
+            return link;
+        }
+
+        protected void AddCollection(string repositoryId, string collection, string link)
+        {
+            GetLinkCache().AddCollection(repositoryId, collection, link);
+        }
+
+        protected void AddCollection(string repositoryId, IDictionary<string, string> colDict)
+        {
+            string collection = null;
+            colDict.TryGetValue("collectionType", out collection);
+
+            string link = null;
+            colDict.TryGetValue("href", out link);
+
+            AddCollection(repositoryId, collection, link);
+        }
+
+        protected string GetRepositoryLink(string repositoryId, string rel)
+        {
+            return GetLinkCache().GetRepositoryLink(repositoryId, rel);
+        }
+
+        protected string LoadRepositoryLink(string repositoryId, string rel)
+        {
+            string link = GetRepositoryLink(repositoryId, rel);
+            if (link == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                link = GetRepositoryLink(repositoryId, rel);
+            }
+
+            return link;
+        }
+
+        protected void AddRepositoryLink(string repositoryId, string rel, string link)
+        {
+            GetLinkCache().AddRepositoryLink(repositoryId, rel, link);
+        }
+
+        protected void AddRepositoryLink(string repositoryId, AtomLink link)
+        {
+            AddRepositoryLink(repositoryId, link.Rel, link.Href);
+        }
+
+        protected string GetTemplateLink(string repositoryId, string type, IDictionary<string, object> parameters)
+        {
+            return GetLinkCache().GetTemplateLink(repositoryId, type, parameters);
+        }
+
+        protected string LoadTemplateLink(string repositoryId, string type, IDictionary<string, object> parameters)
+        {
+            string link = GetTemplateLink(repositoryId, type, parameters);
+            if (link == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                link = GetTemplateLink(repositoryId, type, parameters);
+            }
+
+            return link;
+        }
+
+        protected void AddTemplate(string repositoryId, string type, string link)
+        {
+            GetLinkCache().AddTemplate(repositoryId, type, link);
+        }
+
+        protected void AddTemplate(string repositoryId, IDictionary<string, string> tempDict)
+        {
+            string type = null;
+            tempDict.TryGetValue("type", out type);
+
+            string template = null;
+            tempDict.TryGetValue("template", out template);
+
+            AddTemplate(repositoryId, type, template);
+        }
+
+        // ---- exceptions ----
+
+        protected CmisBaseException ConvertStatusCode(HttpStatusCode code, string message, string errorContent, Exception e)
+        {
+            switch (code)
+            {
+                case HttpStatusCode.BadRequest:
+                    return new CmisInvalidArgumentException(message, errorContent, e);
+                case HttpStatusCode.NotFound:
+                    return new CmisObjectNotFoundException(message, errorContent, e);
+                case HttpStatusCode.Forbidden:
+                    return new CmisPermissionDeniedException(message, errorContent, e);
+                case HttpStatusCode.MethodNotAllowed:
+                    return new CmisNotSupportedException(message, errorContent, e);
+                case HttpStatusCode.Conflict:
+                    return new CmisConstraintException(message, errorContent, e);
+                default:
+                    return new CmisRuntimeException(message, errorContent, e);
+            }
+        }
+
+        protected void ThrowLinkException(String repositoryId, String id, String rel, String type)
+        {
+            int index = GetLinkCache().CheckLink(repositoryId, id, rel, type);
+
+            switch (index)
+            {
+                case 0:
+                    throw new CmisObjectNotFoundException("Unknown repository!");
+                case 1:
+                    throw new CmisObjectNotFoundException("Unknown object!");
+                case 2:
+                    throw new CmisNotSupportedException("Operation not supported by the repository for this object!");
+                case 3:
+                    throw new CmisNotSupportedException("No link with matching media type!");
+                case 4:
+                    throw new CmisRuntimeException("Nothing wrong! Either this is a bug or threading issue.");
+                default:
+                    throw new CmisRuntimeException("Unknown error!");
+            }
+        }
+
+        // ---- helpers ----
+
+        protected T Parse<T>(Stream stream) where T : AtomBase
+        {
+            AtomPubParser parser = new AtomPubParser(stream);
+
+            try
+            {
+                parser.Parse();
+            }
+            catch (Exception e)
+            {
+                throw new CmisConnectionException("Parsing exception!", e);
+            }
+
+            AtomBase parseResult = parser.GetParseResults();
+
+            if (!typeof(T).IsInstanceOfType(parseResult))
+            {
+                throw new CmisConnectionException("Unexpected document! Received "
+                        + (parseResult == null ? "something unknown" : parseResult.GetAtomType()) + "!");
+            }
+
+            return (T)parseResult;
+        }
+
+        protected HttpUtils.Response Read(UrlBuilder url)
+        {
+            HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session);
+
+            if (resp.StatusCode != HttpStatusCode.OK)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            return resp;
+        }
+
+        protected HttpUtils.Response Post(UrlBuilder url, string contentType, HttpUtils.Output writer)
+        {
+            HttpUtils.Response resp = HttpUtils.InvokePOST(url, contentType, writer, Session);
+
+            if (resp.StatusCode != HttpStatusCode.Created)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            return resp;
+        }
+
+        protected HttpUtils.Response Put(UrlBuilder url, string contentType, HttpUtils.Output writer)
+        {
+            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, writer, Session);
+
+            if ((int)resp.StatusCode < 200 || (int)resp.StatusCode > 299)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            return resp;
+        }
+
+        protected void Delete(UrlBuilder url)
+        {
+            HttpUtils.Response resp = HttpUtils.InvokeDELETE(url, Session);
+
+            if (resp.StatusCode != HttpStatusCode.NoContent)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+        }
+
+        protected string GetServiceDocURL()
+        {
+            return Session.GetValue(SessionParameter.AtomPubUrl) as string;
+        }
+
+        protected bool IsNextLink(AtomElement element)
+        {
+            return AtomPubConstants.RelNext == ((AtomLink)element.Object).Rel;
+        }
+
+        protected bool IsStr(string name, AtomElement element)
+        {
+            return name == element.LocalName && element.Object is string;
+        }
+
+        protected bool IsInt(string name, AtomElement element)
+        {
+            return name == element.LocalName && element.Object is Int64;
+        }
+
+        // ---- common methods ----
+
+        protected cmisObjectType CreateIdObject(string objectId)
+        {
+            cmisObjectType cmisObject = new cmisObjectType();
+
+            cmisPropertiesType properties = new cmisPropertiesType();
+            cmisObject.properties = properties;
+
+            cmisPropertyId idProperty = new cmisPropertyId();
+            properties.Items = new cmisProperty[] { idProperty };
+            idProperty.propertyDefinitionId = PropertyIds.ObjectId;
+            idProperty.value = new string[] { objectId };
+
+            return cmisObject;
+        }
+
+        protected bool IsAclMergeRequired(IAcl addAces, IAcl removeAces)
+        {
+            return (addAces != null && addAces.Aces != null && addAces.Aces.Count > 0)
+                    || (removeAces != null && removeAces.Aces != null && removeAces.Aces.Count > 0);
+        }
+
+        protected IAcl MergeAcls(IAcl originalAces, IAcl addAces, IAcl removeAces)
+        {
+            IDictionary<string, HashSet<string>> originals = ConvertAclToDict(originalAces);
+            IDictionary<string, HashSet<string>> adds = ConvertAclToDict(addAces);
+            IDictionary<string, HashSet<string>> removes = ConvertAclToDict(removeAces);
+            IList<IAce> newACEs = new List<IAce>();
+
+            // iterate through the original ACEs
+            foreach (KeyValuePair<string, HashSet<string>> ace in originals)
+            {
+                // add permissions
+                HashSet<string> addPermissions = adds[ace.Key];
+                if (addPermissions != null)
+                {
+                    foreach (string perm in addPermissions)
+                    {
+                        ace.Value.Add(perm);
+                    }
+                }
+
+                // remove permissions
+                HashSet<string> removePermissions = removes[ace.Key];
+                if (removePermissions != null)
+                {
+                    foreach (string perm in removePermissions)
+                    {
+                        ace.Value.Remove(perm);
+                    }
+                }
+
+                // create new ACE
+                Ace resultAce = new Ace();
+                Principal resultPrincipal = new Principal();
+                resultPrincipal.Id = ace.Key;
+                resultAce.Principal = resultPrincipal;
+                resultAce.Permissions = new List<string>(ace.Value);
+
+                newACEs.Add(resultAce);
+            }
+
+            // find all ACEs that should be added but are not in the original ACE list
+            foreach (KeyValuePair<string, HashSet<string>> ace in adds)
+            {
+                if (!originals.ContainsKey(ace.Key) && ace.Value.Count > 0)
+                {
+                    Ace resultAce = new Ace();
+                    Principal resultPrincipal = new Principal();
+                    resultPrincipal.Id = ace.Key;
+                    resultAce.Principal = resultPrincipal;
+                    resultAce.Permissions = new List<string>(ace.Value);
+
+                    newACEs.Add(resultAce);
+                }
+            }
+
+            Acl result = new Acl();
+            result.Aces = newACEs;
+
+            return result;
+        }
+
+        private IDictionary<string, HashSet<string>> ConvertAclToDict(IAcl acl)
+        {
+            IDictionary<string, HashSet<string>> result = new Dictionary<string, HashSet<string>>();
+
+            if (acl == null || acl.Aces == null)
+            {
+                return result;
+            }
+
+            foreach (Ace ace in acl.Aces)
+            {
+                // don't consider indirect ACEs - we can't change them
+                if (!ace.IsDirect)
+                {
+                    // ignore
+                    continue;
+                }
+
+                // although a principal must not be null, check it
+                if ((ace.Principal == null) || (ace.Principal.Id == null))
+                {
+                    // ignore
+                    continue;
+                }
+
+                if (ace.Permissions == null)
+                {
+                    continue;
+                }
+
+                HashSet<string> permissions;
+                if (!result.TryGetValue(ace.Principal.Id, out permissions))
+                {
+                    permissions = new HashSet<string>();
+                    result[ace.Principal.Id] = permissions;
+                }
+
+                foreach (string perm in ace.Permissions)
+                {
+                    permissions.Add(perm);
+                }
+            }
+
+            return result;
+        }
+
+
+        protected AtomAcl UpdateAcl(string repositoryId, string objectId, IAcl acl, AclPropagation? aclPropagation)
+        {
+            // find the link
+            String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelACL, AtomPubConstants.MediatypeACL);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelACL, AtomPubConstants.MediatypeACL);
+            }
+
+            UrlBuilder aclUrl = new UrlBuilder(link);
+            aclUrl.AddParameter(AtomPubConstants.ParamACLPropagation, aclPropagation);
+
+            // set up object and writer
+            cmisAccessControlListType cmisAcl = Converter.Convert(acl);
+            HttpUtils.Output output = delegate(Stream stream)
+            {
+                AtomWriter.AclSerializer.Serialize(stream, cmisAcl);
+            };
+
+            // update
+            HttpUtils.Response resp = Put(aclUrl, AtomPubConstants.MediatypeACL, output);
+
+            // parse new acl
+            return Parse<AtomAcl>(resp.Stream);
+        }
+
+        protected IList<IRepositoryInfo> GetRepositoriesInternal(string repositoryId)
+        {
+            IList<IRepositoryInfo> repInfos = new List<IRepositoryInfo>();
+
+            // retrieve service doc
+            UrlBuilder url = new UrlBuilder(GetServiceDocURL());
+            url.AddParameter(AtomPubConstants.ParamRepositoryId, repositoryId);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            ServiceDoc serviceDoc = Parse<ServiceDoc>(resp.Stream);
+
+            // walk through the workspaces
+            foreach (RepositoryWorkspace ws in serviceDoc.GetWorkspaces())
+            {
+                if (ws.Id == null)
+                {
+                    // found a non-CMIS workspace
+                    continue;
+                }
+
+                foreach (AtomElement element in ws.GetElements())
+                {
+                    if (element.LocalName == NameCollection)
+                    {
+                        AddCollection(ws.Id, (IDictionary<string, string>)element.Object);
+                    }
+                    else if (element.Object is AtomLink)
+                    {
+                        AddRepositoryLink(ws.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.LocalName == NameURITemplate)
+                    {
+                        AddTemplate(ws.Id, (IDictionary<string, string>)element.Object);
+                    }
+                    else if (element.Object is cmisRepositoryInfoType)
+                    {
+                        repInfos.Add(Converter.Convert((cmisRepositoryInfoType)element.Object));
+                    }
+                }
+            }
+
+            return repInfos;
+        }
+
+        protected IObjectData GetObjectInternal(string repositoryId, IdentifierType idOrPath, string objectIdOrPath,
+            ReturnVersion? returnVersion, string filter, bool? includeAllowableActions, IncludeRelationships? includeRelationships,
+            string renditionFilter, bool? includePolicyIds, bool? includeAcl, IExtensionsData extension)
+        {
+            IObjectData result = null;
+
+            Dictionary<string, object> parameters = new Dictionary<string, object>();
+            parameters[AtomPubConstants.ParamId] = objectIdOrPath;
+            parameters[AtomPubConstants.ParamPath] = objectIdOrPath;
+            parameters[AtomPubConstants.ParamReturnVersion] = returnVersion;
+            parameters[AtomPubConstants.ParamFilter] = filter;
+            parameters[AtomPubConstants.ParamAllowableActions] = includeAllowableActions;
+            parameters[AtomPubConstants.ParamACL] = includeAcl;
+            parameters[AtomPubConstants.ParamPolicyIds] = includePolicyIds;
+            parameters[AtomPubConstants.ParamRelationships] = includeRelationships;
+            parameters[AtomPubConstants.ParamRenditionFilter] = renditionFilter;
+
+            string link = LoadTemplateLink(repositoryId, (idOrPath == IdentifierType.Id ? AtomPubConstants.TemplateObjectById
+                    : AtomPubConstants.TemplateObjectByPath), parameters);
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            // workaround for missing template parameter in the CMIS spec
+            if ((returnVersion != null) && (returnVersion != ReturnVersion.This))
+            {
+                url.AddParameter(AtomPubConstants.ParamReturnVersion, returnVersion);
+            }
+
+            HttpUtils.Response resp = Read(url);
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            if (entry.Id == null)
+            {
+                throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
+            }
+
+            LockLinks();
+            try
+            {
+                RemoveLinks(repositoryId, entry.Id);
+
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisObjectType)
+                    {
+                        result = Converter.Convert((cmisObjectType)element.Object);
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+
+            return result;
+        }
+
+        protected ITypeDefinition GetTypeDefinitionInternal(string repositoryId, string typeId)
+        {
+            ITypeDefinition result = null;
+
+            Dictionary<string, object> parameters = new Dictionary<string, object>();
+            parameters[AtomPubConstants.ParamId] = typeId;
+
+            string link = LoadTemplateLink(repositoryId, AtomPubConstants.TemplateTypeById, parameters);
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            // read and parse
+            HttpUtils.Response resp = Read(new UrlBuilder(link));
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // we expect a CMIS entry
+            if (entry.Id == null)
+            {
+                throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
+            }
+
+            LockTypeLinks();
+            try
+            {
+                // clean up cache
+                RemoveTypeLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisTypeDefinitionType)
+                    {
+                        result = Converter.Convert((cmisTypeDefinitionType)element.Object);
+                    }
+                }
+            }
+            finally
+            {
+                UnlockTypeLinks();
+            }
+
+            return result;
+        }
+    }
+
+    internal class RepositoryService : AbstractAtomPubService, IRepositoryService
+    {
+        public RepositoryService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IList<IRepositoryInfo> GetRepositoryInfos(IExtensionsData extension)
+        {
+            return GetRepositoriesInternal(null);
+        }
+
+        public IRepositoryInfo GetRepositoryInfo(string repositoryId, IExtensionsData extension)
+        {
+            IList<IRepositoryInfo> repositoryInfos = GetRepositoriesInternal(repositoryId);
+
+            // find the repository
+            foreach (IRepositoryInfo info in repositoryInfos)
+            {
+                if (info.Id == null) { continue; }
+                if (info.Id == repositoryId) { return info; }
+            }
+
+            throw new CmisObjectNotFoundException("Repository not found!");
+        }
+
+        public ITypeDefinitionList GetTypeChildren(string repositoryId, string typeId, bool? includePropertyDefinitions,
+            long? maxItems, long? skipCount, IExtensionsData extension)
+        {
+            TypeDefinitionList result = new TypeDefinitionList();
+
+            // find the link
+            string link = null;
+            if (typeId == null)
+            {
+                link = LoadCollection(repositoryId, AtomPubConstants.CollectionTypes);
+            }
+            else
+            {
+                link = LoadTypeLink(repositoryId, typeId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository or type!");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamTypeId, typeId);
+            url.AddParameter(AtomPubConstants.ParamPropertyDefinitions, includePropertyDefinitions);
+            url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
+            url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // handle top level
+            foreach (AtomElement element in feed.GetElements())
+            {
+                if (element.Object is AtomLink)
+                {
+                    if (IsNextLink(element)) { result.HasMoreItems = true; }
+                }
+                else if (IsInt(NameNumItems, element))
+                {
+                    result.NumItems = (long)element.Object;
+                }
+            }
+
+            result.List = new List<ITypeDefinition>(feed.GetEntries().Count);
+
+            // get the children
+            foreach (AtomEntry entry in feed.GetEntries())
+            {
+                ITypeDefinition child = null;
+
+                LockTypeLinks();
+                try
+                {
+                    foreach (AtomElement element in entry.GetElements())
+                    {
+                        if (element.Object is AtomLink)
+                        {
+                            AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                        }
+                        else if (element.Object is cmisTypeDefinitionType)
+                        {
+                            child = Converter.Convert((cmisTypeDefinitionType)element.Object);
+                        }
+                    }
+                }
+                finally
+                {
+                    UnlockTypeLinks();
+                }
+
+                if (child != null)
+                {
+                    result.List.Add(child);
+                }
+            }
+
+            return result;
+        }
+
+        public IList<ITypeDefinitionContainer> GetTypeDescendants(string repositoryId, string typeId, long? depth,
+            bool? includePropertyDefinitions, IExtensionsData extension)
+        {
+            List<ITypeDefinitionContainer> result = new List<ITypeDefinitionContainer>();
+
+            // find the link
+            string link = null;
+            if (typeId == null)
+            {
+                link = LoadRepositoryLink(repositoryId, AtomPubConstants.RepRelTypeDesc);
+            }
+            else
+            {
+                link = LoadTypeLink(repositoryId, typeId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
+            }
+
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository or type!");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamTypeId, typeId);
+            url.AddParameter(AtomPubConstants.ParamDepth, depth);
+            url.AddParameter(AtomPubConstants.ParamPropertyDefinitions, includePropertyDefinitions);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // process tree
+            AddTypeDescendantsLevel(repositoryId, feed, result);
+
+            return result;
+        }
+
+        private void AddTypeDescendantsLevel(string repositoryId, AtomFeed feed, List<ITypeDefinitionContainer> containerList)
+        {
+            if (feed == null || feed.GetEntries().Count == 0)
+            {
+                return;
+            }
+
+            foreach (AtomEntry entry in feed.GetEntries())
+            {
+                TypeDefinitionContainer childContainer = null;
+                List<ITypeDefinitionContainer> childContainerList = new List<ITypeDefinitionContainer>();
+
+                // walk through the entry
+                LockTypeLinks();
+                try
+                {
+                    foreach (AtomElement element in entry.GetElements())
+                    {
+                        if (element.Object is AtomLink)
+                        {
+                            AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                        }
+                        else if (element.Object is cmisTypeDefinitionType)
+                        {
+                            childContainer = new TypeDefinitionContainer();
+                            childContainer.TypeDefinition = Converter.Convert((cmisTypeDefinitionType)element.Object);
+                        }
+                        else if (element.Object is AtomFeed)
+                        {
+                            AddTypeDescendantsLevel(repositoryId, (AtomFeed)element.Object, childContainerList);
+                        }
+                    }
+                }
+                finally
+                {
+                    UnlockTypeLinks();
+                }
+
+                if (childContainer != null)
+                {
+                    childContainer.Children = childContainerList;
+                    containerList.Add(childContainer);
+                }
+            }
+        }
+
+        public ITypeDefinition GetTypeDefinition(string repositoryId, string typeId, IExtensionsData extension)
+        {
+            return GetTypeDefinitionInternal(repositoryId, typeId);
+        }
+    }
+
+    internal class NavigationService : AbstractAtomPubService, INavigationService
+    {
+        public NavigationService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IObjectInFolderList GetChildren(string repositoryId, string folderId, string filter, string orderBy,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, long? maxItems, long? skipCount, IExtensionsData extension)
+        {
+            ObjectInFolderList result = new ObjectInFolderList();
+
+            // find the link
+            String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+            url.AddParameter(AtomPubConstants.ParamOrderBy, orderBy);
+            url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
+            url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
+            url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
+            url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // handle top level
+            foreach (AtomElement element in feed.GetElements())
+            {
+                if (element.Object is AtomLink)
+                {
+                    if (IsNextLink(element)) { result.HasMoreItems = true; }
+                }
+                else if (IsInt(NameNumItems, element))
+                {
+                    result.NumItems = (long)element.Object;
+                }
+            }
+
+            // get the children
+            if (feed.GetEntries().Count > 0)
+            {
+                result.Objects = new List<IObjectInFolderData>(feed.GetEntries().Count);
+
+                foreach (AtomEntry entry in feed.GetEntries())
+                {
+                    ObjectInFolderData child = null;
+                    String pathSegment = null;
+
+                    LockLinks();
+                    try
+                    {
+                        // clean up cache
+                        RemoveLinks(repositoryId, entry.Id);
+
+                        // walk through the entry
+                        foreach (AtomElement element in entry.GetElements())
+                        {
+                            if (element.Object is AtomLink)
+                            {
+                                AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                            }
+                            else if (IsStr(NamePathSegment, element))
+                            {
+                                pathSegment = (string)element.Object;
+                            }
+                            else if (element.Object is cmisObjectType)
+                            {
+                                child = new ObjectInFolderData();
+                                child.Object = Converter.Convert((cmisObjectType)element.Object);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        UnlockLinks();
+                    }
+
+                    if (child != null)
+                    {
+                        child.PathSegment = pathSegment;
+                        result.Objects.Add(child);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public IList<IObjectInFolderContainer> GetDescendants(string repositoryId, string folderId, long? depth, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, IExtensionsData extension)
+        {
+            IList<IObjectInFolderContainer> result = new List<IObjectInFolderContainer>();
+
+            // find the link
+            String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamDepth, depth);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+            url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
+            url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // process tree
+            AddDescendantsLevel(repositoryId, feed, result);
+
+            return result;
+        }
+
+        public IList<IObjectInFolderContainer> GetFolderTree(string repositoryId, string folderId, long? depth, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, IExtensionsData extension)
+        {
+            IList<IObjectInFolderContainer> result = new List<IObjectInFolderContainer>();
+
+            // find the link
+            string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelFolderTree, AtomPubConstants.MediatypeDescendants);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelFolderTree, AtomPubConstants.MediatypeDescendants);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamDepth, depth);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+            url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
+            url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // process tree
+            AddDescendantsLevel(repositoryId, feed, result);
+
+            return result;
+        }
+
+        public IList<IObjectParentData> GetObjectParents(string repositoryId, string objectId, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includeRelativePathSegment, IExtensionsData extension)
+        {
+            IList<IObjectParentData> result = new List<IObjectParentData>();
+
+            // find the link
+            String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeFeed);
+
+            if (link == null)
+            {
+                // root and unfiled objects have no UP link
+                return result;
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+            url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
+            url.AddParameter(AtomPubConstants.ParamRelativePathSegment, includeRelativePathSegment);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomBase atomBase = Parse<AtomBase>(resp.Stream);
+
+            if (atomBase is AtomFeed)
+            {
+                // it's a feed
+                AtomFeed feed = (AtomFeed)atomBase;
+
+                // walk through the feed
+                foreach (AtomEntry entry in feed.GetEntries())
+                {
+                    IObjectParentData objectParent = ProcessParentEntry(entry, repositoryId);
+
+                    if (objectParent != null)
+                    {
+                        result.Add(objectParent);
+                    }
+                }
+            }
+            else if (atomBase is AtomEntry)
+            {
+                // it's an entry
+                AtomEntry entry = (AtomEntry)atomBase;
+
+                IObjectParentData objectParent = ProcessParentEntry(entry, repositoryId);
+
+                if (objectParent != null)
+                {
+                    result.Add(objectParent);
+                }
+            }
+
+            return result;
+        }
+
+        private IObjectParentData ProcessParentEntry(AtomEntry entry, string repositoryId)
+        {
+            ObjectParentData result = null;
+            String relativePathSegment = null;
+
+            LockLinks();
+            try
+            {
+                // clean up cache
+                RemoveLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisObjectType)
+                    {
+                        result = new ObjectParentData();
+                        result.Object = Converter.Convert((cmisObjectType)element.Object);
+                    }
+                    else if (IsStr(NameRelativePathSegment, element))
+                    {
+                        relativePathSegment = (string)element.Object;
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+
+            if (result != null)
+            {
+                result.RelativePathSegment = relativePathSegment;
+            }
+
+            return result;
+        }
+
+        public IObjectData GetFolderParent(string repositoryId, string folderId, string filter, ExtensionsData extension)
+        {
+            IObjectData result = null;
+
+            // find the link
+            String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeEntry);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeEntry);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+
+            // read
+            HttpUtils.Response resp = Read(url);
+            AtomBase atomBase = Parse<AtomBase>(resp.Stream);
+
+            // get the entry
+            AtomEntry entry = null;
+            if (atomBase is AtomFeed)
+            {
+                AtomFeed feed = (AtomFeed)atomBase;
+                if (feed.GetEntries().Count == 0)
+                {
+                    throw new CmisRuntimeException("Parent feed is empty!");
+                }
+                entry = feed.GetEntries()[0];
+            }
+            else if (atomBase is AtomEntry)
+            {
+                entry = (AtomEntry)atomBase;
+            }
+            else
+            {
+                throw new CmisRuntimeException("Unexpected document!");
+            }
+
+            LockLinks();
+            try
+            {
+                // clean up cache
+                RemoveLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisObjectType)
+                    {
+                        result = Converter.Convert((cmisObjectType)element.Object);
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+
+            return result;
+        }
+
+        public IObjectList GetCheckedOutDocs(string repositoryId, string folderId, string filter, string orderBy,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            long? maxItems, long? skipCount, IExtensionsData extension)
+        {
+            ObjectList result = new ObjectList();
+
+            // find the link
+            String link = LoadCollection(repositoryId, AtomPubConstants.CollectionCheckedout);
+
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamFolderId, folderId);
+            url.AddParameter(AtomPubConstants.ParamFilter, filter);
+            url.AddParameter(AtomPubConstants.ParamOrderBy, orderBy);
+            url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
+            url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
+            url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomFeed feed = Parse<AtomFeed>(resp.Stream);
+
+            // handle top level
+            foreach (AtomElement element in feed.GetElements())
+            {
+                if (element.Object is AtomLink)
+                {
+                    if (IsNextLink(element))
+                    {
+                        result.HasMoreItems = true;
+                    }
+                }
+                else if (IsInt(NameNumItems, element))
+                {
+                    result.NumItems = (long)element.Object;
+                }
+            }
+
+            // get the documents
+            if (feed.GetEntries().Count > 0)
+            {
+                result.Objects = new List<IObjectData>(feed.GetEntries().Count);
+
+                foreach (AtomEntry entry in feed.GetEntries())
+                {
+                    IObjectData child = null;
+
+                    LockLinks();
+                    try
+                    {
+                        // clean up cache
+                        RemoveLinks(repositoryId, entry.Id);
+
+                        // walk through the entry
+                        foreach (AtomElement element in entry.GetElements())
+                        {
+                            if (element.Object is AtomLink)
+                            {
+                                AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                            }
+                            else if (element.Object is cmisObjectType)
+                            {
+                                child = Converter.Convert((cmisObjectType)element.Object);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        UnlockLinks();
+                    }
+
+                    if (child != null)
+                    {
+                        result.Objects.Add(child);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        private void AddDescendantsLevel(String repositoryId, AtomFeed feed, IList<IObjectInFolderContainer> containerList)
+        {
+            if ((feed == null) || (feed.GetEntries().Count == 0))
+            {
+                return;
+            }
+
+            // walk through the feed
+            foreach (AtomEntry entry in feed.GetEntries())
+            {
+                ObjectInFolderData objectInFolder = null;
+                string pathSegment = null;
+                IList<IObjectInFolderContainer> childContainerList = new List<IObjectInFolderContainer>();
+
+                LockLinks();
+                try
+                {
+                    // clean up cache
+                    RemoveLinks(repositoryId, entry.Id);
+
+                    // walk through the entry
+                    foreach (AtomElement element in entry.GetElements())
+                    {
+                        if (element.Object is AtomLink)
+                        {
+                            AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                        }
+                        else if (element.Object is cmisObjectType)
+                        {
+                            objectInFolder = new ObjectInFolderData();
+                            objectInFolder.Object = Converter.Convert((cmisObjectType)element.Object);
+                        }
+                        else if (IsStr(NamePathSegment, element))
+                        {
+                            pathSegment = (string)element.Object;
+                        }
+                        else if (element.Object is AtomFeed)
+                        {
+                            AddDescendantsLevel(repositoryId, (AtomFeed)element.Object, childContainerList);
+                        }
+                    }
+                }
+                finally
+                {
+                    UnlockLinks();
+                }
+
+                if (objectInFolder != null)
+                {
+                    objectInFolder.PathSegment = pathSegment;
+                    ObjectInFolderContainer childContainer = new ObjectInFolderContainer();
+                    childContainer.Object = objectInFolder;
+                    childContainer.Children = childContainerList;
+                    containerList.Add(childContainer);
+                }
+            }
+        }
+    }
+
+    internal class ObjectService : AbstractAtomPubService, IObjectService
+    {
+        public ObjectService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public string CreateDocument(string repositoryId, IProperties properties, string folderId, IContentStream contentStream,
+            VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            CheckCreateProperties(properties);
+
+            // find the link
+            string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamVersioningState, versioningState);
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+            cmisObject.policyIds = Converter.ConvertPolicies(policies);
+
+            String mediaType = null;
+            Stream stream = null;
+
+            if (contentStream != null)
+            {
+                mediaType = contentStream.MimeType;
+                stream = contentStream.Stream;
+            }
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject, mediaType, stream);
+
+            // post the new folder object
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // handle ACL modifications
+            HandleAclModifications(repositoryId, entry, addAces, removeAces);
+
+            return entry.Id;
+        }
+
+        public string CreateDocumentFromSource(string repositoryId, string sourceId, IProperties properties, string folderId,
+            VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            throw new CmisNotSupportedException("createDocumentFromSource is not supported by the AtomPub binding!");
+        }
+
+        public string CreateFolder(string repositoryId, IProperties properties, string folderId, IList<string> policies,
+            IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            CheckCreateProperties(properties);
+
+            // find the link
+            string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+            cmisObject.policyIds = Converter.ConvertPolicies(policies);
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
+
+            // post the new folder object
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // handle ACL modifications
+            HandleAclModifications(repositoryId, entry, addAces, removeAces);
+
+            return entry.Id;
+        }
+
+        public string CreateRelationship(string repositoryId, IProperties properties, IList<string> policies, IAcl addAces,
+            IAcl removeAces, IExtensionsData extension)
+        {
+            CheckCreateProperties(properties);
+
+            // find source id
+            IPropertyData sourceIdProperty = properties[PropertyIds.SourceId];
+            if (!(sourceIdProperty is IPropertyId))
+            {
+                throw new CmisInvalidArgumentException("Source Id is not set!");
+            }
+
+            string sourceId = ((IPropertyId)sourceIdProperty).FirstValue;
+            if (sourceId == null)
+            {
+                throw new CmisInvalidArgumentException("Source Id is not set!");
+            }
+
+            // find the link
+            string link = LoadLink(repositoryId, sourceId, AtomPubConstants.RelRelationships, AtomPubConstants.MediatypeFeed);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, sourceId, AtomPubConstants.RelRelationships, AtomPubConstants.MediatypeFeed);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+            cmisObject.policyIds = Converter.ConvertPolicies(policies);
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
+
+            // post the new folder object
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // handle ACL modifications
+            HandleAclModifications(repositoryId, entry, addAces, removeAces);
+
+            return entry.Id;
+        }
+
+        public string CreatePolicy(string repositoryId, IProperties properties, string folderId, IList<string> policies,
+            IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            CheckCreateProperties(properties);
+
+            // find the link
+            string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+            cmisObject.policyIds = Converter.ConvertPolicies(policies);
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
+
+            // post the new folder object
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // handle ACL modifications
+            HandleAclModifications(repositoryId, entry, addAces, removeAces);
+
+            return entry.Id;
+        }
+
+        public IAllowableActions GetAllowableActions(string repositoryId, string objectId, IExtensionsData extension)
+        {
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelAllowableActions, AtomPubConstants.MediatypeAllowableAction);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelAllowableActions, AtomPubConstants.MediatypeAllowableAction);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+
+            // read and parse
+            HttpUtils.Response resp = Read(url);
+            AtomAllowableActions allowableActions = Parse<AtomAllowableActions>(resp.Stream);
+
+            return Converter.Convert(allowableActions.AllowableActions);
+        }
+
+        public IProperties GetProperties(string repositoryId, string objectId, string filter, IExtensionsData extension)
+        {
+            IObjectData obj = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This, filter,
+                    false, IncludeRelationships.None, "cmis:none", false, false, extension);
+
+            return obj.Properties;
+        }
+
+        public IList<IRenditionData> GetRenditions(string repositoryId, string objectId, string renditionFilter,
+            long? maxItems, long? skipCount, IExtensionsData extension)
+        {
+            IObjectData obj = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This,
+                PropertyIds.ObjectId, false, IncludeRelationships.None, renditionFilter, false, false, extension);
+
+            IList<IRenditionData> result = obj.Renditions;
+            if (result == null)
+            {
+                result = new List<IRenditionData>();
+            }
+
+            return result;
+        }
+
+        public IObjectData GetObject(string repositoryId, string objectId, string filter, bool? includeAllowableActions,
+            IncludeRelationships? includeRelationships, string renditionFilter, bool? includePolicyIds,
+            bool? includeAcl, IExtensionsData extension)
+        {
+            return GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This, filter, includeAllowableActions,
+                includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
+        }
+
+        public IObjectData GetObjectByPath(string repositoryId, string path, string filter, bool? includeAllowableActions,
+            IncludeRelationships? includeRelationships, string renditionFilter, bool? includePolicyIds, bool? includeAcl,
+            IExtensionsData extension)
+        {
+            return GetObjectInternal(repositoryId, IdentifierType.Path, path, ReturnVersion.This, filter, includeAllowableActions,
+                includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
+        }
+
+        public IContentStream GetContentStream(string repositoryId, string objectId, string streamId, long? offset, long? length,
+            IExtensionsData extension)
+        {
+            ContentStream result = new ContentStream();
+
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.LinkRelContent, null);
+
+            if (link == null)
+            {
+                throw new CmisConstraintException("No content stream");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamStreamId, streamId);
+
+            // get the content
+            if (offset != null && offset > Int32.MaxValue)
+            {
+                throw new CmisInvalidArgumentException("Offset >" + Int32.MaxValue);
+            }
+            if (length != null && length > Int32.MaxValue)
+            {
+                throw new CmisInvalidArgumentException("Length >" + Int32.MaxValue);
+            }
+            HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session, (int?)offset, (int?)length);
+
+            // check response code
+            if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.PartialContent)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            result.FileName = null;
+            result.Length = resp.ContentLength;
+            result.MimeType = resp.ContentType;
+            result.Stream = resp.Stream;
+
+            return result;
+        }
+
+        public void UpdateProperties(string repositoryId, ref string objectId, ref string changeToken, IProperties properties,
+            IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object id must be set!");
+            }
+
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
+
+            // update
+            HttpUtils.Response resp = Put(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse new entry
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // we expect a CMIS entry
+            if (entry.Id == null)
+            {
+                throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
+            }
+
+            // set object id
+            objectId = entry.Id;
+            changeToken = null;
+
+            LockLinks();
+            try
+            {
+                // clean up cache
+                RemoveLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisObjectType)
+                    {
+                        // extract new change token
+                        cmisObject = (cmisObjectType)element.Object;
+                        if (cmisObject.properties != null)
+                        {
+                            foreach (cmisProperty property in cmisObject.properties.Items)
+                            {
+                                if (PropertyIds.ChangeToken == property.propertyDefinitionId && property is cmisPropertyString)
+                                {
+                                    cmisPropertyString changeTokenProperty = (cmisPropertyString)property;
+                                    if (changeTokenProperty.value != null && changeTokenProperty.value.Length > 0)
+                                    {
+                                        changeToken = changeTokenProperty.value[0];
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+        }
+
+        public void MoveObject(string repositoryId, ref string objectId, string targetFolderId, string sourceFolderId,
+            IExtensionsData extension)
+        {
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object id must be set!");
+            }
+
+            if (targetFolderId == null || targetFolderId.Length == 0 || sourceFolderId == null || sourceFolderId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Source and target folder must be set!");
+            }
+
+            // find the link
+            String link = LoadLink(repositoryId, targetFolderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, targetFolderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamSourceFolderId, sourceFolderId);
+
+            // set up object and writer
+            AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId));
+
+            // post move request
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            objectId = entry.Id;
+        }
+
+        public void DeleteObject(string repositoryId, string objectId, bool? allVersions, IExtensionsData extension)
+        {
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamAllVersions, allVersions);
+
+            Delete(url);
+        }
+
+        public IFailedToDeleteData DeleteTree(string repositoryId, string folderId, bool? allVersions, UnfileObject? unfileObjects,
+            bool? continueOnFailure, ExtensionsData extension)
+        {
+            // find the link
+            String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamAllVersions, allVersions);
+            url.AddParameter(AtomPubConstants.ParamUnfildeObjects, unfileObjects);
+            url.AddParameter(AtomPubConstants.ParamContinueOnFailure, continueOnFailure);
+
+            // make the call
+            HttpUtils.Response resp = HttpUtils.InvokeDELETE(url, Session);
+
+            // check response code
+            if (resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.Accepted || resp.StatusCode == HttpStatusCode.NoContent)
+            {
+                return new FailedToDeleteData();
+            }
+
+            throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+        }
+
+        public void SetContentStream(string repositoryId, ref string objectId, bool? overwriteFlag, ref string changeToken,
+            IContentStream contentStream, IExtensionsData extension)
+        {
+            if (objectId == null)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // we need content
+            if (contentStream == null || contentStream.Stream == null || contentStream.MimeType == null)
+            {
+                throw new CmisInvalidArgumentException("Content must be set!");
+            }
+
+            // find the link
+            String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
+            url.AddParameter(AtomPubConstants.ParamOverwriteFlag, overwriteFlag);
+
+            HttpUtils.Output output = delegate(Stream stream)
+            {
+                int b;
+                byte[] buffer = new byte[4096];
+                while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > -1)
+                {
+                    stream.Write(buffer, 0, b);
+                }
+
+                contentStream.Stream.Close();
+            };
+
+            // send content
+            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, output, Session);
+
+            // check response code
+            if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created && resp.StatusCode != HttpStatusCode.NoContent)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            objectId = null;
+            changeToken = null;
+        }
+
+        public void DeleteContentStream(string repositoryId, ref string objectId, ref string changeToken, IExtensionsData extension)
+        {
+            if (objectId == null)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // find the link
+            String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            if (changeToken != null)
+            {
+                url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
+            }
+
+            Delete(url);
+
+            objectId = null;
+            changeToken = null;
+        }
+
+        // ---- internal ---
+
+        private void CheckCreateProperties(IProperties properties)
+        {
+            if (properties == null || properties.PropertyList == null)
+            {
+                throw new CmisInvalidArgumentException("Properties must be set!");
+            }
+
+            if (properties[PropertyIds.ObjectTypeId] == null)
+            {
+                throw new CmisInvalidArgumentException("Property " + PropertyIds.ObjectTypeId + " must be set!");
+            }
+
+            if (properties[PropertyIds.ObjectId] != null)
+            {
+                throw new CmisInvalidArgumentException("Property " + PropertyIds.ObjectId + " must not be set!");
+            }
+        }
+
+        private void HandleAclModifications(String repositoryId, AtomEntry entry, IAcl addAces, IAcl removeAces)
+        {
+            if (!IsAclMergeRequired(addAces, removeAces))
+            {
+                return;
+            }
+
+            IAcl originalAces = null;
+
+            // walk through the entry and find the current ACL
+            foreach (AtomElement element in entry.GetElements())
+            {
+                if (element.Object is cmisObjectType)
+                {
+                    // extract current ACL
+                    cmisObjectType cmisObject = (cmisObjectType)element.Object;
+                    originalAces = Converter.Convert(cmisObject.acl, cmisObject.exactACLSpecified ? (bool?)cmisObject.exactACL : null);
+
+                    break;
+                }
+            }
+
+            if (originalAces != null)
+            {
+                // merge and update ACL
+                IAcl newACL = MergeAcls(originalAces, addAces, removeAces);
+                if (newACL != null)
+                {
+                    UpdateAcl(repositoryId, entry.Id, newACL, null);
+                }
+            }
+        }
+    }
+
+    internal class VersioningService : AbstractAtomPubService, IVersioningService
+    {
+        public VersioningService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public void CheckOut(string repositoryId, ref string objectId, IExtensionsData extension, out bool? contentCopied)
+        {
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object id must be set!");
+            }
+
+            // find the link
+            String link = LoadCollection(repositoryId, AtomPubConstants.CollectionCheckedout);
+
+            if (link == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+
+            // set up object and writer
+            AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId));
+
+            // post move request
+            HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse the response
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            objectId = entry.Id;
+
+            LockLinks();
+            try
+            {
+                // clean up cache
+                RemoveLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+
+            contentCopied = null;
+        }
+
+        public void CancelCheckOut(string repositoryId, string objectId, IExtensionsData extension)
+        {
+            // find the link
+            String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+            }
+
+            Delete(new UrlBuilder(link));
+        }
+
+        public void CheckIn(string repositoryId, ref string objectId, bool? major, IProperties properties,
+            IContentStream contentStream, string checkinComment, List<string> policies, IAcl addAces, IAcl removeAces,
+            IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object id must be set!");
+            }
+
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
+            }
+
+            UrlBuilder url = new UrlBuilder(link);
+            url.AddParameter(AtomPubConstants.ParamCheckinComment, checkinComment);
+            url.AddParameter(AtomPubConstants.ParamMajor, major);
+            url.AddParameter(AtomPubConstants.ParamCheckIn, "true");
+
+            // set up object and writer
+            cmisObjectType cmisObject = new cmisObjectType();
+            cmisObject.properties = Converter.Convert(properties);
+            cmisObject.policyIds = Converter.ConvertPolicies(policies);
+
+            if (cmisObject.properties == null)
+            {
+                cmisObject.properties = new cmisPropertiesType();
+            }
+
+            string mediaType = null;
+            Stream stream = null;
+
+            if (contentStream != null)
+            {
+                mediaType = contentStream.MimeType;
+                stream = contentStream.Stream;
+            }
+
+            AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject, mediaType, stream);
+
+            // update
+            HttpUtils.Response resp = Put(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
+
+            // parse new entry
+            AtomEntry entry = Parse<AtomEntry>(resp.Stream);
+
+            // we expect a CMIS entry
+            if (entry.Id == null)
+            {
+                throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
+            }
+
+            // set object id
+            objectId = entry.Id;
+
+            IAcl originalAces = null;
+
+            LockLinks();
+            try
+            {
+                // clean up cache
+                RemoveLinks(repositoryId, entry.Id);
+
+                // walk through the entry
+                foreach (AtomElement element in entry.GetElements())
+                {
+                    if (element.Object is AtomLink)
+                    {
+                        AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
+                    }
+                    else if (element.Object is cmisObjectType)
+                    {
+                        // extract current ACL
+                        cmisObject = (cmisObjectType)element.Object;
+                        originalAces = Converter.Convert(cmisObject.acl, cmisObject.exactACLSpecified ? (bool?)cmisObject.exactACL : null);
+                    }
+                }
+            }
+            finally
+            {
+                UnlockLinks();
+            }
+
+            // handle ACL modifications
+            if ((originalAces != null) && (IsAclMergeRequired(addAces, removeAces)))
+            {
+                // merge and update ACL
+                IAcl newACL = MergeAcls(originalAces, addAces, removeAces);
+                if (newACL != null)
+                {
+                    UpdateAcl(repositoryId, entry.Id, newACL, null);
+                }
+            }
+        }
+
+        public IObjectData GetObjectOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
+            string filter, bool? includeAllowableActions, IncludeRelationships? includeRelationships,
+            string renditionFilter, bool? includePolicyIds, bool? includeAcl, IExtensionsData extension)
+        {
+            ReturnVersion returnVersion = ReturnVersion.Latest;
+            if (major)
+            {
+                returnVersion = ReturnVersion.LatestMajor;
+            }
+
+            return GetObjectInternal(repositoryId, IdentifierType.Id, objectId, returnVersion, filter,
+                    includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
+        }
+
+        public IProperties GetPropertiesOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
+            string filter, IExtensionsData extension)
+        {
+            ReturnVersion returnVersion = ReturnVersion.Latest;
+            if (major)
+            {
+                returnVersion = ReturnVersion.LatestMajor;
+            }
+
+            IObjectData objectData = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, returnVersion, filter,
+                    false, IncludeRelationships.None, "cmis:none", false, false, extension);
+
+            return objectData.Properties;
+        }
+
+        public IList<IObjectData> GetAllVersions(string repositoryId, string objectId, string versionSeriesId, string filter,
+            bool? includeAllowableActions, IExtensionsData extension)
+        {
+            IList<IObjectData> result = new List<IObjectData>();
+
+            // find the link
+            string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelVersionHistory, AtomPubConstants.MediatypeFeed);
+
+            if (link == null)
+            {
+                ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelVersionHistory, AtomPubConstants.MediatypeFeed);
+            }
+

[... 559 lines stripped ...]


Mime
View raw message