| /* |
| * 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) |
| { |
| Tracer.DebugFormat("Adding Component To Id, component {0}, current index: {1}", component, current); |
| 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 |
| } |