File: System\Activities\QualifiedId.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities
{
    using System;
    using System.Collections.Generic;
    using System.Runtime;
    using System.Text;
 
    class QualifiedId : IEquatable<QualifiedId>
    {
        byte[] compressedId;
 
        public QualifiedId(Activity element)
        {
            int bufferSize = 0;
 
            Stack<int> ids = new Stack<int>();
            
            int id = element.InternalId;
            bufferSize += GetEncodedSize(id);
            ids.Push(id);
 
            IdSpace space = element.MemberOf;
 
            while (space != null && space.ParentId != 0)
            {
                bufferSize += GetEncodedSize(space.ParentId);
                ids.Push(space.ParentId);
 
                space = space.Parent;
            }
 
            this.compressedId = new byte[bufferSize];
 
            int offset = 0;
            while (ids.Count > 0)
            {
                offset += Encode(ids.Pop(), this.compressedId, offset);
            }
        }
 
        public QualifiedId(byte[] bytes)
        {
            this.compressedId = bytes;
        }
 
        public QualifiedId(int[] idArray)
        {
            int bufferSize = 0;
 
            for (int i = 0; i < idArray.Length; i++)
            {
                bufferSize += GetEncodedSize(idArray[i]);
            }
 
            this.compressedId = new byte[bufferSize];
 
            int offset = 0;
            for (int i = 0; i < idArray.Length; i++)
            {
                offset += Encode(idArray[i], this.compressedId, offset);
            }
        }
 
        public static bool TryGetElementFromRoot(Activity root, QualifiedId id, out Activity targetElement)
        {
            return TryGetElementFromRoot(root, id.compressedId, out targetElement);
        }
 
        public static bool TryGetElementFromRoot(Activity root, byte[] idBytes, out Activity targetElement)
        {
            Fx.Assert(root.MemberOf != null, "We need to have our IdSpaces set up for this to work.");
 
            Activity currentActivity = root;
            IdSpace currentIdSpace = root.MemberOf;
 
            int offset = 0;
            while (offset < idBytes.Length)
            {
                int value;
                offset += Decode(idBytes, offset, out value);
 
                if (currentIdSpace == null)
                {
                    targetElement = null;
                    return false;
                }
 
                currentActivity = currentIdSpace[value];
 
                if (currentActivity == null)
                {
                    targetElement = null;
                    return false;
                }
 
                currentIdSpace = currentActivity.ParentOf;
            }
 
            targetElement = currentActivity;
            return true;
        }
 
        public static QualifiedId Parse(string value)
        {
            QualifiedId result;
            if (!TryParse(value, out result))
            {
                throw FxTrace.Exception.AsError(new FormatException(SR.InvalidActivityIdFormat));
            }
 
            return result;
        }
 
        public static bool TryParse(string value, out QualifiedId result)
        {
            Fx.Assert(!string.IsNullOrEmpty(value), "We should have already made sure it isn't null or empty.");
 
            string[] idStrings = value.Split('.');
            int[] ids = new int[idStrings.Length];
            int bufferSize = 0;
 
            for (int i = 0; i < idStrings.Length; i++)
            {
                // only support non-negative integers as id segments
                int parsedInt;
                if (!int.TryParse(idStrings[i], out parsedInt) || parsedInt < 0)
                {
                    result = null;
                    return false;
                }
 
                ids[i] = parsedInt;
                bufferSize += GetEncodedSize(ids[i]);
            }
 
            byte[] bytes = new byte[bufferSize];
            int offset = 0;
 
            for (int i = 0; i < ids.Length; i++)
            {
                offset += Encode(ids[i], bytes, offset);
            }
 
            result = new QualifiedId(bytes);
            return true;
        }
 
        public static bool Equals(byte[] lhs, byte[] rhs)
        {
            if (lhs.Length == rhs.Length)
            {
                for (int i = 0; i < lhs.Length; i++)
                {
                    if (lhs[i] != rhs[i])
                    {
                        return false;
                    }
                }
 
                return true;
            }
 
            return false;
        }
 
        public byte[] AsByteArray()
        {
            // Note that we don't do a copy because we assume all users will
            // treat it as immutable.
            return this.compressedId;
        }
 
        public int[] AsIDArray()
        {
            List<int> tmpList = new List<int>();
            int offset = 0;
            while (offset < this.compressedId.Length)
            {
                int value;
                offset += Decode(this.compressedId, offset, out value);
 
                tmpList.Add(value);
            }
            return tmpList.ToArray();
        }
 
        public bool Equals(QualifiedId rhs)
        {
            return Equals(this.compressedId, rhs.compressedId);
        }
 
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
 
            bool needDot = false;
            int offset = 0;
            while (offset < this.compressedId.Length)
            {
                if (needDot)
                {
                    builder.Append('.');   
                }
 
                int value;
                offset += Decode(this.compressedId, offset, out value);
 
                builder.Append(value);
 
                needDot = true;
            }
 
            return builder.ToString();
        }
 
        // This is the same Encode/Decode logic as the WCF FramingEncoder
        static int Encode(int value, byte[] bytes, int offset)
        {
            Fx.Assert(value >= 0, "Must be non-negative");
 
            int count = 1;
            while ((value & 0xFFFFFF80) != 0)
            {
                bytes[offset++] = (byte)((value & 0x7F) | 0x80);
                count++;
                value >>= 7;
            }
            bytes[offset] = (byte)value;
            return count;
        }
 
        // This is the same Encode/Decode logic as the WCF FramingEncoder
        static int Decode(byte[] buffer, int offset, out int value)
        {
            int bytesConsumed = 0;
            value = 0;
            
            while (offset < buffer.Length)
            {
                int next = buffer[offset];
                value |= (next & 0x7F) << (bytesConsumed * 7);
                bytesConsumed++;
                if ((next & 0x80) == 0)
                {
                    break;
                }
                offset++;
            }
 
            return bytesConsumed;
        }
 
        static int GetEncodedSize(int value)
        {
            Fx.Assert(value >= 0, "Must be non-negative");
 
            int count = 1;
            while ((value & 0xFFFFFF80) != 0)
            {
                count++;
                value >>= 7;
            }
            return count;
        }
    }
}