blob: f3d1c7aa5d24626c7fdd64aeb28334b232d00f3c [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 System;
using System.Net;
using System.Security.Permissions;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Apache.NMS.Util;
using Apache.NMS;
namespace Apache.NMS.AMQP.Util
{
#region Id Class
public class Id : IComparable
{
public static readonly Id EMPTY = new Id();
protected const int DEFAULT_MAX_CAPACITY = 1;
protected static readonly int[] HashTable = new int[] { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
protected static readonly int HashTableSize = HashTable.Length;
protected delegate ComponentId InstanceFactory(object o);
protected static readonly Dictionary<Type, InstanceFactory> buildMap;
#region Class Initializer
static Id()
{
buildMap = new Dictionary<Type, InstanceFactory>();
buildMap.Add(typeof(String), (o) => { return new ComponentId<string>(o as string); });
buildMap.Add(typeof(UInt16), (o) => { return new ComponentId<UInt16>(Convert.ToUInt16(o)); });
buildMap.Add(typeof(UInt32), (o) => { return new ComponentId<UInt32>(Convert.ToUInt32(o)); });
buildMap.Add(typeof(UInt64), (o) => { return new ComponentId<UInt64>(Convert.ToUInt64(o)); });
buildMap.Add(typeof(Int16), (o) => { return new ComponentId<UInt16>(Convert.ToUInt16(o)); });
buildMap.Add(typeof(Int32), (o) => { return new ComponentId<UInt32>(Convert.ToUInt32(o)); });
buildMap.Add(typeof(Int64), (o) => { return new ComponentId<UInt64>(Convert.ToUInt64(o)); });
buildMap.Add(typeof(AtomicSequence), (o) =>
{
ulong val = 0;
AtomicSequence seq = o as AtomicSequence;
if (o != null && seq != null)
{
val = seq.getAndIncrement();
}
return new ComponentId<UInt64>(val);
});
buildMap.Add(typeof(Guid), (o) =>
{
Guid id;
if (o == null)
{
id = Guid.Empty;
}
else
{
id = (Guid)o;
}
return new ComponentId<Guid>(id);
});
buildMap.Add(typeof(Id), (o) => { return new ComponentId<Id>(o as Id); });
}
#endregion
ComponentId[] components;
private bool isReadOnly = false;
private int length;
private int maxCapacity;
private int current;
private string componentDelimeter;
private int hashcode = 0;
#region Constructors
public Id(string delimeter, int size, int maxSize)
{
maxCapacity = Math.Max(maxSize, DEFAULT_MAX_CAPACITY);
length = size;
components = new ComponentId[length];
current = 0;
componentDelimeter = delimeter;
}
public Id(int size, int maxSize) : this(IdGenerator.ID_COMPONENT_DELIMETER, size, maxSize) { }
public Id(int size) : this(IdGenerator.ID_COMPONENT_DELIMETER, size, size) { }
public Id(params object[] args) : this(args.Length)
{
int added = this.AddAll(args);
if(added > maxCapacity)
{
Tracer.ErrorFormat("Id Format error.");
}
Generate();
}
#endregion
#region Public Properties
public int Size
{
get { return length; }
}
#endregion
#region Public Methods
public void Add(object component)
{
if (isReadOnly)
{
throw new NMSException("Invalid Operation when generating Component Id. Can not change id once generated.");
}
if (current >= maxCapacity)
{
throw new NMSException("Invalid Operation when generating Component Id. Can not add component. Adding Compoenent at full capacity " + maxCapacity);
}
if (current >= length)
{
grow(length * 2);
}
Type type = component.GetType();
InstanceFactory instf = null;
buildMap.TryGetValue(type, out instf);
if (instf == null)
{
throw new NMSException(string.Format("Invalid Id component type {0} for component {1}", type.ToString(), component.ToString()));
}
components[current] = instf(component);
current++;
}
public object[] GetComponents(int startIndex=0)
{
return GetComponents(startIndex, length);
}
public object[] GetComponents(int startIndex, int endIndex)
{
int eIndex = Math.Max(0,Math.Min(endIndex, length));
int sIndex = Math.Max(0, startIndex);
int resultLen = eIndex - sIndex;
if (resultLen<0)
{
return null;
}
object[] comps = new object[resultLen];
int index = 0;
for(int i=sIndex; i< eIndex; i++)
{
comps[index] = components[i].IdValue;
index++;
}
return comps;
}
public object GetComponent(int index)
{
if (isReadOnly)
{
if (index >= 0 && index < length)
{
return components[index].IdValue;
}
}
return null;
}
public object GetFirstComponent(Type type)
{
if (isReadOnly)
{
for (int i = 0; i < length; i++)
{
ComponentId cid = components[i];
if (cid.ValueType.Equals(type))
{
return cid.IdValue;
}
}
}
return null;
}
public object GetLastComponent(Type type)
{
if (isReadOnly)
{
for (int i = length; i > 0; i--)
{
ComponentId cid = components[i - 1];
if (cid.ValueType.Equals(type))
{
return cid.IdValue;
}
}
}
return null;
}
public void Generate()
{
if (!isReadOnly)
{
isReadOnly = true;
this.GetHashCode();
}
}
#endregion
#region Object Override Methods
public override bool Equals(object obj)
{
if(GetHashCode() == obj.GetHashCode())
{
return true;
}
else
{
return CompareTo(obj) == 0;
}
}
public override int GetHashCode()
{
if(hashcode == 0 && isReadOnly && length > 0)
{
int hashIndex = 0;
ComponentId cid = components[0];
hashcode = HashTable[hashIndex] * cid.GetHashCode();
for (int i=1; i<length; i++)
{
cid = components[i];
hashIndex = i % HashTableSize;
hashcode = hashcode ^ HashTable[hashIndex] * cid.GetHashCode();
}
}
return hashcode;
}
public override string ToString()
{
if (maxCapacity == 0)
{
return "0";
}
if (isReadOnly)
{
if (length == 0)
{
return EMPTY.ToString();
}
StringBuilder sb = new StringBuilder();
ComponentId cid = this.components[0];
sb.Append(cid.ToString());
for (int i = 1; i < length; i++)
{
cid = this.components[i];
sb.Append(componentDelimeter);
sb.Append(cid.ToString());
}
return sb.ToString();
}
else
{
return base.ToString();
}
}
#endregion
#region IComparable Methods
public int CompareTo(object obj)
{
if(obj!=null && obj is Id)
{
return CompareTo(obj as Id);
}
else if(obj == null)
{
return 1;
}
else
{
return -1;
}
}
public int CompareTo(Id that)
{
if(this.length > that.length)
{
return 1;
}
else if (this.length < that.length)
{
return -1;
}
else
{
int compare = 0;
for(int i=0; i<length; i++)
{
ComponentId thisCid = this.components[i];
ComponentId thatCid = that.components[i];
compare = thisCid.CompareTo(thatCid);
if ( compare > 0)
{
return 1;
}
else if ( compare < 0 )
{
return -1;
}
}
return compare;
}
}
#endregion
#region Protected Methods
protected void grow(int newCapacity)
{
int size = Math.Min(newCapacity, maxCapacity);
ComponentId[] buffer = new ComponentId[size];
Array.Copy(this.components, buffer, this.length);
length = size;
this.components = buffer;
}
protected int AddAll(params object[] args)
{
#if NET46
if(Tracer.IsDebugEnabled)
{
Tracer.DebugFormat("Adding Id components: {0} MaxCapacity: {1}", string.Join(",",
args.Select(x => x.ToString()).ToArray()), maxCapacity);
}
#else
#endif
int added = 0;
foreach (object o in args)
{
Type type = o.GetType();
if (type.IsArray && type.Equals(typeof(object[])))
{
object[] moreArgs = o as object[];
int addlen = (moreArgs).Length;
maxCapacity = maxCapacity + addlen - 1;
added += this.AddAll(moreArgs);
}
else
{
this.Add(o);
added++;
}
}
return added;
}
#endregion
#region Inner ComponentId Classes
protected abstract class ComponentId : IComparable
{
protected object value;
protected ComponentId(object idvalue)
{
value = idvalue;
}
public object IdValue { get { return value; } }
public abstract Type ValueType { get; }
public virtual int CompareTo(object obj)
{
if(obj == null)
{
return 1;
}
else if(obj is ComponentId)
{
return CompareTo(obj as ComponentId);
}
else if (obj is IComparable)
{
return -1 * (obj as IComparable).CompareTo(this.IdValue);
}
else
{
return -1;
}
}
public virtual int CompareTo(ComponentId that)
{
if (this.ValueType.Equals(that.ValueType) || this.ValueType.IsEquivalentTo(that.ValueType))
{
if (this.IdValue.Equals(that.IdValue))
{
return 0;
}
else
{
return this.GetHashCode() - that.GetHashCode();
}
}
else if (this.IdValue is IComparable)
{
return (this.IdValue as IComparable).CompareTo(that.IdValue);
}
else if (that.IdValue is IComparable)
{
return -1 * (that.IdValue as IComparable).CompareTo(this.IdValue);
}
else
{
return this.ValueType.GetHashCode() - that.ValueType.GetHashCode();
}
}
public override bool Equals(object obj)
{
return CompareTo(obj) == 0;
}
public override int GetHashCode()
{
return this.IdValue.GetHashCode();
}
public override string ToString()
{
return value.ToString();
}
}
protected class ComponentId<T> : ComponentId
{
public ComponentId(T val) : base(val)
{
}
public T Value
{
get { return (T)this.value; }
}
public override Type ValueType
{
get
{
if (value != null)
{
return value.GetType();
}
return default(T).GetType();
}
}
}
#endregion
}
#endregion
#region IdGenerator Class
public class IdGenerator
{
public const string ID_COMPONENT_DELIMETER = ":";
protected static readonly string DEFAULT_PREFIX = "ID";
protected static string hostname = null;
protected readonly string prefix;
protected readonly AtomicSequence sequence = new AtomicSequence(1);
#region Class Initializer
static IdGenerator()
{
#if NETSTANDARD2_0
hostname = Dns.GetHostName();
#else
DnsPermission permissions = null;
try
{
permissions = new DnsPermission(PermissionState.Unrestricted);
}
catch (Exception e)
{
Tracer.InfoFormat("{0}", e.StackTrace);
}
if (permissions != null)
{
hostname = Dns.GetHostName();
}
#endif
}
#endregion
#region Constructors
public IdGenerator(string prefix)
{
this.prefix = RemoveEnd(prefix, ID_COMPONENT_DELIMETER)
+ ((hostname == null)
?
""
:
ID_COMPONENT_DELIMETER + hostname)
;
}
public IdGenerator() : this(DEFAULT_PREFIX)
{
}
#endregion
public virtual Id GenerateId()
{
Id id = new Id(this.prefix, Guid.NewGuid(), sequence);
return id;
}
public virtual string generateID()
{
Id id = GenerateId();
return id.ToString();
//return string.Format("{0}{1}" + ID_COMPONENT_DELIMETER + "{2}", this.prefix, Guid.NewGuid().ToString(), sequence.getAndIncrement());
}
protected static string RemoveEnd(string s, string end)
{
string result = s;
if (s != null && end != null && s.EndsWith(end))
{
int sLen = s.Length;
int endLen = end.Length;
int newLen = sLen - endLen;
if (endLen > 0 && newLen > 0)
{
StringBuilder sb = new StringBuilder(s, 0, newLen, newLen);
result = sb.ToString();
}
}
return result;
}
}
#endregion
#region Derivative IdGenerator Classes
class NestedIdGenerator : IdGenerator
{
protected Id parentId;
protected bool removeParentPrefix;
public NestedIdGenerator(string prefix, Id pId, bool remove) : base(prefix)
{
parentId = pId;
removeParentPrefix = remove;
}
public NestedIdGenerator(string prefix, Id pId) : this(prefix, pId, false) { }
public NestedIdGenerator(Id pId):this(DEFAULT_PREFIX, pId) { }
public override Id GenerateId()
{
Id id;
if (removeParentPrefix)
{
int componentIndex = (parentId.Size == 1) ? 0 : 1;
id = new Id(prefix, parentId.GetComponents(componentIndex), sequence);
}
else
{
id = new Id(prefix, parentId, sequence);
}
return id;
}
public override string generateID()
{
return GenerateId().ToString();
}
}
class CustomIdGenerator : IdGenerator
{
protected object[] parts;
protected bool isOnlyParts;
public CustomIdGenerator(string prefix, params object[] args) : base(prefix)
{
parts = args;
}
public CustomIdGenerator(bool onlyParts, params object[] args) : this(DEFAULT_PREFIX, args)
{
isOnlyParts = onlyParts;
}
public CustomIdGenerator(Id pId) : this(DEFAULT_PREFIX, pId) { }
public override Id GenerateId()
{
Id id;
if (isOnlyParts)
{
id = new Id(parts);
}
else
{
id = new Id(prefix, parts, sequence);
}
return id;
}
public override string generateID()
{
return GenerateId().ToString();
}
}
#endregion
}