e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
273 lines
7.4 KiB
C#
273 lines
7.4 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|