/* * 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.Data; using PortCMIS.Enums; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Numerics; using System.Text; namespace PortCMIS.Client { /// /// Operation context implementation. /// public class OperationContext : IOperationContext { /// /// Property selector for all properties ('*'). /// public const string PropertiesStar = "*"; /// /// Rendition constant for 'no rendition' ('cmis:none'). /// public const string RenditionNone = "cmis:none"; private ISet filter; private bool includeAllowableActions; private bool includeAcls; private IncludeRelationships? includeRelationships; private bool includePolicies; private ISet renditionFilter; private bool includePathSegments; private string orderBy; private bool cacheEnabled; private string cacheKey; private int maxItemsPerPage; /// /// Constructor with default values. /// public OperationContext() { filter = null; includeAcls = false; includeAllowableActions = true; includePolicies = false; includeRelationships = PortCMIS.Enums.IncludeRelationships.None; renditionFilter = null; includePathSegments = true; orderBy = null; cacheEnabled = false; maxItemsPerPage = 100; } /// /// Copy constructor. /// public OperationContext(IOperationContext source) { filter = (source.Filter == null ? null : new HashSet(source.Filter)); includeAcls = source.IncludeAcls; includeAllowableActions = source.IncludeAllowableActions; includePolicies = source.IncludePolicies; includeRelationships = source.IncludeRelationships; renditionFilter = (source.RenditionFilter == null ? null : new HashSet(source.RenditionFilter)); includePathSegments = source.IncludePathSegments; orderBy = source.OrderBy; cacheEnabled = source.CacheEnabled; maxItemsPerPage = source.MaxItemsPerPage; } /// /// Constructor with given values. /// public OperationContext(ISet filter, bool includeAcls, bool includeAllowableActions, bool includePolicies, IncludeRelationships includeRelationships, ISet renditionFilter, bool includePathSegments, string orderBy, bool cacheEnabled, int maxItemsPerPage) { this.filter = filter; this.includeAcls = includeAcls; this.includeAllowableActions = includeAllowableActions; this.includePolicies = includePolicies; this.includeRelationships = includeRelationships; this.renditionFilter = renditionFilter; this.includePathSegments = includePathSegments; this.orderBy = orderBy; this.cacheEnabled = cacheEnabled; this.maxItemsPerPage = maxItemsPerPage; } /// public virtual ISet Filter { get { return filter == null ? null : new HashSet(filter); } set { if (value != null) { HashSet tempSet = new HashSet(); foreach (string oid in value) { if (oid == null) { continue; } string toid = oid.Trim(); if (toid.Length == 0) { continue; } if (toid == PropertiesStar) { tempSet = new HashSet(); tempSet.Add(PropertiesStar); break; } if (toid.IndexOf(',') > -1) { throw new ArgumentException("Query ID must not contain a comma!"); } tempSet.Add(toid); } if (tempSet.Count == 0) { filter = null; } else { filter = tempSet; } } else { filter = null; } GenerateCacheKey(); } } /// public virtual string FilterString { get { if (filter == null) { return null; } if (filter.Contains(PropertiesStar)) { return PropertiesStar; } this.filter.Add(PropertyIds.ObjectId); this.filter.Add(PropertyIds.BaseTypeId); this.filter.Add(PropertyIds.ObjectTypeId); StringBuilder sb = new StringBuilder(); foreach (String oid in filter) { if (sb.Length > 0) { sb.Append(','); } sb.Append(oid); } return sb.ToString(); } set { if (value == null || value.Trim().Length == 0) { Filter = null; return; } string[] ids = value.Split(','); HashSet tempSet = new HashSet(); foreach (string qid in ids) { tempSet.Add(qid); } Filter = tempSet; } } /// public virtual bool IncludeAllowableActions { get { return includeAllowableActions; } set { includeAllowableActions = value; GenerateCacheKey(); } } /// public virtual bool IncludeAcls { get { return includeAcls; } set { includeAcls = value; GenerateCacheKey(); } } /// public virtual IncludeRelationships? IncludeRelationships { get { return includeRelationships; } set { includeRelationships = value; GenerateCacheKey(); } } /// public virtual bool IncludePolicies { get { return includePolicies; } set { includePolicies = value; GenerateCacheKey(); } } /// public virtual ISet RenditionFilter { get { return renditionFilter == null ? null : new HashSet(renditionFilter); } set { HashSet tempSet = new HashSet(); if (value != null) { foreach (String rf in value) { if (rf == null) { continue; } String trf = rf.Trim(); if (trf.Length == 0) { continue; } if (trf.IndexOf(',') > -1) { throw new ArgumentException("Rendition must not contain a comma!"); } tempSet.Add(trf); } if (tempSet.Count == 0) { tempSet.Add(RenditionNone); } } else { tempSet.Add(RenditionNone); } renditionFilter = tempSet; GenerateCacheKey(); } } /// public virtual string RenditionFilterString { get { if (renditionFilter == null) { return null; } StringBuilder sb = new StringBuilder(); foreach (string rf in renditionFilter) { if (sb.Length > 0) { sb.Append(','); } sb.Append(rf); } return sb.ToString(); } set { if (value == null || value.Trim().Length == 0) { RenditionFilter = null; return; } string[] renditions = value.Split(','); HashSet tempSet = new HashSet(); foreach (string rend in renditions) { tempSet.Add(rend); } RenditionFilter = tempSet; } } /// public virtual bool IncludePathSegments { get { return includePathSegments; } set { includePathSegments = value; GenerateCacheKey(); } } /// public virtual string OrderBy { get { return orderBy; } set { orderBy = value; GenerateCacheKey(); } } /// public virtual bool CacheEnabled { get { return cacheEnabled; } set { cacheEnabled = value; GenerateCacheKey(); } } /// public virtual string CacheKey { get { if (cacheKey == null) { GenerateCacheKey(); } return cacheKey; } } /// public virtual int MaxItemsPerPage { get { return maxItemsPerPage; } set { maxItemsPerPage = value; } } /// /// Generates a cache key from the current state of the operation context. /// protected virtual void GenerateCacheKey() { if (!cacheEnabled) { cacheKey = null; } else { StringBuilder sb = new StringBuilder(); sb.Append(includeAcls ? "1" : "0"); sb.Append(includeAllowableActions ? "1" : "0"); sb.Append(includePolicies ? "1" : "0"); sb.Append("|"); sb.Append(filter == null ? "" : FilterString); sb.Append("|"); sb.Append(includeRelationships == null ? "" : includeRelationships.GetCmisValue()); sb.Append("|"); sb.Append(renditionFilter == null ? "" : RenditionFilterString); cacheKey = sb.ToString(); } } } /// /// Operation Context helpers. /// public class OperationContextUtils { private OperationContextUtils() { } /// /// Creates a new operation context object. /// public static IOperationContext CreateOperationContext() { return new OperationContext(); } /// /// Copies an operation context object. /// public static IOperationContext CopyOperationContext(OperationContext context) { return new OperationContext(context); } /// /// Creates a new operation context object with the given parameters. /// /// /// Caching is enabled. /// public static IOperationContext CreateOperationContext(HashSet filter, bool includeAcls, bool includeAllowableActions, bool includePolicies, IncludeRelationships includeRelationships, HashSet renditionFilter, bool includePathSegments, string orderBy, bool cacheEnabled, int maxItemsPerPage) { return new OperationContext(filter, includeAcls, includeAllowableActions, includePolicies, includeRelationships, renditionFilter, includePathSegments, orderBy, cacheEnabled, maxItemsPerPage); } /// /// Creates a new operation context object that only selects the bare minimum. /// /// /// Caching is enabled. /// public static IOperationContext CreateMinimumOperationContext() { return CreateMinimumOperationContext((string[])null); } /// /// Creates a new operation context object that only selects the bare minimum plus the provided properties. /// /// /// Caching is enabled. /// public static IOperationContext CreateMinimumOperationContext(params string[] property) { ISet filter = new HashSet(); filter.Add(PropertyIds.ObjectId); filter.Add(PropertyIds.ObjectTypeId); filter.Add(PropertyIds.BaseTypeId); if (property != null) { foreach (string prop in property) { filter.Add(prop); } } return new OperationContext(filter, false, false, false, IncludeRelationships.None, new HashSet() { OperationContext.RenditionNone }, false, null, true, 100); } /// /// Creates a new operation context object that selects everything. /// /// /// Caching is enabled. /// public static IOperationContext CreateMaximumOperationContext() { return new OperationContext(new HashSet() { OperationContext.PropertiesStar }, true, true, true, IncludeRelationships.Both, new HashSet() { "*" }, false, null, true, 100); } /// /// Returns an unmodifiable view of the specified operation context. /// public static IOperationContext CreateReadOnlyOperationContext(IOperationContext context) { return new ReadOnlyOperationContext(context); } internal class ReadOnlyOperationContext : OperationContext { public ReadOnlyOperationContext(IOperationContext originalContext) : base(originalContext) { } public override ISet Filter { get { return base.Filter == null ? null : new HashSet(base.Filter); } set { throw new Exception("Not supported!"); } } public override string FilterString { get { return base.FilterString; } set { throw new Exception("Not supported!"); } } public override bool IncludeAllowableActions { get { return base.IncludeAllowableActions; } set { throw new Exception("Not supported!"); } } public override bool IncludeAcls { get { return base.IncludeAcls; } set { throw new Exception("Not supported!"); } } public override IncludeRelationships? IncludeRelationships { get { return base.IncludeRelationships; } set { throw new Exception("Not supported!"); } } public override bool IncludePolicies { get { return base.IncludePolicies; } set { throw new Exception("Not supported!"); } } public override ISet RenditionFilter { get { return base.RenditionFilter == null ? null : new HashSet(base.RenditionFilter); } set { throw new Exception("Not supported!"); } } public override string RenditionFilterString { get { return base.RenditionFilterString; } set { throw new Exception("Not supported!"); } } public override bool IncludePathSegments { get { return base.IncludePathSegments; } set { throw new Exception("Not supported!"); } } public override string OrderBy { get { return base.OrderBy; } set { throw new Exception("Not supported!"); } } public override bool CacheEnabled { get { return base.CacheEnabled; } set { throw new Exception("Not supported!"); } } public override int MaxItemsPerPage { get { return base.MaxItemsPerPage; } set { throw new Exception("Not supported!"); } } } } /// /// Object ID implementation. /// public class ObjectId : IObjectId { private string id; /// public string Id { get { return id; } set { if (value == null || value.Length == 0) { throw new ArgumentException("ID must be set!"); } id = value; } } /// /// Constructor. /// /// the object ID as a string public ObjectId(string id) { Id = id; } /// public override string ToString() { return Id; } /// public override int GetHashCode() { return Id.GetHashCode(); } /// public override bool Equals(object obj) { return this.Equals(obj as ObjectId); } /// /// Determines whether the specified object ID is equal to the current object ID. /// /// the object ID public bool Equals(ObjectId oid) { if (Object.ReferenceEquals(oid, null)) { return false; } if (Object.ReferenceEquals(this, oid)) { return true; } if (this.GetType() != oid.GetType()) { return false; } return Id == oid.Id; } /// /// Determines whether two specified object IDs have the same ID. /// public static bool operator ==(ObjectId id1, ObjectId id2) { if (object.ReferenceEquals(id1, null)) { return object.ReferenceEquals(id2, null); } return id1.Id == id2.Id; } /// /// Determines whether two specified object IDs have different IDs. /// public static bool operator !=(ObjectId id1, ObjectId id2) { return !(id1 == id2); } } /// /// Tree implementation. /// internal class Tree : ITree { /// public T Item { get; set; } /// public IList> Children { get; set; } } /// /// Base class for IItemEnumerable's. /// internal abstract class AbstractEnumerable : IItemEnumerable { private AbstractEnumerator enumerator; /// /// Gets the enumerator to creates one if it hasn't been set up, yet. /// protected AbstractEnumerator Enumerator { get { if (enumerator == null) { enumerator = CreateEnumerator(); } return enumerator; } } /// /// Gets the delegate that fetches a page. /// protected PageFetcher PageFetcher { get; set; } /// /// Gets the skip count. /// protected BigInteger SkipCount { get; private set; } /// /// Constructor. /// /// >the delegate that fetches a page public AbstractEnumerable(PageFetcher pageFetcher) : this(0, pageFetcher) { } /// /// Constructor. /// /// the skip count /// >the delegate that fetches a page protected AbstractEnumerable(BigInteger position, PageFetcher pageFetcher) { this.PageFetcher = pageFetcher; this.SkipCount = position; } /// /// Creates an enumerator. /// protected abstract AbstractEnumerator CreateEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// public IEnumerator GetEnumerator() { return Enumerator; } /// public IItemEnumerable SkipTo(BigInteger position) { return new CollectionEnumerable(position, PageFetcher); } /// public IItemEnumerable GetPage() { return new CollectionPageEnumerable(SkipCount, PageFetcher); } /// public IItemEnumerable GetPage(int maxNumItems) { PageFetcher.MaxNumItems = maxNumItems; return new CollectionPageEnumerable(SkipCount, PageFetcher); } /// public BigInteger PageNumItems { get { return Enumerator.PageNumItems; } } /// public bool HasMoreItems { get { return Enumerator.HasMoreItems; } } /// public BigInteger TotalNumItems { get { return Enumerator.TotalNumItems; } } } /// /// Abstract Enumerator implementation. /// internal abstract class AbstractEnumerator : IEnumerator { private PageFetcher pageFetcher; private PageFetcher.Page page = null; private BigInteger? totalNumItems = null; private bool? hasMoreItems = null; /// /// The current element. /// protected T current; /// /// Constructor. /// /// the skip count /// the delegate that fetches a page public AbstractEnumerator(BigInteger skipCount, PageFetcher pageFetcher) { this.SkipCount = skipCount; this.pageFetcher = pageFetcher; } /// T IEnumerator.Current { get { return Current; } } /// object IEnumerator.Current { get { return Current; } } /// public T Current { get { return current; } } /// /// Reset is not supported. /// public void Reset() { throw new NotSupportedException(); } /// public abstract bool MoveNext(); /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// protected virtual void Dispose(bool disposing) { } /// /// Gets the skip count. /// public BigInteger SkipCount { get; protected set; } /// /// Gets the skip offset. /// public int SkipOffset { get; protected set; } /// /// Gets the current position. /// public BigInteger Position { get { return SkipCount + SkipOffset; } } /// /// Gets the number of items of the current page. /// public BigInteger PageNumItems { get { PageFetcher.Page page = GetCurrentPage(); if (page != null) { IList items = page.Items; if (items != null) { return items.Count; } } return 0; } } /// /// Gets the total number of items. /// public BigInteger TotalNumItems { get { if (totalNumItems == null) { totalNumItems = -1; PageFetcher.Page page = GetCurrentPage(); if (page != null) { totalNumItems = page.TotalNumItems; } } return (BigInteger)totalNumItems; } } /// /// Gets whether there are more items or not. /// public bool HasMoreItems { get { if (hasMoreItems == null) { hasMoreItems = false; PageFetcher.Page page = GetCurrentPage(); if (page != null) { if (page.HasMoreItems.HasValue) { hasMoreItems = page.HasMoreItems; } } } return (bool)hasMoreItems; } } /// /// Increments the skip offset. /// /// the new offset protected int IncrementSkipOffset() { return SkipOffset++; } /// /// Returns the current page. /// /// protected PageFetcher.Page GetCurrentPage() { if (page == null) { page = pageFetcher.FetchNextPage(SkipCount); } return page; } /// /// Fetches the next page. /// /// the next page protected PageFetcher.Page IncrementPage() { SkipCount += SkipOffset; SkipOffset = 0; totalNumItems = null; hasMoreItems = null; page = pageFetcher.FetchNextPage(SkipCount); return page; } } /// /// Page fetcher. /// internal class PageFetcher { /// /// A delegate that fetches a page. /// /// max number of items /// the skip count /// a page public delegate Page FetchPage(BigInteger maxNumItems, BigInteger skipCount); private FetchPage fetchPageDelegate; /// /// Constructor. /// /// max number of items /// the delegate that fetches a page public PageFetcher(BigInteger maxNumItems, FetchPage fetchPageDelegate) { MaxNumItems = maxNumItems; this.fetchPageDelegate = fetchPageDelegate; } /// /// Gets the max number of items. /// public BigInteger MaxNumItems { get; set; } /// /// Fetches the next page. /// /// the skip count /// the next page public Page FetchNextPage(BigInteger skipCount) { return fetchPageDelegate(MaxNumItems, skipCount); } /// /// A page. /// public class Page

{ ///

/// Constructor. /// /// list of items /// total number of items, if known /// a flag whether there are more items, if known public Page(IList

items, BigInteger? totalNumItems, bool? hasMoreItems) { Items = items; TotalNumItems = totalNumItems; HasMoreItems = hasMoreItems; } /// /// Gets the items of the page. /// public IList

Items { get; private set; } /// /// Gets the total number of items, if known. /// public BigInteger? TotalNumItems { get; private set; } /// /// Gets whether there are more items or not, if known. /// public bool? HasMoreItems { get; private set; } } } ///

/// CMIS Collection Enumerable. /// internal class CollectionEnumerable : AbstractEnumerable { /// /// Constructor. /// /// the delegate that fetches a page public CollectionEnumerable(PageFetcher pageFetcher) : this(0, pageFetcher) { } /// /// Constructor. /// /// the position /// the delegate that fetches a page public CollectionEnumerable(BigInteger position, PageFetcher pageFetcher) : base(position, pageFetcher) { } /// protected override AbstractEnumerator CreateEnumerator() { return new CollectionEnumerator(SkipCount, PageFetcher); } } /// /// Enumerator for iterating over all items in a CMIS Collection. /// internal class CollectionEnumerator : AbstractEnumerator { /// /// Constructor. /// /// the skip count /// the delegate that fetches a page public CollectionEnumerator(BigInteger skipCount, PageFetcher pageFetcher) : base(skipCount, pageFetcher) { } /// /// Move to the next items. /// /// true if there is a next item, false otherwise public override bool MoveNext() { PageFetcher.Page page = GetCurrentPage(); if (page == null) { return false; } IList items = page.Items; if (items == null || items.Count == 0) { return false; } if (SkipOffset == items.Count) { if (!HasMoreItems) { return false; } page = IncrementPage(); items = page == null ? null : page.Items; } if (items == null || items.Count == 0 || SkipOffset == items.Count) { return false; } current = items[IncrementSkipOffset()]; return true; } } /// /// Enumerable for a CMIS Collection Page. /// internal class CollectionPageEnumerable : AbstractEnumerable { /// /// Constructor. /// /// the delegate that fetches a page public CollectionPageEnumerable(PageFetcher pageFetcher) : this(0, pageFetcher) { } /// /// Constructor. /// /// the position /// the delegate that fetches a page public CollectionPageEnumerable(BigInteger position, PageFetcher pageFetcher) : base(position, pageFetcher) { } /// protected override AbstractEnumerator CreateEnumerator() { return new CollectionPageEnumerator(SkipCount, PageFetcher); } } /// /// Enumerator for iterating over a page of items in a CMIS Collection. /// internal class CollectionPageEnumerator : AbstractEnumerator { /// /// Constructor. /// /// the skip count /// the delegate that fetches a page public CollectionPageEnumerator(BigInteger skipCount, PageFetcher pageFetcher) : base(skipCount, pageFetcher) { } /// /// Move to the next items. /// /// true if there is a next item, false otherwise public override bool MoveNext() { PageFetcher.Page page = GetCurrentPage(); if (page == null) { return false; } IList items = page.Items; if (items == null || items.Count == 0 || SkipOffset == items.Count) { return false; } current = items[IncrementSkipOffset()]; return true; } } /// /// Content Stream helpers. /// public class ContentStreamUtils { /// Octet Stream MIME type. private const string OctetStream = "application/octet-stream"; private ContentStreamUtils() { } /// /// Creates a content stream object. /// /// the filename /// the length /// the MIME type /// the stream /// the content stream public static IContentStream CreateContentStream(string filename, BigInteger? length, string mimetype, Stream stream) { return new ContentStream() { FileName = CheckFilename(filename), Length = length, MimeType = CheckMimeType(mimetype), Stream = stream }; } // --- byte arrays --- /// /// Creates a content stream object from a byte array. /// /// the filename /// the byte array /// the MIME type /// the content stream public static IContentStream CreateByteArrayContentStream(string filename, byte[] contentBytes, string mimetype) { if (contentBytes == null) { return CreateContentStream(filename, null, mimetype, null); } return CreateByteArrayContentStream(filename, contentBytes, 0, contentBytes.Length, mimetype); } /// /// Creates a content stream object from a byte array. /// /// the filename /// the byte array /// the begin of the stream in the byte array /// the length of the stream /// the MIME type /// the content stream public static IContentStream CreateByteArrayContentStream(string filename, byte[] contentBytes, int index, int count, string mimetype) { if (contentBytes == null) { return CreateContentStream(filename, null, mimetype, null); } if (index < 0 || index > contentBytes.Length) { throw new ArgumentOutOfRangeException("index"); } else if (count < 0 || (index + count) > contentBytes.Length || (index + count) < 0) { throw new ArgumentOutOfRangeException("count"); } return CreateContentStream(filename, count, mimetype, new MemoryStream(contentBytes, index, count)); } // --- strings --- /// /// Creates a content stream object from a string. /// /// the filename /// the content /// the content stream public static IContentStream CreateTextContentStream(string filename, string content) { return CreateTextContentStream(filename, content, "text/plain; charset=UTF-8"); } /// /// Creates a content stream object from a string. /// /// the filename /// the content /// the MIME type /// the content stream public static IContentStream CreateTextContentStream(string filename, string content, string mimetype) { byte[] contentBytes = Encoding.UTF8.GetBytes(content); return CreateByteArrayContentStream(filename, contentBytes, CheckMimeType(mimetype)); } // --- private static string CheckFilename(string filename) { if (filename == null || filename.Length == 0) { return "content"; } return filename; } private static string CheckMimeType(string mimetype) { if (mimetype == null) { return OctetStream; } string result = mimetype.Trim(); if (result.Length < 3) { return OctetStream; } return result; } } internal class StringListBuilder { private string seperator; private bool first; public StringBuilder StringBuilder { get; private set; } public StringListBuilder() : this(",", new StringBuilder()) { } public StringListBuilder(StringBuilder stringBuilder) : this(",", stringBuilder) { } public StringListBuilder(string seperator) : this(seperator, new StringBuilder()) { } public StringListBuilder(string seperator, StringBuilder stringBuilder) { this.seperator = seperator; StringBuilder = stringBuilder; first = true; } public void Add(string s) { if (!first) { StringBuilder.Append(seperator); } else { first = false; } StringBuilder.Append(s); } override public string ToString() { return StringBuilder.ToString(); } } }