Mono.Zeroconf – Rev 1
?pathlinks?
// Copyright 2006 Alp Toker <alp@atoker.com>
// This software is made available under the MIT License
// See COPYING for details
using System;
using System.Text;
using System.Collections.Generic;
//TODO: Reflection should be done at a higher level than this class
using System.Reflection;
namespace NDesk.DBus
{
//maybe this should be nullable?
struct Signature
{
//TODO: this class needs some work
//Data should probably include the null terminator
public static readonly Signature Empty = new Signature (String.Empty);
public static bool operator == (Signature a, Signature b)
{
/*
//TODO: remove this hack to handle bad case when Data is null
if (a.data == null || b.data == null)
throw new Exception ("Encountered Signature with null buffer");
*/
/*
if (a.data == null && b.data == null)
return true;
if (a.data == null || b.data == null)
return false;
*/
if (a.data.Length != b.data.Length)
return false;
for (int i = 0 ; i != a.data.Length ; i++)
if (a.data[i] != b.data[i])
return false;
return true;
}
public static bool operator != (Signature a, Signature b)
{
return !(a == b);
}
public override bool Equals (object o)
{
if (o == null)
return false;
if (!(o is Signature))
return false;
return this == (Signature)o;
}
public override int GetHashCode ()
{
return data.GetHashCode ();
}
public static Signature operator + (Signature s1, Signature s2)
{
return Concat (s1, s2);
}
//these need to be optimized
public static Signature Concat (Signature s1, Signature s2)
{
return new Signature (s1.Value + s2.Value);
}
public static Signature Copy (Signature sig)
{
return new Signature (sig.data);
}
public Signature (string value)
{
this.data = Encoding.ASCII.GetBytes (value);
}
public Signature (byte[] value)
{
this.data = (byte[])value.Clone ();
}
//this will become obsolete soon
internal Signature (DType value)
{
this.data = new byte[] {(byte)value};
}
internal Signature (DType[] value)
{
this.data = new byte[value.Length];
/*
MemoryStream ms = new MemoryStream (this.data);
foreach (DType t in value)
ms.WriteByte ((byte)t);
*/
for (int i = 0 ; i != value.Length ; i++)
this.data[i] = (byte)value[i];
}
byte[] data;
//TODO: this should be private, but MessageWriter and Monitor still use it
//[Obsolete]
public byte[] GetBuffer ()
{
return data;
}
internal DType this[int index]
{
get {
return (DType)data[index];
}
}
public int Length
{
get {
return data.Length;
}
}
//[Obsolete]
public string Value
{
get {
/*
//FIXME: hack to handle bad case when Data is null
if (data == null)
return String.Empty;
*/
return Encoding.ASCII.GetString (data);
}
}
public override string ToString ()
{
return Value;
/*
StringBuilder sb = new StringBuilder ();
foreach (DType t in data) {
//we shouldn't rely on object mapping here, but it's an easy way to get string representations for now
Type type = DTypeToType (t);
if (type != null) {
sb.Append (type.Name);
} else {
char c = (char)t;
if (!Char.IsControl (c))
sb.Append (c);
else
sb.Append (@"\" + (int)c);
}
sb.Append (" ");
}
return sb.ToString ();
*/
}
public Signature MakeArraySignature ()
{
return new Signature (DType.Array) + this;
}
public static Signature MakeStruct (params Signature[] elems)
{
Signature sig = Signature.Empty;
sig += new Signature (DType.StructBegin);
foreach (Signature elem in elems)
sig += elem;
sig += new Signature (DType.StructEnd);
return sig;
}
public static Signature MakeDictEntry (Signature keyType, Signature valueType)
{
Signature sig = Signature.Empty;
sig += new Signature (DType.DictEntryBegin);
sig += keyType;
sig += valueType;
sig += new Signature (DType.DictEntryEnd);
return sig;
}
public static Signature MakeDict (Signature keyType, Signature valueType)
{
return MakeDictEntry (keyType, valueType).MakeArraySignature ();
}
/*
//TODO: complete this
public bool IsPrimitive
{
get {
if (this == Signature.Empty)
return true;
return false;
}
}
*/
public bool IsDict
{
get {
if (Length < 3)
return false;
if (!IsArray)
return false;
if (this[2] != DType.DictEntryBegin)
return false;
return true;
}
}
public bool IsArray
{
get {
if (Length < 2)
return false;
if (this[0] != DType.Array)
return false;
return true;
}
}
public Signature GetElementSignature ()
{
if (!IsArray)
throw new Exception ("Cannot get the element signature of a non-array (signature was '" + this + "')");
//TODO: improve this
if (Length != 2)
throw new NotSupportedException ("Parsing signatures with more than one primitive value is not supported (signature was '" + this + "')");
return new Signature (this[1]);
}
public Type[] ToTypes ()
{
List<Type> types = new List<Type> ();
for (int i = 0 ; i != data.Length ; types.Add (ToType (ref i)));
return types.ToArray ();
}
public Type ToType ()
{
int pos = 0;
Type ret = ToType (ref pos);
if (pos != data.Length)
throw new Exception ("Signature '" + Value + "' is not a single complete type");
return ret;
}
internal static DType TypeCodeToDType (TypeCode typeCode)
{
switch (typeCode)
{
case TypeCode.Empty:
return DType.Invalid;
case TypeCode.Object:
return DType.Invalid;
case TypeCode.DBNull:
return DType.Invalid;
case TypeCode.Boolean:
return DType.Boolean;
case TypeCode.Char:
return DType.UInt16;
case TypeCode.SByte:
return DType.Byte;
case TypeCode.Byte:
return DType.Byte;
case TypeCode.Int16:
return DType.Int16;
case TypeCode.UInt16:
return DType.UInt16;
case TypeCode.Int32:
return DType.Int32;
case TypeCode.UInt32:
return DType.UInt32;
case TypeCode.Int64:
return DType.Int64;
case TypeCode.UInt64:
return DType.UInt64;
case TypeCode.Single:
return DType.Single;
case TypeCode.Double:
return DType.Double;
case TypeCode.Decimal:
return DType.Invalid;
case TypeCode.DateTime:
return DType.Invalid;
case TypeCode.String:
return DType.String;
default:
return DType.Invalid;
}
}
//FIXME: this method is bad, get rid of it
internal static DType TypeToDType (Type type)
{
if (type == typeof (void))
return DType.Invalid;
if (type == typeof (string))
return DType.String;
if (type == typeof (ObjectPath))
return DType.ObjectPath;
if (type == typeof (Signature))
return DType.Signature;
if (type == typeof (object))
return DType.Variant;
if (type.IsPrimitive)
return TypeCodeToDType (Type.GetTypeCode (type));
if (type.IsEnum)
return TypeToDType (Enum.GetUnderlyingType (type));
//needs work
if (type.IsArray)
return DType.Array;
//if (type.UnderlyingSystemType != null)
// return TypeToDType (type.UnderlyingSystemType);
if (Mapper.IsPublic (type))
return DType.ObjectPath;
if (!type.IsPrimitive && !type.IsEnum)
return DType.Struct;
//TODO: maybe throw an exception here
return DType.Invalid;
}
/*
public static DType TypeToDType (Type type)
{
if (type == null)
return DType.Invalid;
else if (type == typeof (byte))
return DType.Byte;
else if (type == typeof (bool))
return DType.Boolean;
else if (type == typeof (short))
return DType.Int16;
else if (type == typeof (ushort))
return DType.UInt16;
else if (type == typeof (int))
return DType.Int32;
else if (type == typeof (uint))
return DType.UInt32;
else if (type == typeof (long))
return DType.Int64;
else if (type == typeof (ulong))
return DType.UInt64;
else if (type == typeof (float)) //not supported by libdbus at time of writing
return DType.Single;
else if (type == typeof (double))
return DType.Double;
else if (type == typeof (string))
return DType.String;
else if (type == typeof (ObjectPath))
return DType.ObjectPath;
else if (type == typeof (Signature))
return DType.Signature;
else
return DType.Invalid;
}
*/
public Type ToType (ref int pos)
{
DType dtype = (DType)data[pos++];
switch (dtype) {
case DType.Invalid:
return typeof (void);
case DType.Byte:
return typeof (byte);
case DType.Boolean:
return typeof (bool);
case DType.Int16:
return typeof (short);
case DType.UInt16:
return typeof (ushort);
case DType.Int32:
return typeof (int);
case DType.UInt32:
return typeof (uint);
case DType.Int64:
return typeof (long);
case DType.UInt64:
return typeof (ulong);
case DType.Single: ////not supported by libdbus at time of writing
return typeof (float);
case DType.Double:
return typeof (double);
case DType.String:
return typeof (string);
case DType.ObjectPath:
return typeof (ObjectPath);
case DType.Signature:
return typeof (Signature);
case DType.Array:
//peek to see if this is in fact a dictionary
if ((DType)data[pos] == DType.DictEntryBegin) {
//skip over the {
pos++;
Type keyType = ToType (ref pos);
Type valueType = ToType (ref pos);
//skip over the }
pos++;
//return typeof (IDictionary<,>).MakeGenericType (new Type[] {keyType, valueType});
//workaround for Mono bug #81035 (memory leak)
return Mapper.GetGenericType (typeof (IDictionary<,>), new Type[] {keyType, valueType});
} else {
return ToType (ref pos).MakeArrayType ();
}
case DType.Struct:
return typeof (ValueType);
case DType.DictEntry:
return typeof (System.Collections.Generic.KeyValuePair<,>);
case DType.Variant:
return typeof (object);
default:
throw new NotSupportedException ("Parsing or converting this signature is not yet supported (signature was '" + this + "'), at DType." + dtype);
}
}
public static Signature GetSig (object[] objs)
{
return GetSig (Type.GetTypeArray (objs));
}
public static Signature GetSig (Type[] types)
{
if (types == null)
throw new ArgumentNullException ("types");
Signature sig = Signature.Empty;
foreach (Type type in types)
sig += GetSig (type);
return sig;
}
public static Signature GetSig (Type type)
{
if (type == null)
throw new ArgumentNullException ("type");
//this is inelegant, but works for now
if (type == typeof (Signature))
return new Signature (DType.Signature);
if (type == typeof (ObjectPath))
return new Signature (DType.ObjectPath);
if (type == typeof (void))
return Signature.Empty;
if (type == typeof (string))
return new Signature (DType.String);
if (type == typeof (object))
return new Signature (DType.Variant);
if (type.IsArray)
return GetSig (type.GetElementType ()).MakeArraySignature ();
if (type.IsGenericType && (type.GetGenericTypeDefinition () == typeof (IDictionary<,>) || type.GetGenericTypeDefinition () == typeof (Dictionary<,>))) {
Type[] genArgs = type.GetGenericArguments ();
return Signature.MakeDict (GetSig (genArgs[0]), GetSig (genArgs[1]));
}
if (Mapper.IsPublic (type)) {
return new Signature (DType.ObjectPath);
}
if (!type.IsPrimitive && !type.IsEnum) {
Signature sig = Signature.Empty;
foreach (FieldInfo fi in type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
sig += GetSig (fi.FieldType);
return Signature.MakeStruct (sig);
}
DType dtype = Signature.TypeToDType (type);
return new Signature (dtype);
}
}
enum ArgDirection
{
In,
Out,
}
enum DType : byte
{
Invalid = (byte)'\0',
Byte = (byte)'y',
Boolean = (byte)'b',
Int16 = (byte)'n',
UInt16 = (byte)'q',
Int32 = (byte)'i',
UInt32 = (byte)'u',
Int64 = (byte)'x',
UInt64 = (byte)'t',
Single = (byte)'f', //This is not yet supported!
Double = (byte)'d',
String = (byte)'s',
ObjectPath = (byte)'o',
Signature = (byte)'g',
Array = (byte)'a',
//TODO: remove Struct and DictEntry -- they are not relevant to wire protocol
Struct = (byte)'r',
DictEntry = (byte)'e',
Variant = (byte)'v',
StructBegin = (byte)'(',
StructEnd = (byte)')',
DictEntryBegin = (byte)'{',
DictEntryEnd = (byte)'}',
}
}