File: System\Activities\Debugger\Symbol\WorkflowSymbol.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Debugger.Symbol
{
    using System;
    using System.IO;
    using System.IO.Compression;
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Globalization;
    using System.Collections.Generic;
    using System.Security.Cryptography;
 
    // Represent debug symbol of a workflow tree (similar to pdb file).
    // It contains the absolute path of the xaml file and the location of each activity in the workflow tree.
    // This is used to instrument the workflow without having access to the original xaml file.
    public class WorkflowSymbol
    {
        public string FileName { get; set; }
        public ICollection<ActivitySymbol> Symbols { get; set; }
 
        private byte[] checksum;
        public byte[] GetChecksum()
        {
            if (this.checksum == null)
            {
                return null;
            }
            return (byte[]) this.checksum.Clone();
        }
        
        [Flags]
        internal enum EncodingFormat : byte
        {
            String = 0x76,    // Format as well as cookie. String format is hidden from public.
            Binary = 0x77,
            Checksum = 0x80            
        }
 
        internal const EncodingFormat DefaultEncodingFormat = EncodingFormat.Binary;
 
        public WorkflowSymbol()
        {
        }
 
        // These constructors are private and used by Decode() method.
 
        // Binary deserializer.
        WorkflowSymbol(BinaryReader reader, byte[] checksum)
        {
            this.FileName = reader.ReadString();
            int numSymbols = SymbolHelper.ReadEncodedInt32(reader);
            this.Symbols = new List<ActivitySymbol>(numSymbols);
            for (int i = 0; i < numSymbols; ++i)
            {
                this.Symbols.Add(new ActivitySymbol(reader));
            }
            this.checksum = checksum;
        }
 
        // Decode from Base64 string.
        public static WorkflowSymbol Decode(string symbolString)
        {
            byte[] data = Convert.FromBase64String(symbolString);
            using (BinaryReader reader = new BinaryReader(new MemoryStream(data)))
            {
                byte[] checksum = null;
                EncodingFormat format = (EncodingFormat)reader.ReadByte();
                int payloadBytesCount = data.Length - sizeof(EncodingFormat);
                if (0 != (format & EncodingFormat.Checksum))
                {
                    int bytesCount = SymbolHelper.ReadEncodedInt32(reader);
                    checksum = reader.ReadBytes(bytesCount);
                    payloadBytesCount -= SymbolHelper.GetEncodedSize(bytesCount);
                    format &= (~EncodingFormat.Checksum);
                }
                switch (format)
                {
                    case EncodingFormat.Binary:
                        return ParseBinary(reader.ReadBytes(payloadBytesCount), checksum); // Compute the 
                    case EncodingFormat.String:
                        return ParseStringRepresentation(reader.ReadString(), checksum);
                }
            }
            throw FxTrace.Exception.AsError(new SerializationException());
        }
 
        // Serialization
 
        // Encode to Base64 string
        public string Encode()
        {
            return Encode(WorkflowSymbol.DefaultEncodingFormat); // default format
        }
 
        internal string Encode(EncodingFormat encodingFormat)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(ms))
                {
                    if (this.checksum != null)
                    {
                        writer.Write((byte)(encodingFormat | EncodingFormat.Checksum));
                        SymbolHelper.WriteEncodedInt32(writer, this.checksum.Length);
                        writer.Write(this.checksum);
                    }
                    else
                    {
                        writer.Write((byte)encodingFormat);
                    }
                    switch (encodingFormat)
                    {
                        case EncodingFormat.Binary:
                            this.Write(writer);
                            break;
                        case EncodingFormat.String:
                            writer.Write(this.ToString());
                            break;
                        default:
                            throw FxTrace.Exception.AsError(new SerializationException());
                    }
                    // Need to copy to a buffer to trim excess capacity.
                    byte[] buffer = new byte[ms.Length];
                    Array.Copy(ms.GetBuffer(), buffer, ms.Length);
                    return Convert.ToBase64String(buffer);
                }
            } 
        }
 
        // Binary deserializer
        static WorkflowSymbol ParseBinary(byte[] bytes, byte[] checksum)
        {
            using (BinaryReader reader = new BinaryReader(new MemoryStream(bytes)))
            {
                return new WorkflowSymbol(reader, checksum);
            }
        }
 
        // Binary serializer
        void Write(BinaryWriter writer)
        {
            writer.Write(this.FileName ?? string.Empty);
            if (this.Symbols != null)
            {
                SymbolHelper.WriteEncodedInt32(writer, this.Symbols.Count);
                foreach (ActivitySymbol actSym in this.Symbols)
                {
                    actSym.Write(writer);
                }
            }
            else
            {
                SymbolHelper.WriteEncodedInt32(writer, 0);
            }
        }
 
        // String encoding serialization.
 
        // This is used for String encoding format.
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("{0}", this.FileName ?? string.Empty);
            if (this.Symbols != null)
            {
                foreach (ActivitySymbol symbol in Symbols)
                {
                    builder.AppendFormat(";{0}", symbol.ToString());
                }
            }
            return builder.ToString();
        }
 
        // Deserialization of string encoding format.
        static WorkflowSymbol ParseStringRepresentation(string symbolString, byte[] checksum)
        {
            string[] s = symbolString.Split(';');
            int numSymbols = s.Length - 1;
            ActivitySymbol[] symbols = new ActivitySymbol[numSymbols];
            for (int i = 0; i < numSymbols; ++i)
            {
                string[] symbolSegments = s[i + 1].Split(',');
                Fx.Assert(symbolSegments.Length == 5, "Invalid activity symbol");
                symbols[i] = new ActivitySymbol
                {
                    QualifiedId = QualifiedId.Parse(symbolSegments[0]).AsByteArray(),
                    StartLine = int.Parse(symbolSegments[1], CultureInfo.InvariantCulture),
                    StartColumn = int.Parse(symbolSegments[2], CultureInfo.InvariantCulture),
                    EndLine = int.Parse(symbolSegments[3], CultureInfo.InvariantCulture),
                    EndColumn = int.Parse(symbolSegments[4], CultureInfo.InvariantCulture)
                };
            }
 
            return new WorkflowSymbol
            {
                FileName = s[0],
                Symbols = symbols,
                checksum = checksum
            };
 
        }
 
        public bool CalculateChecksum()
        {
            this.checksum = null;
            if (!string.IsNullOrEmpty(this.FileName))
            {
                this.checksum = SymbolHelper.CalculateChecksum(this.FileName);
            }
            return (this.checksum != null);
        }
 
    }
}