blob: 59abe28512c052945d1e7c17f7bd3b8ef6032b2d [file] [log] [blame]
/*
* 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
{
/// <summary>
/// This is a factory.
/// </summary>
private SessionFactory()
{
}
/// <summary>
/// Creates a new factory object.
/// </summary>
/// <returns>a factory object</returns>
public static SessionFactory NewInstance()
{
return new SessionFactory();
}
/// <inheritdoc/>
public ISession CreateSession(IDictionary<string, string> parameters)
{
return CreateSession(parameters, null, null, null);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public IList<IRepository> GetRepositories(IDictionary<string, string> parameters)
{
return GetRepositories(parameters, null, null, null);
}
/// <summary>
/// Gets all repository available at the specified endpoint.
/// </summary>
/// <param name="parameters">the session parameters</param>
/// <param name="objectFactory">Object factory.</param>
/// <param name="authenticationProvider">Authentication provider.</param>
/// <param name="cache">Client object cache.</param>
/// <returns>a list of all available repositories</returns>
/// <seealso cref="PortCMIS.Client.SessionParameter"/>
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;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="info">the low-level repository info object</param>
/// <param name="parameters">the session parameters</param>
/// <param name="sessionFactory">the session factory</param>
/// <param name="objectFactory">the object factory (may be <c>null</c>)</param>
/// <param name="authenticationProvider">the authentication provider (may be <c>null</c>)</param>
/// <param name="cache">the cache (may be <c>null</c>)</param>
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;
}
/// <inheritdoc/>
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);
}
/// <summary>
/// Initial default operation context.
/// </summary>
protected static IOperationContext FallbackContext = new OperationContext(null, false, true, false, IncludeRelationships.None, null, true, null, true, 100);
/// <summary>
/// Session parameters
/// </summary>
protected IDictionary<string, string> parameters;
private object sessionLock = new object();
/// <value>
/// Gets the low-level binding.
/// </value>
public ICmisBinding Binding { get; protected set; }
/// <value>
/// Gets the repository info.
/// </value>
public IRepositoryInfo RepositoryInfo { get; protected set; }
/// <value>
/// Gets the repository ID.
/// </value>
public string RepositoryId { get { return RepositoryInfo.Id; } }
/// <inheritdoc/>
public IObjectFactory ObjectFactory { get; protected set; }
/// <summary>
/// Authentication provider.
/// </summary>
protected IAuthenticationProvider AuthenticationProvider { get; set; }
/// <summary>
/// Object and path cache.
/// </summary>
protected ICache Cache { get; set; }
/// <summary>
/// Indicating if the path cache should be used.
/// </summary>
protected bool cachePathOmit;
private IOperationContext context = FallbackContext;
/// <inheritdoc/>
public IOperationContext DefaultContext
{
get
{
lock (sessionLock)
{
return context;
}
}
set
{
lock (sessionLock)
{
context = (value == null ? FallbackContext : value);
}
}
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="parameters">the session parameters</param>
/// <param name="objectFactory">an object factory, if available</param>
/// <param name="authenticationProvider">an authentication provider, if available</param>
/// <param name="cache">a cache, if available</param>
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;
}
}
/// <summary>
/// Connects to the repository and fetches the repository info.
/// </summary>
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);
}
}
/// <summary>
/// Create the cache.
/// </summary>
/// <returns>the cache object</returns>
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);
}
}
/// <summary>
/// Creates the object factory.
/// </summary>
/// <returns>the object factory</returns>
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);
}
}
/// <inheritdoc/>
public void Clear()
{
lock (sessionLock)
{
Cache = CreateCache();
Binding.ClearAllCaches();
}
}
// session context
/// <inheritdoc/>
public IOperationContext CreateOperationContext()
{
return new OperationContext();
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateObjectId(string id)
{
return new ObjectId(id);
}
// types
/// <inheritdoc/>
public IObjectType GetTypeDefinition(string typeId)
{
ITypeDefinition typeDefinition = Binding.GetRepositoryService().GetTypeDefinition(RepositoryId, typeId, null);
return ObjectFactory.ConvertTypeDefinition(typeDefinition);
}
/// <inheritdoc/>
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));
}
/// <inheritdoc/>
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
/// <inheritdoc/>
public IFolder GetRootFolder()
{
return GetRootFolder(DefaultContext);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public IItemEnumerable<IDocument> GetCheckedOutDocs()
{
return GetCheckedOutDocs(DefaultContext);
}
/// <inheritdoc/>
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));
}
/// <inheritdoc/>
public ICmisObject GetObject(IObjectId objectId)
{
return GetObject(objectId, DefaultContext);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public ICmisObject GetObject(string objectId)
{
return GetObject(objectId, DefaultContext);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public ICmisObject GetObjectByPath(string path)
{
return GetObjectByPath(path, DefaultContext);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public ICmisObject GetObjectByPath(string parentPath, string name)
{
return GetObjectByPath(parentPath, name, DefaultContext);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IDocument GetLatestDocumentVersion(string objectId)
{
return GetLatestDocumentVersion(objectId, DefaultContext);
}
/// <inheritdoc/>
public IDocument GetLatestDocumentVersion(string objectId, IOperationContext context)
{
if (objectId == null)
{
throw new ArgumentNullException("objectId");
}
return GetLatestDocumentVersion(CreateObjectId(objectId), false, context);
}
/// <inheritdoc/>
public IDocument GetLatestDocumentVersion(IObjectId objectId)
{
return GetLatestDocumentVersion(objectId, false, DefaultContext);
}
/// <inheritdoc/>
public IDocument GetLatestDocumentVersion(IObjectId objectId, IOperationContext context)
{
return GetLatestDocumentVersion(objectId, false, context);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public void RemoveObjectFromCache(IObjectId objectId)
{
if (objectId == null || objectId.Id == null)
{
return;
}
RemoveObjectFromCache(objectId.Id);
}
/// <inheritdoc/>
public void RemoveObjectFromCache(string objectId)
{
Cache.Remove(objectId);
}
// discovery
/// <inheritdoc/>
public IItemEnumerable<IQueryResult> Query(string statement, bool searchAllVersions)
{
return Query(statement, searchAllVersions, DefaultContext);
}
/// <inheritdoc/>
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));
}
/// <inheritdoc/>
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));
}
/// <inheritdoc/>
public IQueryStatement CreateQueryStatement(string statement)
{
return new QueryStatement(this, statement);
}
/// <inheritdoc/>
public string GetLatestChangeLogToken()
{
return Binding.GetRepositoryService().GetRepositoryInfo(RepositoryId, null).LatestChangeLogToken;
}
/// <inheritdoc/>
public IChangeEvents GetContentChanges(string changeLogToken, bool includeProperties, long maxNumItems)
{
return GetContentChanges(changeLogToken, includeProperties, maxNumItems, DefaultContext);
}
/// <inheritdoc/>
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
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateDocument(IDictionary<string, object> properties, IObjectId folderId, IContentStream contentStream,
VersioningState? versioningState)
{
return CreateDocument(properties, folderId, contentStream, versioningState, null, null, null);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateDocumentFromSource(IObjectId source, IDictionary<string, object> properties, IObjectId folderId,
VersioningState? versioningState)
{
return CreateDocumentFromSource(source, properties, folderId, versioningState, null, null, null);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateFolder(IDictionary<string, object> properties, IObjectId folderId)
{
return CreateFolder(properties, folderId, null, null, null);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreatePolicy(IDictionary<string, object> properties, IObjectId folderId)
{
return CreatePolicy(properties, folderId, null, null, null);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateItem(IDictionary<string, object> properties, IObjectId folderId)
{
return CreateItem(properties, folderId, null, null, null);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IObjectId CreateRelationship(IDictionary<string, object> properties)
{
return CreateRelationship(properties, null, null, null);
}
/// <inheritdoc/>
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
/// <inheritdoc/>
public void Delete(IObjectId objectId)
{
Delete(objectId, true);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public IList<string> DeleteTree(IObjectId folderId, bool allVersions, UnfileObject? unfile, bool continueOnFailure)
{
if (folderId == null || folderId.Id == null)
{
throw new ArgumentException("Invalid object ID!", "folderId");
}
IFailedToDeleteData failed = Binding.GetObjectService().DeleteTree(RepositoryId, folderId.Id, allVersions, unfile, continueOnFailure, null);
if (failed == null || failed.Ids == null || failed.Ids.Count == 0)
{
RemoveObjectFromCache(folderId);
}
return failed != null ? failed.Ids : null;
}
// content stream
/// <inheritdoc/>
public IContentStream GetContentStream(IObjectId docId)
{
return GetContentStream(docId, null, null, null);
}
/// <inheritdoc/>
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
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
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);
}
}
/// <inheritdoc/>
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);
}
}
}
internal 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();
}
/// <inheritdoc/>
public void SetType(int parameterIndex, string typeId)
{
SetType(parameterIndex, session.GetTypeDefinition(typeId));
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
public void SetStringLike(int parameterIndex, string str)
{
if (str == null)
{
throw new ArgumentException("String must be set!");
}
parametersDict[parameterIndex] = EscapeLike(str);
}
/// <inheritdoc/>
public void SetStringContains(int parameterIndex, string str)
{
if (str == null)
{
throw new ArgumentException("String must be set!");
}
parametersDict[parameterIndex] = EscapeContains(str);
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
public void SetDateTime(int parameterIndex, params DateTime[] dt)
{
SetDateTime(parameterIndex, false, dt);
}
/// <inheritdoc/>
public void SetDateTime(int parameterIndex, params long[] ms)
{
SetDateTime(parameterIndex, false, ms);
}
/// <inheritdoc/>
public void SetDateTimeTimestamp(int parameterIndex, params DateTime[] dt)
{
SetDateTime(parameterIndex, true, dt);
}
/// <inheritdoc/>
public void SetDateTimeTimestamp(int parameterIndex, params long[] ms)
{
SetDateTime(parameterIndex, true, ms);
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
public IItemEnumerable<IQueryResult> Query(bool searchAllVersions)
{
return session.Query(ToQueryString(), searchAllVersions);
}
/// <inheritdoc/>
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();
}
}
}