/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using DotCMIS.Binding.Impl; | |
using System.Web; | |
namespace DotCMIS.Binding.AtomPub | |
{ | |
internal class LinkCache | |
{ | |
private const int CacheSizeRepositories = 10; | |
private const int CacheSizeTypes = 100; | |
private const int CacheSizeLinks = 400; | |
private ICache linkCache; | |
private ICache typeLinkCache; | |
private ICache collectionLinkCache; | |
private ICache templateCache; | |
private ICache repositoryLinkCache; | |
public LinkCache(BindingSession session) | |
{ | |
int repCount = session.GetValue(SessionParameter.CacheSizeRepositories, CacheSizeRepositories); | |
if (repCount < 1) | |
{ | |
repCount = CacheSizeRepositories; | |
} | |
int typeCount = session.GetValue(SessionParameter.CacheSizeTypes, CacheSizeTypes); | |
if (typeCount < 1) | |
{ | |
typeCount = CacheSizeTypes; | |
} | |
int objCount = session.GetValue(SessionParameter.CacheSizeLinks, CacheSizeLinks); | |
if (objCount < 1) | |
{ | |
objCount = CacheSizeLinks; | |
} | |
linkCache = new Cache("Link Cache"); | |
linkCache.Initialize(new string[] { | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository | |
typeof(LruCacheLevel).FullName + " " + LruCacheLevel.MaxEntries + "=" + objCount, // id | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=16", // rel | |
typeof(ContentTypeCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=3," | |
+ DictionaryCacheLevel.SingleValue + "=true" // type | |
}); | |
typeLinkCache = new Cache("Type Link Cache"); | |
typeLinkCache.Initialize(new string[] { | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository | |
typeof(LruCacheLevel).FullName + " " + LruCacheLevel.MaxEntries + "=" + typeCount, // id | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=16", // rel | |
typeof(ContentTypeCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=3," | |
+ DictionaryCacheLevel.SingleValue + "=true"// type | |
}); | |
collectionLinkCache = new Cache("Collection Link Cache"); | |
collectionLinkCache.Initialize(new string[] { | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=8" // collection | |
}); | |
templateCache = new Cache("URI Template Cache"); | |
templateCache.Initialize(new string[] { | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=6" // type | |
}); | |
repositoryLinkCache = new Cache("Repository Link Cache"); | |
repositoryLinkCache.Initialize(new string[] { | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository | |
typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=6" // rel | |
}); | |
} | |
// ---- links --- | |
public void AddLink(string repositoryId, string id, string rel, string type, string link) | |
{ | |
linkCache.Put(new string[] { repositoryId, id, rel, type }, link); | |
} | |
public void RemoveLinks(string repositoryId, string id) | |
{ | |
linkCache.Remove(new string[] { repositoryId, id }); | |
} | |
public string GetLink(string repositoryId, string id, string rel, string type) | |
{ | |
return (string)linkCache.Get(new string[] { repositoryId, id, rel, type }); | |
} | |
public string GetLink(string repositoryId, string id, string rel) | |
{ | |
return GetLink(repositoryId, id, rel, null); | |
} | |
public int CheckLink(string repositoryId, string id, string rel, string type) | |
{ | |
return linkCache.Check(new string[] { repositoryId, id, rel, type }); | |
} | |
public void LockLinks() | |
{ | |
linkCache.Lock(); | |
} | |
public void UnlockLinks() | |
{ | |
linkCache.Unlock(); | |
} | |
// ---- type links --- | |
public void AddTypeLink(string repositoryId, string id, string rel, string type, string link) | |
{ | |
typeLinkCache.Put(new string[] { repositoryId, id, rel, type }, link); | |
} | |
public void RemoveTypeLinks(string repositoryId, string id) | |
{ | |
typeLinkCache.Remove(new string[] { repositoryId, id }); | |
} | |
public string GetTypeLink(string repositoryId, string id, string rel, string type) | |
{ | |
return (string)typeLinkCache.Get(new string[] { repositoryId, id, rel, type }); | |
} | |
public void LockTypeLinks() | |
{ | |
typeLinkCache.Lock(); | |
} | |
public void UnlockTypeLinks() | |
{ | |
typeLinkCache.Unlock(); | |
} | |
// ---- collections ---- | |
public void AddCollection(string repositoryId, string collection, string link) | |
{ | |
collectionLinkCache.Put(new string[] { repositoryId, collection }, link); | |
} | |
public string GetCollection(string repositoryId, string collection) | |
{ | |
return (string)collectionLinkCache.Get(new string[] { repositoryId, collection }); | |
} | |
// ---- templates ---- | |
public void AddTemplate(string repositoryId, string type, string link) | |
{ | |
templateCache.Put(new string[] { repositoryId, type }, link); | |
} | |
public string GetTemplateLink(string repositoryId, string type, IDictionary<string, object> parameters) | |
{ | |
string template = (string)templateCache.Get(new string[] { repositoryId, type }); | |
if (template == null) | |
{ | |
return null; | |
} | |
StringBuilder result = new StringBuilder(); | |
StringBuilder param = new StringBuilder(); | |
bool paramMode = false; | |
for (int i = 0; i < template.Length; i++) | |
{ | |
char c = template[i]; | |
if (paramMode) | |
{ | |
if (c == '}') | |
{ | |
paramMode = false; | |
object paramValue; | |
if (parameters.TryGetValue(param.ToString(), out paramValue)) | |
{ | |
result.Append(HttpUtility.UrlEncode(UrlBuilder.NormalizeParameter(paramValue))); | |
} | |
param = new StringBuilder(); | |
} | |
else | |
{ | |
param.Append(c); | |
} | |
} | |
else | |
{ | |
if (c == '{') | |
{ | |
paramMode = true; | |
} | |
else | |
{ | |
result.Append(c); | |
} | |
} | |
} | |
return result.ToString(); | |
} | |
// ---- repository links ---- | |
public void AddRepositoryLink(string repositoryId, string rel, string link) | |
{ | |
repositoryLinkCache.Put(new string[] { repositoryId, rel }, link); | |
} | |
public string GetRepositoryLink(string repositoryId, string rel) | |
{ | |
return (string)repositoryLinkCache.Get(new string[] { repositoryId, rel }); | |
} | |
// ---- clear ---- | |
public void ClearRepository(string repositoryId) | |
{ | |
linkCache.Remove(new string[] { repositoryId }); | |
typeLinkCache.Remove(new string[] { repositoryId }); | |
collectionLinkCache.Remove(new string[] { repositoryId }); | |
templateCache.Remove(new string[] { repositoryId }); | |
repositoryLinkCache.Remove(new string[] { repositoryId }); | |
} | |
} | |
internal class ContentTypeCacheLevel : DictionaryCacheLevel | |
{ | |
public ContentTypeCacheLevel() | |
{ | |
EnableKeyFallback(null); | |
} | |
public override object this[string key] | |
{ | |
get | |
{ | |
return base[Normalize(key)]; | |
} | |
set | |
{ | |
base[Normalize(key)] = value; | |
} | |
} | |
public override void Remove(string key) | |
{ | |
base.Remove(Normalize(key)); | |
} | |
private string Normalize(string key) | |
{ | |
if (key == null) | |
{ | |
return null; | |
} | |
StringBuilder sb = new StringBuilder(); | |
int parameterStart = 0; | |
// first, get the MIME type | |
for (int i = 0; i < key.Length; i++) | |
{ | |
char c = key[i]; | |
if (Char.IsWhiteSpace(c)) | |
{ | |
continue; | |
} | |
else if (c == ';') | |
{ | |
parameterStart = i; | |
break; | |
} | |
sb.Append(Char.ToLower(c)); | |
} | |
// if parameters have been found, gather them | |
if (parameterStart > 0) | |
{ | |
SortedList<string, string> parameter = new SortedList<string, string>(); | |
StringBuilder ksb = new StringBuilder(); | |
StringBuilder vsb = new StringBuilder(); | |
bool isKey = true; | |
for (int i = parameterStart + 1; i < key.Length; i++) | |
{ | |
char c = key[i]; | |
if (Char.IsWhiteSpace(c)) | |
{ | |
continue; | |
} | |
if (isKey) | |
{ | |
if (c == '=') | |
{ | |
// value start | |
isKey = false; | |
continue; | |
} | |
ksb.Append(Char.ToLower(c)); | |
} | |
else | |
{ | |
if (c == ';') | |
{ | |
// next key | |
isKey = true; | |
parameter.Add(ksb.ToString(), vsb.ToString()); | |
ksb = new StringBuilder(); | |
vsb = new StringBuilder(); | |
continue; | |
} | |
else if (c == '"') | |
{ | |
// filter quotes | |
continue; | |
} | |
vsb.Append(Char.ToLower(c)); | |
} | |
} | |
// add last parameter | |
if (ksb.Length > 0) | |
{ | |
parameter.Add(ksb.ToString(), vsb.ToString()); | |
} | |
// write parameters sorted by key | |
for (int i = 0; i < parameter.Count; i++) | |
{ | |
sb.Append(";"); | |
sb.Append(parameter.Keys[i]); | |
sb.Append("="); | |
sb.Append(parameter.Values[i]); | |
} | |
} | |
return sb.ToString(); | |
} | |
} | |
} |