You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2015/07/20 10:48:59 UTC

svn commit: r1691890 [9/14] - in /chemistry/portcmis: ./ PortCMIS/ PortCMIS/Properties/ PortCMIS/binding/ PortCMIS/binding/atompub/ PortCMIS/binding/browser/ PortCMIS/binding/browser/json/ PortCMIS/client/ PortCMIS/const/ PortCMIS/data/ PortCMIS/enum/ ...

Added: chemistry/portcmis/PortCMIS/client/ClientImpl.cs
URL: http://svn.apache.org/viewvc/chemistry/portcmis/PortCMIS/client/ClientImpl.cs?rev=1691890&view=auto
==============================================================================
--- chemistry/portcmis/PortCMIS/client/ClientImpl.cs (added)
+++ chemistry/portcmis/PortCMIS/client/ClientImpl.cs Mon Jul 20 08:48:57 2015
@@ -0,0 +1,1625 @@
+/*
+* 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 PortCMIS.Binding;
+using PortCMIS.Binding.Services;
+using PortCMIS.Data;
+using PortCMIS.Enums;
+using PortCMIS.Exceptions;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Numerics;
+using System.Text;
+
+namespace PortCMIS.Client.Impl
+{
+    /// <summary>
+    /// Session factory implementation.
+    /// </summary>
+    public class SessionFactory : ISessionFactory
+    {
+        private SessionFactory()
+        {
+        }
+
+        public static SessionFactory NewInstance()
+        {
+            return new SessionFactory();
+        }
+
+        public ISession CreateSession(IDictionary<string, string> parameters)
+        {
+            return CreateSession(parameters, null, null, null);
+        }
+
+        public ISession CreateSession(IDictionary<string, string> parameters, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache)
+        {
+            Session session = new Session(parameters, objectFactory, authenticationProvider, cache);
+            session.Connect();
+
+            return session;
+        }
+
+        public IList<IRepository> GetRepositories(IDictionary<string, string> parameters)
+        {
+            return GetRepositories(parameters, null, null, null);
+        }
+
+        public IList<IRepository> GetRepositories(IDictionary<string, string> parameters, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache)
+        {
+            ICmisBinding binding = CmisBindingHelper.CreateBinding(parameters);
+
+            IList<IRepositoryInfo> repositoryInfos = binding.GetRepositoryService().GetRepositoryInfos(null);
+
+            IList<IRepository> result = new List<IRepository>();
+            foreach (IRepositoryInfo data in repositoryInfos)
+            {
+                result.Add(new Repository(data, parameters, this, objectFactory, binding.GetAuthenticationProvider(), cache));
+            }
+
+            return result;
+        }
+    }
+
+    /// <summary>
+    /// Binding helper class.
+    /// </summary>
+    internal class CmisBindingHelper
+    {
+        public static ICmisBinding CreateBinding(IDictionary<string, string> parameters)
+        {
+            return CreateBinding(parameters, null);
+        }
+
+        public static ICmisBinding CreateBinding(IDictionary<string, string> parameters, IAuthenticationProvider authenticationProvider)
+        {
+            if (parameters == null)
+            {
+                throw new ArgumentNullException("parameters");
+            }
+
+            if (!parameters.ContainsKey(SessionParameter.BindingType))
+            {
+                parameters[SessionParameter.BindingType] = BindingType.Custom;
+            }
+
+            string bt = parameters[SessionParameter.BindingType];
+            switch (bt)
+            {
+                case BindingType.AtomPub:
+                    return CreateAtomPubBinding(parameters, authenticationProvider);
+                case BindingType.WebServices:
+                    return CreateWebServiceBinding(parameters, authenticationProvider);
+                case BindingType.Browser:
+                    return CreateBrowserBinding(parameters, authenticationProvider);
+                case BindingType.Custom:
+                    return CreateCustomBinding(parameters, authenticationProvider);
+                default:
+                    throw new CmisRuntimeException("Ambiguous session parameter: " + parameters);
+            }
+        }
+
+        private static ICmisBinding CreateCustomBinding(IDictionary<string, string> parameters, IAuthenticationProvider authenticationProvider)
+        {
+            CmisBindingFactory factory = CmisBindingFactory.NewInstance();
+            ICmisBinding binding = factory.CreateCmisBinding(parameters, authenticationProvider);
+
+            return binding;
+        }
+
+        private static ICmisBinding CreateWebServiceBinding(IDictionary<string, string> parameters, IAuthenticationProvider authenticationProvider)
+        {
+            CmisBindingFactory factory = CmisBindingFactory.NewInstance();
+            ICmisBinding binding = factory.CreateCmisWebServicesBinding(parameters, authenticationProvider);
+
+            return binding;
+        }
+
+        private static ICmisBinding CreateAtomPubBinding(IDictionary<string, string> parameters, IAuthenticationProvider authenticationProvider)
+        {
+            CmisBindingFactory factory = CmisBindingFactory.NewInstance();
+            ICmisBinding binding = factory.CreateCmisAtomPubBinding(parameters, authenticationProvider);
+
+            return binding;
+        }
+
+        private static ICmisBinding CreateBrowserBinding(IDictionary<string, string> parameters, IAuthenticationProvider authenticationProvider)
+        {
+            CmisBindingFactory factory = CmisBindingFactory.NewInstance();
+            ICmisBinding binding = factory.CreateCmisBrowserBinding(parameters, authenticationProvider);
+
+            return binding;
+        }
+    }
+
+    /// <summary>
+    /// Repository implementation.
+    /// </summary>
+    public class Repository : RepositoryInfo, IRepository
+    {
+        private IDictionary<string, string> parameters;
+        private SessionFactory sessionFactory;
+        private IObjectFactory objectFactory;
+        private IAuthenticationProvider authenticationProvider;
+        private ICache cache;
+
+        public Repository(IRepositoryInfo info, IDictionary<string, string> parameters, SessionFactory sessionFactory, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache)
+            : base(info)
+        {
+            this.parameters = new Dictionary<string, string>(parameters);
+            this.parameters[SessionParameter.RepositoryId] = Id;
+
+            this.sessionFactory = sessionFactory;
+            this.objectFactory = objectFactory;
+            this.authenticationProvider = authenticationProvider;
+            this.cache = cache;
+        }
+
+        public ISession CreateSession()
+        {
+            return sessionFactory.CreateSession(parameters, objectFactory, authenticationProvider, cache);
+        }
+    }
+
+    /// <summary>
+    /// Session implementation.
+    /// </summary>
+    public class Session : ISession
+    {
+        private static HashSet<Updatability> CreateUpdatability = new HashSet<Updatability>();
+        private static HashSet<Updatability> CreateAndCheckoutUpdatability = new HashSet<Updatability>();
+        static Session()
+        {
+            CreateUpdatability.Add(Updatability.OnCreate);
+            CreateUpdatability.Add(Updatability.ReadWrite);
+            CreateAndCheckoutUpdatability.Add(Updatability.OnCreate);
+            CreateAndCheckoutUpdatability.Add(Updatability.ReadWrite);
+            CreateAndCheckoutUpdatability.Add(Updatability.WhenCheckedOut);
+        }
+
+        protected static IOperationContext FallbackContext = new OperationContext(null, false, true, false, IncludeRelationships.None, null, true, null, true, 100);
+
+        protected IDictionary<string, string> parameters;
+        private object sessionLock = new object();
+
+        public ICmisBinding Binding { get; protected set; }
+        public IRepositoryInfo RepositoryInfo { get; protected set; }
+        public string RepositoryId { get { return RepositoryInfo.Id; } }
+
+        public IObjectFactory ObjectFactory { get; protected set; }
+        protected IAuthenticationProvider AuthenticationProvider { get; set; }
+        protected ICache Cache { get; set; }
+        protected bool cachePathOmit;
+
+        private IOperationContext context = FallbackContext;
+        public IOperationContext DefaultContext
+        {
+            get
+            {
+                lock (sessionLock)
+                {
+                    return context;
+                }
+            }
+            set
+            {
+                lock (sessionLock)
+                {
+                    context = (value == null ? FallbackContext : value);
+                }
+            }
+        }
+
+        public Session(IDictionary<string, string> parameters, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache)
+        {
+            if (parameters == null)
+            {
+                throw new ArgumentNullException("parameters");
+            }
+
+            this.parameters = parameters;
+
+            ObjectFactory = (objectFactory == null ? CreateObjectFactory() : objectFactory);
+            AuthenticationProvider = authenticationProvider;
+            Cache = (cache == null ? CreateCache() : cache);
+
+            string cachePathOmitStr;
+            if (parameters.TryGetValue(SessionParameter.CachePathOmit, out cachePathOmitStr))
+            {
+                cachePathOmit = cachePathOmitStr.ToLower() == "true";
+            }
+            else
+            {
+                cachePathOmit = false;
+            }
+        }
+
+        public void Connect()
+        {
+            lock (sessionLock)
+            {
+                Binding = CmisBindingHelper.CreateBinding(parameters, AuthenticationProvider);
+
+                string repositoryId;
+                if (!parameters.TryGetValue(SessionParameter.RepositoryId, out repositoryId))
+                {
+                    throw new ArgumentException("Repository Id is not set!");
+                }
+
+                RepositoryInfo = Binding.GetRepositoryService().GetRepositoryInfo(repositoryId, null);
+            }
+        }
+
+        protected ICache CreateCache()
+        {
+            try
+            {
+                string typeName;
+                Type cacheType;
+
+                if (parameters.TryGetValue(SessionParameter.CacheClass, out typeName))
+                {
+                    cacheType = Type.GetType(typeName);
+                }
+                else
+                {
+                    cacheType = typeof(CmisObjectCache);
+                }
+
+                ICache cacheObject = Activator.CreateInstance(cacheType) as ICache;
+                if (cacheObject == null)
+                {
+                    throw new Exception("Class does not implement ICache!");
+                }
+
+                cacheObject.Initialize(this, parameters);
+
+                return cacheObject;
+            }
+            catch (Exception e)
+            {
+                throw new ArgumentException("Unable to create cache: " + e, e);
+            }
+        }
+
+        protected IObjectFactory CreateObjectFactory()
+        {
+            try
+            {
+                string ofName;
+                Type ofType;
+
+                if (parameters.TryGetValue(SessionParameter.ObjectFactoryClass, out ofName))
+                {
+                    ofType = Type.GetType(ofName);
+                }
+                else
+                {
+                    ofType = typeof(ObjectFactory);
+                }
+
+                IObjectFactory ofObject = Activator.CreateInstance(ofType) as IObjectFactory;
+                if (ofObject == null)
+                {
+                    throw new Exception("Class does not implement IObjectFactory!");
+                }
+
+                ofObject.Initialize(this, parameters);
+
+                return ofObject;
+            }
+            catch (Exception e)
+            {
+                throw new ArgumentException("Unable to create object factory: " + e, e);
+            }
+        }
+
+        public void Clear()
+        {
+            lock (sessionLock)
+            {
+                Cache = CreateCache();
+                Binding.ClearAllCaches();
+            }
+        }
+
+        // session context
+
+        public IOperationContext CreateOperationContext()
+        {
+            return new OperationContext();
+        }
+
+        public IOperationContext CreateOperationContext(HashSet<string> filter, bool includeAcls, bool includeAllowableActions, bool includePolicies,
+            IncludeRelationships includeRelationships, HashSet<string> renditionFilter, bool includePathSegments, string orderBy,
+            bool cacheEnabled, int maxItemsPerPage)
+        {
+            return new OperationContext(filter, includeAcls, includeAllowableActions, includePolicies, includeRelationships, renditionFilter,
+                includePathSegments, orderBy, cacheEnabled, maxItemsPerPage);
+        }
+
+        public IObjectId CreateObjectId(string id)
+        {
+            return new ObjectId(id);
+        }
+
+        // types
+
+        public IObjectType GetTypeDefinition(string typeId)
+        {
+            ITypeDefinition typeDefinition = Binding.GetRepositoryService().GetTypeDefinition(RepositoryId, typeId, null);
+            return ObjectFactory.ConvertTypeDefinition(typeDefinition);
+        }
+
+        public IItemEnumerable<IObjectType> GetTypeChildren(string typeId, bool includePropertyDefinitions)
+        {
+            IRepositoryService service = Binding.GetRepositoryService();
+
+            PageFetcher<IObjectType>.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount)
+            {
+                // fetch the data
+                ITypeDefinitionList tdl = service.GetTypeChildren(RepositoryId, typeId, includePropertyDefinitions, maxNumItems, skipCount, null);
+
+                // convert type definitions
+                int count = (tdl != null && tdl.List != null ? tdl.List.Count : 0);
+                IList<IObjectType> page = new List<IObjectType>(count);
+                if (count > 0)
+                {
+                    foreach (ITypeDefinition typeDefinition in tdl.List)
+                    {
+                        page.Add(ObjectFactory.ConvertTypeDefinition(typeDefinition));
+                    }
+                }
+
+                return new PageFetcher<IObjectType>.Page<IObjectType>(page, tdl.NumItems, tdl.HasMoreItems);
+            };
+
+            return new CollectionEnumerable<IObjectType>(new PageFetcher<IObjectType>(DefaultContext.MaxItemsPerPage, fetchPageDelegate));
+        }
+
+        public IList<ITree<IObjectType>> GetTypeDescendants(string typeId, int depth, bool includePropertyDefinitions)
+        {
+            IList<ITypeDefinitionContainer> descendants = Binding.GetRepositoryService().GetTypeDescendants(
+            RepositoryId, typeId, depth, includePropertyDefinitions, null);
+
+            return ConvertTypeDescendants(descendants);
+        }
+
+        private IList<ITree<IObjectType>> ConvertTypeDescendants(IList<ITypeDefinitionContainer> descendantsList)
+        {
+            if (descendantsList == null || descendantsList.Count == 0)
+            {
+                return null;
+            }
+
+            IList<ITree<IObjectType>> result = new List<ITree<IObjectType>>();
+
+            foreach (ITypeDefinitionContainer container in descendantsList)
+            {
+                Tree<IObjectType> tree = new Tree<IObjectType>();
+                tree.Item = ObjectFactory.ConvertTypeDefinition(container.TypeDefinition);
+                tree.Children = ConvertTypeDescendants(container.Children);
+
+                result.Add(tree);
+            }
+
+            return result;
+        }
+
+        // navigation
+
+        public IFolder GetRootFolder()
+        {
+            return GetRootFolder(DefaultContext);
+        }
+
+        public IFolder GetRootFolder(IOperationContext context)
+        {
+            IFolder rootFolder = GetObject(RepositoryInfo.RootFolderId, context) as IFolder;
+            if (rootFolder == null)
+            {
+                throw new CmisRuntimeException("Root folder object is not a folder!");
+            }
+
+            return rootFolder;
+        }
+
+        public IItemEnumerable<IDocument> GetCheckedOutDocs()
+        {
+            return GetCheckedOutDocs(DefaultContext);
+        }
+
+        public IItemEnumerable<IDocument> GetCheckedOutDocs(IOperationContext context)
+        {
+            INavigationService service = Binding.GetNavigationService();
+            IOperationContext ctxt = new OperationContext(context);
+
+            PageFetcher<IDocument>.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount)
+            {
+                // get all checked out documents
+                IObjectList checkedOutDocs = service.GetCheckedOutDocs(RepositoryId, null, ctxt.FilterString, ctxt.OrderBy,
+                    ctxt.IncludeAllowableActions, ctxt.IncludeRelationships, ctxt.RenditionFilterString, maxNumItems, skipCount, null);
+
+                // convert objects
+                IList<IDocument> page = new List<IDocument>();
+                if (checkedOutDocs.Objects != null)
+                {
+                    foreach (IObjectData objectData in checkedOutDocs.Objects)
+                    {
+                        IDocument doc = ObjectFactory.ConvertObject(objectData, ctxt) as IDocument;
+                        if (doc == null)
+                        {
+                            // should not happen...
+                            continue;
+                        }
+
+                        page.Add(doc);
+                    }
+                }
+
+                return new PageFetcher<IDocument>.Page<IDocument>(page, checkedOutDocs.NumItems, checkedOutDocs.HasMoreItems);
+            };
+
+            return new CollectionEnumerable<IDocument>(new PageFetcher<IDocument>(DefaultContext.MaxItemsPerPage, fetchPageDelegate));
+        }
+
+        public ICmisObject GetObject(IObjectId objectId)
+        {
+            return GetObject(objectId, DefaultContext);
+        }
+
+        public ICmisObject GetObject(IObjectId objectId, IOperationContext context)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Object Id must be set!", "objectId");
+            }
+
+            return GetObject(objectId.Id, context);
+        }
+
+        public ICmisObject GetObject(string objectId)
+        {
+            return GetObject(objectId, DefaultContext);
+        }
+
+        public ICmisObject GetObject(string objectId, IOperationContext context)
+        {
+            if (objectId == null)
+            {
+                throw new ArgumentException("Object Id must be set!", "objectId");
+            }
+            if (context == null)
+            {
+                throw new ArgumentException("Operation context must be set!", "context");
+            }
+
+            ICmisObject result = null;
+
+            // ask the cache first
+            if (context.CacheEnabled)
+            {
+                result = Cache.GetById(objectId, context.CacheKey);
+                if (result != null)
+                {
+                    return result;
+                }
+            }
+
+            // get the object
+            IObjectData objectData = Binding.GetObjectService().GetObject(RepositoryId, objectId, context.FilterString,
+                context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePolicies,
+                context.IncludeAcls, null);
+
+            result = ObjectFactory.ConvertObject(objectData, context);
+
+            // put into cache
+            if (context.CacheEnabled)
+            {
+                Cache.Put(result, context.CacheKey);
+            }
+
+            return result;
+        }
+
+        public ICmisObject GetObjectByPath(string path)
+        {
+            return GetObjectByPath(path, DefaultContext);
+        }
+
+        public ICmisObject GetObjectByPath(string path, IOperationContext context)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException("path");
+            }
+            if (context == null)
+            {
+                throw new ArgumentNullException("context");
+            }
+
+            ICmisObject result = null;
+
+            // ask the cache first
+            if (context.CacheEnabled && !cachePathOmit)
+            {
+                result = Cache.GetByPath(path, context.CacheKey);
+                if (result != null)
+                {
+                    return result;
+                }
+            }
+
+            // get the object
+            IObjectData objectData = Binding.GetObjectService().GetObjectByPath(RepositoryId, path, context.FilterString,
+                context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePolicies,
+                context.IncludeAcls, null);
+
+            result = ObjectFactory.ConvertObject(objectData, context);
+
+            // put into cache
+            if (context.CacheEnabled)
+            {
+                Cache.PutPath(path, result, context.CacheKey);
+            }
+
+            return result;
+        }
+
+        public ICmisObject GetObjectByPath(string parentPath, string name)
+        {
+            return GetObjectByPath(parentPath, name, DefaultContext);
+        }
+
+        public ICmisObject GetObjectByPath(string parentPath, string name, IOperationContext context)
+        {
+            if (parentPath == null || parentPath.Length < 1)
+            {
+                throw new ArgumentException("Parent path must be set!", "parentPath");
+            }
+            if (parentPath[0] != '/')
+            {
+                throw new ArgumentException("Parent path must start with a '/'!", "parentPath");
+            }
+            if (name == null || name.Length < 1)
+            {
+                throw new ArgumentException("Name must be set!", "name");
+            }
+
+            StringBuilder path = new StringBuilder();
+            path.Append(parentPath);
+            if (!parentPath.EndsWith("/"))
+            {
+                path.Append('/');
+            }
+            path.Append(name);
+
+            return GetObjectByPath(path.ToString(), context);
+        }
+
+        public IDocument GetLatestDocumentVersion(string objectId)
+        {
+            return GetLatestDocumentVersion(objectId, DefaultContext);
+        }
+
+        public IDocument GetLatestDocumentVersion(string objectId, IOperationContext context)
+        {
+            if (objectId == null)
+            {
+                throw new ArgumentNullException("objectId");
+            }
+
+            return GetLatestDocumentVersion(CreateObjectId(objectId), false, context);
+        }
+
+        public IDocument GetLatestDocumentVersion(IObjectId objectId)
+        {
+            return GetLatestDocumentVersion(objectId, false, DefaultContext);
+        }
+
+        public IDocument GetLatestDocumentVersion(IObjectId objectId, IOperationContext context)
+        {
+            return GetLatestDocumentVersion(objectId, false, context);
+        }
+
+        public IDocument GetLatestDocumentVersion(IObjectId objectId, bool major, IOperationContext context)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentNullException("objectId");
+            }
+
+            if (context == null)
+            {
+                throw new ArgumentNullException("context");
+            }
+
+            ICmisObject result = null;
+
+            string versionSeriesId = null;
+
+            // first attempt: if we got a Document object, try getting the version
+            // series ID from it
+            if (objectId is IDocument)
+            {
+                versionSeriesId = ((IDocument)objectId).VersionSeriesId;
+            }
+
+            // second attempt: if we have a Document object in the cache, retrieve
+            // the version series ID form there
+            if (versionSeriesId == null)
+            {
+                if (context.CacheEnabled)
+                {
+                    ICmisObject sourceDoc = Cache.GetById(objectId.Id, context.CacheKey);
+                    if (sourceDoc is IDocument)
+                    {
+                        versionSeriesId = ((IDocument)sourceDoc).VersionSeriesId;
+                    }
+                }
+            }
+
+            // third attempt (Web Services only): get the version series ID from the
+            // repository
+            // (the AtomPub and Browser binding don't need the version series ID ->
+            // avoid roundtrip)
+            if (versionSeriesId == null)
+            {
+                string bindingType = Binding.BindingType;
+                if (bindingType == BindingType.WebServices || bindingType == BindingType.Custom)
+                {
+
+                    // get the document to find the version series ID
+                    IObjectData sourceObjectData = Binding.GetObjectService().GetObject(RepositoryId, objectId.Id,
+                            PropertyIds.ObjectId + "," + PropertyIds.VersionSeriesId, false, IncludeRelationships.None,
+                            "cmis:none", false, false, null);
+
+                    if (sourceObjectData.Properties != null)
+                    {
+                        IPropertyData verionsSeriesIdProp = sourceObjectData.Properties[PropertyIds.VersionSeriesId];
+                        if (verionsSeriesIdProp != null)
+                        {
+                            versionSeriesId = verionsSeriesIdProp.FirstValue as string;
+                        }
+                    }
+
+                    // the Web Services binding needs the version series ID -> fail
+                    if (versionSeriesId == null)
+                    {
+                        throw new ArgumentException("Object is not a document or not versionable!");
+                    }
+                }
+            }
+
+            // get the object
+            IObjectData objectData = Binding.GetVersioningService().GetObjectOfLatestVersion(RepositoryId,
+                    objectId.Id, versionSeriesId, major, context.FilterString,
+                    context.IncludeAllowableActions, context.IncludeRelationships,
+                    context.RenditionFilterString, context.IncludePolicies, context.IncludeAcls, null);
+
+            result = ObjectFactory.ConvertObject(objectData, context);
+
+            // put into cache
+            if (context.CacheEnabled)
+            {
+                Cache.Put(result, context.CacheKey);
+            }
+
+            // check result
+            if (!(result is IDocument))
+            {
+                throw new ArgumentException("Latest version is not a document!");
+            }
+
+            return result as IDocument;
+        }
+
+        public void RemoveObjectFromCache(IObjectId objectId)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                return;
+            }
+
+            RemoveObjectFromCache(objectId.Id);
+        }
+
+        public void RemoveObjectFromCache(string objectId)
+        {
+            Cache.Remove(objectId);
+        }
+
+        // discovery
+
+        public IItemEnumerable<IQueryResult> Query(string statement, bool searchAllVersions)
+        {
+            return Query(statement, searchAllVersions, DefaultContext);
+        }
+
+        public IItemEnumerable<IQueryResult> Query(string statement, bool searchAllVersions, IOperationContext context)
+        {
+            IDiscoveryService service = Binding.GetDiscoveryService();
+            IOperationContext ctxt = new OperationContext(context);
+
+            PageFetcher<IQueryResult>.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount)
+            {
+                // fetch the data
+                IObjectList resultList = service.Query(RepositoryId, statement, searchAllVersions, ctxt.IncludeAllowableActions,
+                    ctxt.IncludeRelationships, ctxt.RenditionFilterString, maxNumItems, skipCount, null);
+
+                // convert query results
+                IList<IQueryResult> page = new List<IQueryResult>();
+                if (resultList.Objects != null)
+                {
+                    foreach (IObjectData objectData in resultList.Objects)
+                    {
+                        if (objectData == null)
+                        {
+                            continue;
+                        }
+
+                        page.Add(ObjectFactory.ConvertQueryResult(objectData));
+                    }
+                }
+
+                return new PageFetcher<IQueryResult>.Page<IQueryResult>(page, resultList.NumItems, resultList.HasMoreItems);
+            };
+
+            return new CollectionEnumerable<IQueryResult>(new PageFetcher<IQueryResult>(DefaultContext.MaxItemsPerPage, fetchPageDelegate));
+        }
+
+        public IItemEnumerable<ICmisObject> QueryObjects(string typeId, string where, bool searchAllVersions, IOperationContext context)
+        {
+            if (typeId == null || typeId.Trim().Length == 0)
+            {
+                throw new ArgumentException("Type ID must be set!", "typeId");
+            }
+
+            if (context == null)
+            {
+                throw new ArgumentException("Operation context must be set!", "context");
+            }
+
+            IDiscoveryService discoveryService = Binding.GetDiscoveryService();
+            IObjectFactory of = ObjectFactory;
+            OperationContext ctxt = new OperationContext(context);
+            StringBuilder statement = new StringBuilder("SELECT ");
+
+            string select = ctxt.FilterString;
+            if (select == null)
+            {
+                statement.Append('*');
+            }
+            else
+            {
+                statement.Append(select);
+            }
+
+            IObjectType type = GetTypeDefinition(typeId);
+            statement.Append(" FROM ");
+            statement.Append(type.QueryName);
+
+            if (where != null && where.Trim().Length > 0)
+            {
+                statement.Append(" WHERE ");
+                statement.Append(where);
+            }
+
+            string orderBy = ctxt.OrderBy;
+            if (orderBy != null && orderBy.Trim().Length > 0)
+            {
+                statement.Append(" ORDER BY ");
+                statement.Append(orderBy);
+            }
+
+            PageFetcher<ICmisObject>.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount)
+            {
+                // fetch the data
+                IObjectList resultList = discoveryService.Query(RepositoryId, statement.ToString(),
+                        searchAllVersions, ctxt.IncludeAllowableActions, ctxt.IncludeRelationships,
+                        ctxt.RenditionFilterString, maxNumItems, skipCount, null);
+
+                // convert query results
+                IList<ICmisObject> page = new List<ICmisObject>();
+                if (resultList.Objects != null)
+                {
+                    foreach (IObjectData objectData in resultList.Objects)
+                    {
+                        if (objectData == null)
+                        {
+                            continue;
+                        }
+
+                        page.Add(of.ConvertObject(objectData, ctxt));
+                    }
+                }
+
+                return new PageFetcher<ICmisObject>.Page<ICmisObject>(page, resultList.NumItems, resultList.HasMoreItems);
+            };
+
+            return new CollectionEnumerable<ICmisObject>(new PageFetcher<ICmisObject>(DefaultContext.MaxItemsPerPage, fetchPageDelegate));
+        }
+
+        public IQueryStatement CreateQueryStatement(string statement)
+        {
+            return new QueryStatement(this, statement);
+        }
+
+        public string GetLatestChangeLogToken()
+        {
+            return Binding.GetRepositoryService().GetRepositoryInfo(RepositoryId, null).LatestChangeLogToken;
+        }
+
+        public IChangeEvents GetContentChanges(string changeLogToken, bool includeProperties, long maxNumItems)
+        {
+            return GetContentChanges(changeLogToken, includeProperties, maxNumItems, DefaultContext);
+        }
+
+        public IChangeEvents GetContentChanges(string changeLogToken, bool includeProperties, long maxNumItems,
+                IOperationContext context)
+        {
+            lock (sessionLock)
+            {
+                IObjectList objectList = Binding.GetDiscoveryService().GetContentChanges(RepositoryId, ref changeLogToken, includeProperties,
+                    context.FilterString, context.IncludePolicies, context.IncludeAcls, maxNumItems, null);
+
+                return ObjectFactory.ConvertChangeEvents(changeLogToken, objectList);
+            }
+        }
+
+        // create
+
+        public IObjectId CreateDocument(IDictionary<string, object> properties, IObjectId folderId, IContentStream contentStream,
+            VersioningState? versioningState, IList<IPolicy> policies, IList<IAce> addAces, IList<IAce> removeAces)
+        {
+            if (properties == null || properties.Count == 0)
+            {
+                throw new ArgumentException("Properties must not be empty!");
+            }
+
+            string newId = Binding.GetObjectService().CreateDocument(RepositoryId, ObjectFactory.ConvertProperties(properties, null, null,
+                (versioningState == VersioningState.CheckedOut ? CreateAndCheckoutUpdatability : CreateUpdatability)),
+                (folderId == null ? null : folderId.Id), contentStream, versioningState, ObjectFactory.ConvertPolicies(policies),
+                ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreateDocument(IDictionary<string, object> properties, IObjectId folderId, IContentStream contentStream,
+            VersioningState? versioningState)
+        {
+            return CreateDocument(properties, folderId, contentStream, versioningState, null, null, null);
+        }
+
+        public IObjectId CreateDocumentFromSource(IObjectId source, IDictionary<string, object> properties, IObjectId folderId,
+            VersioningState? versioningState, IList<IPolicy> policies, IList<IAce> addAces, IList<IAce> removeAces)
+        {
+            if (source == null || source.Id == null)
+            {
+                throw new ArgumentException("Source must be set!");
+            }
+
+            // get the type of the source document
+            IObjectType type = null;
+            IList<ISecondaryType> secondaryTypes = null;
+            if (source is ICmisObject)
+            {
+                type = ((ICmisObject)source).ObjectType;
+                secondaryTypes = ((ICmisObject)source).SecondaryTypes;
+            }
+            else
+            {
+                ICmisObject sourceObj = GetObject(source);
+                type = sourceObj.ObjectType;
+                secondaryTypes = sourceObj.SecondaryTypes;
+            }
+
+            if (type.BaseTypeId != BaseTypeId.CmisDocument)
+            {
+                throw new ArgumentException("Source object must be a document!");
+            }
+
+            string newId = Binding.GetObjectService().CreateDocumentFromSource(RepositoryId, source.Id,
+                ObjectFactory.ConvertProperties(properties, type, secondaryTypes,
+                (versioningState == VersioningState.CheckedOut ? CreateAndCheckoutUpdatability : CreateUpdatability)),
+                (folderId == null ? null : folderId.Id),
+                versioningState, ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces),
+                ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreateDocumentFromSource(IObjectId source, IDictionary<string, object> properties, IObjectId folderId,
+                VersioningState? versioningState)
+        {
+            return CreateDocumentFromSource(source, properties, folderId, versioningState, null, null, null);
+        }
+
+        public IObjectId CreateFolder(IDictionary<string, object> properties, IObjectId folderId, IList<IPolicy> policies,
+            IList<IAce> addAces, IList<IAce> removeAces)
+        {
+            if (folderId == null || folderId.Id == null)
+            {
+                throw new ArgumentException("Folder ID must be set!");
+            }
+            if (properties == null || properties.Count == 0)
+            {
+                throw new ArgumentException("Properties must not be empty!");
+            }
+
+            string newId = Binding.GetObjectService().CreateFolder(RepositoryId, ObjectFactory.ConvertProperties(properties, null, null, CreateUpdatability),
+                (folderId == null ? null : folderId.Id), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces),
+                ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreateFolder(IDictionary<string, object> properties, IObjectId folderId)
+        {
+            return CreateFolder(properties, folderId, null, null, null);
+        }
+
+        public IObjectId CreatePolicy(IDictionary<string, object> properties, IObjectId folderId, IList<IPolicy> policies,
+            IList<IAce> addAces, IList<IAce> removeAces)
+        {
+            if (properties == null || properties.Count == 0)
+            {
+                throw new ArgumentException("Properties must not be empty!");
+            }
+
+            string newId = Binding.GetObjectService().CreatePolicy(RepositoryId, ObjectFactory.ConvertProperties(properties, null, null, CreateUpdatability),
+                (folderId == null ? null : folderId.Id), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces),
+                ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreatePolicy(IDictionary<string, object> properties, IObjectId folderId)
+        {
+            return CreatePolicy(properties, folderId, null, null, null);
+        }
+
+        public IObjectId CreateItem(IDictionary<string, object> properties, IObjectId folderId, IList<IPolicy> policies, IList<IAce> addAces,
+                IList<IAce> removeAces)
+        {
+            if (properties == null || properties.Count == 0)
+            {
+                throw new ArgumentException("Properties must not be empty!");
+            }
+
+            string newId = Binding.GetObjectService().CreateItem(RepositoryId, ObjectFactory.ConvertProperties(properties, null, null, CreateUpdatability),
+                (folderId == null ? null : folderId.Id), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces),
+                ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreateItem(IDictionary<string, object> properties, IObjectId folderId)
+        {
+            return CreateItem(properties, folderId, null, null, null);
+        }
+
+        public IObjectId CreateRelationship(IDictionary<string, object> properties, IList<IPolicy> policies, IList<IAce> addAces,
+                IList<IAce> removeAces)
+        {
+            if (properties == null || properties.Count == 0)
+            {
+                throw new ArgumentException("Properties must not be empty!");
+            }
+
+            string newId = Binding.GetObjectService().CreateRelationship(RepositoryId, ObjectFactory.ConvertProperties(properties, null, null, CreateUpdatability),
+                ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null);
+
+            return newId == null ? null : CreateObjectId(newId);
+        }
+
+        public IObjectId CreateRelationship(IDictionary<string, object> properties)
+        {
+            return CreateRelationship(properties, null, null, null);
+        }
+
+        public IItemEnumerable<IRelationship> GetRelationships(IObjectId objectId, bool includeSubRelationshipTypes,
+                RelationshipDirection? relationshipDirection, IObjectType type, IOperationContext context)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!");
+            }
+
+            string id = objectId.Id;
+            string typeId = (type == null ? null : type.Id);
+            IRelationshipService service = Binding.GetRelationshipService();
+            IOperationContext ctxt = new OperationContext(context);
+
+            PageFetcher<IRelationship>.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount)
+            {
+                // fetch the relationships
+                IObjectList relList = service.GetObjectRelationships(RepositoryId, id, includeSubRelationshipTypes, relationshipDirection,
+                    typeId, ctxt.FilterString, ctxt.IncludeAllowableActions, maxNumItems, skipCount, null);
+
+                // convert relationship objects
+                IList<IRelationship> page = new List<IRelationship>();
+                if (relList.Objects != null)
+                {
+                    foreach (IObjectData rod in relList.Objects)
+                    {
+                        IRelationship relationship = GetObject(CreateObjectId(rod.Id), ctxt) as IRelationship;
+                        if (relationship == null)
+                        {
+                            throw new CmisRuntimeException("Repository returned an object that is not a relationship!");
+                        }
+
+                        page.Add(relationship);
+                    }
+                }
+
+                return new PageFetcher<IRelationship>.Page<IRelationship>(page, relList.NumItems, relList.HasMoreItems);
+            };
+
+            return new CollectionEnumerable<IRelationship>(new PageFetcher<IRelationship>(DefaultContext.MaxItemsPerPage, fetchPageDelegate));
+        }
+
+        // delete
+        public void Delete(IObjectId objectId)
+        {
+            Delete(objectId, true);
+        }
+
+        public void Delete(IObjectId objectId, bool allVersions)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!", "objectId");
+            }
+
+            Binding.GetObjectService().DeleteObject(RepositoryId, objectId.Id, allVersions, null);
+            RemoveObjectFromCache(objectId);
+        }
+
+        // content stream
+        public IContentStream GetContentStream(IObjectId docId)
+        {
+            return GetContentStream(docId, null, null, null);
+        }
+
+        public IContentStream GetContentStream(IObjectId docId, string streamId, long? offset, long? length)
+        {
+            if (docId == null || docId.Id == null)
+            {
+                throw new ArgumentException("Invalid document ID!", "objectId");
+            }
+
+            // get the content stream
+            IContentStream contentStream = null;
+            try
+            {
+                contentStream = Binding.GetObjectService().GetContentStream(RepositoryId, docId.Id, streamId, offset, length, null);
+            }
+            catch (CmisConstraintException)
+            {
+                // no content stream
+                return null;
+            }
+
+            return contentStream;
+        }
+
+        // permissions
+
+        public IAcl GetAcl(IObjectId objectId, bool onlyBasicPermissions)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!", "objectId");
+            }
+
+            return Binding.GetAclService().GetAcl(RepositoryId, objectId.Id, onlyBasicPermissions, null);
+        }
+
+        public IAcl ApplyAcl(IObjectId objectId, IList<IAce> addAces, IList<IAce> removeAces, AclPropagation? aclPropagation)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!", "objectId");
+            }
+
+            return Binding.GetAclService().ApplyAcl(RepositoryId, objectId.Id, ObjectFactory.ConvertAces(addAces),
+                ObjectFactory.ConvertAces(removeAces), aclPropagation, null);
+        }
+
+        public void ApplyPolicy(IObjectId objectId, params IObjectId[] policyIds)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!", "objectId");
+            }
+            if (policyIds == null || (policyIds.Length == 0))
+            {
+                throw new ArgumentException("No Policies provided!");
+            }
+
+            string[] ids = new string[policyIds.Length];
+            for (int i = 0; i < policyIds.Length; i++)
+            {
+                if (policyIds[i] == null || policyIds[i].Id == null)
+                {
+                    throw new ArgumentException("A Policy ID is not set!", "policyIds");
+                }
+
+                ids[i] = policyIds[i].Id;
+            }
+
+            foreach (string id in ids)
+            {
+                Binding.GetPolicyService().ApplyPolicy(RepositoryId, id, objectId.Id, null);
+            }
+        }
+
+        public void RemovePolicy(IObjectId objectId, params IObjectId[] policyIds)
+        {
+            if (objectId == null || objectId.Id == null)
+            {
+                throw new ArgumentException("Invalid object ID!", "objectId");
+            }
+            if (policyIds == null || policyIds.Length == 0)
+            {
+                throw new ArgumentException("No Policies provided!", "policyIds");
+            }
+
+            string[] ids = new string[policyIds.Length];
+            for (int i = 0; i < policyIds.Length; i++)
+            {
+                if (policyIds[i] == null || policyIds[i].Id == null)
+                {
+                    throw new ArgumentException("A Policy Id is not set!");
+                }
+
+                ids[i] = policyIds[i].Id;
+            }
+
+            foreach (string id in ids)
+            {
+                Binding.GetPolicyService().RemovePolicy(RepositoryId, id, objectId.Id, null);
+            }
+        }
+    }
+
+    public class QueryStatement : IQueryStatement
+    {
+        private ISession session;
+        private string statement;
+        private IDictionary<int, string> parametersDict = new Dictionary<int, string>();
+
+        public QueryStatement(Session session, string statement)
+        {
+            if (session == null)
+            {
+                throw new ArgumentNullException("session");
+            }
+
+            if (statement == null)
+            {
+                throw new ArgumentNullException("statement");
+            }
+
+            this.session = session;
+            this.statement = statement.Trim();
+        }
+
+        public void SetType(int parameterIndex, string typeId)
+        {
+            SetType(parameterIndex, session.GetTypeDefinition(typeId));
+        }
+
+        public void SetType(int parameterIndex, IObjectType type)
+        {
+            if (type == null)
+            {
+                throw new ArgumentException("Type must be set!");
+            }
+
+            if (type.QueryName == null)
+            {
+                throw new ArgumentException("Type has no query name!");
+            }
+
+            parametersDict[parameterIndex] = type.QueryName;
+        }
+
+        public void SetProperty(int parameterIndex, string typeId, string propertyId)
+        {
+            IObjectType type = session.GetTypeDefinition(typeId);
+
+            IPropertyDefinition propertyDefinition = type[propertyId];
+            if (propertyDefinition == null)
+            {
+                throw new ArgumentException("Property does not exist!");
+            }
+
+            SetProperty(parameterIndex, propertyDefinition);
+        }
+
+        public void SetProperty(int parameterIndex, IPropertyDefinition propertyDefinition)
+        {
+            if (propertyDefinition == null)
+            {
+                throw new ArgumentException("Property must be set!");
+            }
+
+            string queryName = propertyDefinition.QueryName;
+            if (queryName == null)
+            {
+                throw new ArgumentException("Property has no query name!");
+            }
+
+            parametersDict[parameterIndex] = queryName;
+        }
+
+        public void SetInteger(int parameterIndex, params BigInteger[] num)
+        {
+            if (num == null || num.Length == 0)
+            {
+                throw new ArgumentException("Number must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (BigInteger n in num)
+            {
+                if (n == null)
+                {
+                    throw new ArgumentException("Number is null!");
+                }
+
+                slb.Add(n.ToString("#", CultureInfo.InvariantCulture));
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetDecimal(int parameterIndex, params decimal[] num)
+        {
+            if (num == null || num.Length == 0)
+            {
+                throw new ArgumentException("Number must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (decimal n in num)
+            {
+                slb.Add(n.ToString("#", CultureInfo.InvariantCulture));
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetString(int parameterIndex, params string[] str)
+        {
+            if (str == null || str.Length == 0)
+            {
+                throw new ArgumentException("String must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (string s in str)
+            {
+                if (s == null)
+                {
+                    throw new ArgumentException("String is null!");
+                }
+
+                slb.Add(Escape(s));
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetStringLike(int parameterIndex, string str)
+        {
+            if (str == null)
+            {
+                throw new ArgumentException("String must be set!");
+            }
+
+            parametersDict[parameterIndex] = EscapeLike(str);
+        }
+
+        public void SetStringContains(int parameterIndex, string str)
+        {
+            if (str == null)
+            {
+                throw new ArgumentException("String must be set!");
+            }
+
+            parametersDict[parameterIndex] = EscapeContains(str);
+        }
+
+        public void SetId(int parameterIndex, params  IObjectId[] id)
+        {
+            if (id == null || id.Length == 0)
+            {
+                throw new ArgumentException("Id must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (IObjectId oid in id)
+            {
+                if (oid == null || oid.Id == null)
+                {
+                    throw new ArgumentException("Id is null!");
+                }
+
+                slb.Add(Escape(oid.Id));
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetUri(int parameterIndex, params Uri[] uri)
+        {
+            if (uri == null || uri.Length == 0)
+            {
+                throw new ArgumentException("URI must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (Uri u in uri)
+            {
+                if (u == null)
+                {
+                    throw new ArgumentException("URI is null!");
+                }
+
+                slb.Add(Escape(u.ToString()));
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetBoolean(int parameterIndex, params bool[] boolean)
+        {
+            if (boolean == null || boolean.Length == 0)
+            {
+                throw new ArgumentException("Boolean must be set!");
+            }
+
+            StringListBuilder slb = new StringListBuilder(",");
+            foreach (bool b in boolean)
+            {
+                slb.Add(b ? "TRUE" : "FALSE");
+            }
+
+            parametersDict[parameterIndex] = slb.ToString();
+        }
+
+        public void SetDateTime(int parameterIndex, params DateTime[] dt)
+        {
+            SetDateTime(parameterIndex, false, dt);
+        }
+
+        public void SetDateTime(int parameterIndex, params long[] ms)
+        {
+            SetDateTime(parameterIndex, false, ms);
+        }
+
+        public void SetDateTimeTimestamp(int parameterIndex, params DateTime[] dt)
+        {
+            SetDateTime(parameterIndex, true, dt);
+        }
+
+        public void SetDateTimeTimestamp(int parameterIndex, params long[] ms)
+        {
+            SetDateTime(parameterIndex, true, ms);
+        }
+
+        protected void SetDateTime(int parameterIndex, bool prefix, params DateTime[] cal)
+        {
+            if (cal == null || cal.Length == 0)
+            {
+                throw new ArgumentException("DateTime must be set!");
+            }
+
+            StringBuilder sb = new StringBuilder();
+            foreach (DateTime dt in cal)
+            {
+                if (dt == null)
+                {
+                    throw new ArgumentException("DateTime is null!");
+                }
+
+                if (sb.Length > 0)
+                {
+                    sb.Append(',');
+                }
+
+                if (prefix)
+                {
+                    sb.Append("TIMESTAMP ");
+                }
+
+                DateTime tmp = dt.ToUniversalTime();
+                sb.Append("'");
+                sb.Append(tmp.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture));
+                sb.Append("'");
+            }
+
+            parametersDict[parameterIndex] = sb.ToString();
+        }
+
+        protected void SetDateTime(int parameterIndex, bool prefix, params long[] ms)
+        {
+            if (ms == null || ms.Length == 0)
+            {
+                throw new ArgumentException("DateTime must be set!");
+            }
+
+            StringBuilder sb = new StringBuilder();
+            foreach (long dt in ms)
+            {
+                if (sb.Length > 0)
+                {
+                    sb.Append(',');
+                }
+
+                if (prefix)
+                {
+                    sb.Append("TIMESTAMP ");
+                }
+
+                DateTime tmp = DateTimeHelper.ConvertMillisToDateTime(dt).ToUniversalTime();
+                sb.Append("'");
+                sb.Append(tmp.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture));
+                sb.Append("'");
+            }
+
+            parametersDict[parameterIndex] = sb.ToString();
+        }
+
+        public string ToQueryString()
+        {
+            bool inStr = false;
+            int parameterIndex = 0;
+
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < statement.Length; i++)
+            {
+                char c = statement[i];
+
+                if (c == '\'')
+                {
+                    if (inStr && statement[i - 1] == '\\')
+                    {
+                        inStr = true;
+                    }
+                    else
+                    {
+                        inStr = !inStr;
+                    }
+                    sb.Append(c);
+                }
+                else if (c == '?' && !inStr)
+                {
+                    parameterIndex++;
+                    string s;
+                    if (parametersDict.TryGetValue(parameterIndex, out s))
+                    {
+                        sb.Append(s);
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
+                }
+                else
+                {
+                    sb.Append(c);
+                }
+            }
+
+            return sb.ToString();
+        }
+
+        public IItemEnumerable<IQueryResult> Query(bool searchAllVersions)
+        {
+            return session.Query(ToQueryString(), searchAllVersions);
+        }
+
+        public IItemEnumerable<IQueryResult> Query(bool searchAllVersions, IOperationContext context)
+        {
+            return session.Query(ToQueryString(), searchAllVersions, context);
+        }
+
+        // --- internal ---
+
+        private static string Escape(string str)
+        {
+            StringBuilder sb = new StringBuilder("'");
+            for (int i = 0; i < str.Length; i++)
+            {
+                char c = str[i];
+
+                if (c == '\'' || c == '\\')
+                {
+                    sb.Append('\\');
+                }
+
+                sb.Append(c);
+            }
+
+            sb.Append('\'');
+
+            return sb.ToString();
+        }
+
+        private static string EscapeLike(string str)
+        {
+            StringBuilder sb = new StringBuilder("'");
+            for (int i = 0; i < str.Length; i++)
+            {
+                char c = str[i];
+
+                if (c == '\'')
+                {
+                    sb.Append('\\');
+                }
+                else if (c == '\\')
+                {
+                    if (i + 1 < str.Length && (str[i + 1] == '%' || str[i + 1] == '_'))
+                    {
+                        // no additional back slash
+                    }
+                    else
+                    {
+                        sb.Append('\\');
+                    }
+                }
+
+                sb.Append(c);
+            }
+
+            sb.Append('\'');
+
+            return sb.ToString();
+        }
+
+        private static string EscapeContains(string str)
+        {
+            StringBuilder sb = new StringBuilder("'");
+            for (int i = 0; i < str.Length; i++)
+            {
+                char c = str[i];
+
+                if (c == '\\')
+                {
+                    sb.Append('\\');
+                }
+                else if (c == '\'' || c == '\"')
+                {
+                    sb.Append("\\\\\\");
+                }
+
+                sb.Append(c);
+            }
+
+            sb.Append('\'');
+
+            return sb.ToString();
+        }
+    }
+}