diff options
| author | Robert Greig <rgreig@apache.org> | 2007-02-13 16:56:03 +0000 |
|---|---|---|
| committer | Robert Greig <rgreig@apache.org> | 2007-02-13 16:56:03 +0000 |
| commit | f1e37d08908f9dc1aa2728c8379091846bccbf13 (patch) | |
| tree | c0a6fdd4b35416c552a8b5bc7847c1a85b3b6303 /dotnet/Qpid.Common | |
| parent | cd7311844c3058bcc7a0a73e26d31570da63bed7 (diff) | |
| download | qpid-python-f1e37d08908f9dc1aa2728c8379091846bccbf13.tar.gz | |
(Path submitted by Tomas Restrepo) Qpid-336 Field table updated to match Java client.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@507096 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'dotnet/Qpid.Common')
| -rw-r--r-- | dotnet/Qpid.Common/Framing/AMQType.cs | 700 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Framing/AMQTypeMap.cs | 75 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Framing/AMQTypedValue.cs | 76 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Framing/EncodingUtils.cs | 607 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Framing/FieldTable.cs | 729 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Qpid.Common.csproj | 3 | ||||
| -rw-r--r-- | dotnet/Qpid.Common/Qpid.Common.mdp | 5 |
7 files changed, 1790 insertions, 405 deletions
diff --git a/dotnet/Qpid.Common/Framing/AMQType.cs b/dotnet/Qpid.Common/Framing/AMQType.cs new file mode 100644 index 0000000000..3bcc6c6222 --- /dev/null +++ b/dotnet/Qpid.Common/Framing/AMQType.cs @@ -0,0 +1,700 @@ +/*
+ *
+ * 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.Text;
+using Qpid.Buffer;
+
+namespace Qpid.Framing
+{
+ /// <summary>
+ /// Base class for the Field Table Type system.
+ /// Ported over from the Java AMQType enumeration
+ /// </summary>
+ public abstract class AMQType
+ {
+ private byte _identifier;
+
+ /// <summary>
+ /// Type code identifier for this type
+ /// </summary>
+ public byte Identifier
+ {
+ get { return _identifier; }
+ }
+
+ protected AMQType(char identifier)
+ {
+ _identifier = (byte)identifier;
+ }
+
+ /// <summary>
+ /// Create a new <see cref="AMQTypedValue"/> instance
+ /// </summary>
+ /// <param name="value">Value to initialize with</param>
+ /// <returns>A new typed value instance</returns>
+ public AMQTypedValue AsTypedValue(object value)
+ {
+ return new AMQTypedValue(this, ToNativeValue(value));
+ }
+
+ /// <summary>
+ /// Write the specified value to the buffer using the encoding
+ /// specified for this type
+ /// </summary>
+ /// <param name="value">Value to write</param>
+ /// <param name="buffer">Buffer to write to</param>
+ public void WriteToBuffer(object value, ByteBuffer buffer)
+ {
+ buffer.put(Identifier);
+ WriteValueImpl(value, buffer);
+ }
+
+ public override string ToString()
+ {
+ return ((Char) Identifier).ToString();
+ }
+
+ /// <summary>
+ /// Get the encoding size for the specified value in this type format
+ /// </summary>
+ /// <param name="value">Value to find encoded size for</param>
+ /// <returns>The encoded size</returns>
+ public abstract uint GetEncodingSize(object value);
+ /// <summary>
+ /// Convert the specified value to this type
+ /// </summary>
+ /// <param name="value">Value to convert</param>
+ /// <returns>The converted value</returns>
+ public abstract object ToNativeValue(object value);
+
+ /// <summary>
+ /// Read a value from the specified buffer using the encoding for
+ /// this type
+ /// </summary>
+ /// <param name="buffer">Buffer to read from</param>
+ /// <returns>The value read</returns>
+ public abstract object ReadValueFromBuffer(ByteBuffer buffer);
+
+ protected abstract void WriteValueImpl(Object value, ByteBuffer buffer);
+
+
+ #region Known Types
+ //
+ // Known Types
+ //
+
+ // long string is not defined in the proposed specification,
+ // and the 'S' discriminator is left for unsigned short (16-bit) values
+ public static readonly AMQType LONG_STRING = new AMQLongStringType();
+ public static readonly AMQType UINT32 = new AMQUInt32Type();
+ public static readonly AMQType DECIMAL = new AMQDecimalType();
+ public static readonly AMQType TIMESTAMP = new AMQTimeStampType();
+ public static readonly AMQType FIELD_TABLE = new AMQFieldTableType();
+ public static readonly AMQType VOID = new AMQVoidType();
+ public static readonly AMQType BINARY = new AMQBinaryType();
+ public static readonly AMQType ASCII_STRING = new AMQAsciiStringType();
+ public static readonly AMQType WIDE_STRING = new AMQWideStringType();
+ public static readonly AMQType BOOLEAN = new AMQBooleanType();
+ public static readonly AMQType ASCII_CHARACTER = new AMQAsciiCharType();
+ public static readonly AMQType BYTE = new AMQByteType();
+ public static readonly AMQType SBYTE = new AMQSByteType();
+ public static readonly AMQType INT16 = new AMQInt16Type();
+ public static readonly AMQType UINT16 = new AMQUInt16Type();
+ public static readonly AMQType INT32 = new AMQInt32Type();
+ public static readonly AMQType INT64 = new AMQInt64Type();
+ public static readonly AMQType UINT64 = new AMQUInt64Type();
+ public static readonly AMQType FLOAT = new AMQFloatType();
+ public static readonly AMQType DOUBLE = new AMQDoubleType();
+
+ #endregion // Known Types
+
+ #region Type Implementation
+ //
+ // Type Implementation
+ //
+
+ sealed class AMQLongStringType : AMQType
+ {
+ public AMQLongStringType() : base('S')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedLongStringLength((string) value);
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ if ( value == null )
+ throw new ArgumentNullException("value");
+ return value.ToString();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadLongString(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteLongStringBytes(buffer, (string) value);
+ }
+
+ }
+
+ sealed class AMQUInt32Type : AMQType
+ {
+ public AMQUInt32Type() : base('I')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.UnsignedIntegerLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToUInt32(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadUnsignedInteger(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteUnsignedInteger(buffer, (uint) value);
+ }
+
+ }
+
+ sealed class AMQDecimalType : AMQType
+ {
+ public AMQDecimalType() : base('D')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ sealed class AMQTimeStampType : AMQType
+ {
+ public AMQTimeStampType() : base('T')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ sealed class AMQFieldTableType : AMQType
+ {
+ public AMQFieldTableType() : base('F')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ sealed class AMQVoidType : AMQType
+ {
+ public AMQVoidType() : base('V')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return 0;
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ if ( value != null )
+ throw new FormatException(string.Format("Cannot convert {0} to VOID type", value));
+ return null;
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return null;
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ }
+ }
+
+ // Extended Types
+
+ sealed class AMQBinaryType : AMQType
+ {
+ public AMQBinaryType() : base('x')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedLongstrLength((byte[]) value);
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ if ( value is byte[] || value == null )
+ {
+ return value;
+ }
+ throw new ArgumentException("Value cannot be converted to byte[]");
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadLongstr(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteLongstr(buffer, (byte[])value);
+ }
+ }
+
+ sealed class AMQAsciiStringType : AMQType
+ {
+ public AMQAsciiStringType() : base('c')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedAsciiStringLength((string)value);
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ if ( value == null )
+ throw new ArgumentNullException("value");
+ return value.ToString();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadAsciiString(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteAsciiString(buffer, (string)value);
+ }
+ }
+
+ sealed class AMQWideStringType : AMQType
+ {
+ // todo: Change encoding to UTF16 (java code still uses default
+ // ascii encoding for wide strings
+ private static readonly Encoding ENCODING = Encoding.ASCII;
+
+ public AMQWideStringType()
+ : base('C')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedLongStringLength((string)value, ENCODING);
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ if ( value == null )
+ throw new ArgumentNullException("value");
+ return value.ToString();
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadLongString(buffer, ENCODING);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteLongStringBytes(buffer, (string)value, ENCODING);
+ }
+ }
+
+ sealed class AMQBooleanType : AMQType
+ {
+ public AMQBooleanType() : base('t')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedBooleanLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToBoolean(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadBoolean(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteBoolean(buffer, (bool)value);
+ }
+ }
+
+ sealed class AMQAsciiCharType : AMQType
+ {
+ public AMQAsciiCharType() : base('k')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedCharLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToChar(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadChar(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteChar(buffer, (char)value);
+ }
+ }
+
+ sealed class AMQByteType : AMQType
+ {
+ public AMQByteType() : base('B')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedByteLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToByte(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadByte(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteByte(buffer, (byte)value);
+ }
+ }
+
+ sealed class AMQSByteType : AMQType
+ {
+ public AMQSByteType()
+ : base('b')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedSByteLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToSByte(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadSByte(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteSByte(buffer, (sbyte)value);
+ }
+ }
+
+ sealed class AMQInt16Type : AMQType
+ {
+ public AMQInt16Type() : base('s')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedShortLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToInt16(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadShort(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteShort(buffer, (short)value);
+ }
+ }
+
+ sealed class AMQUInt16Type : AMQType
+ {
+ public AMQUInt16Type()
+ : base('S')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedUnsignedShortLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToUInt16(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadUnsignedShort(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteUnsignedShort(buffer, (ushort)value);
+ }
+ }
+
+ sealed class AMQInt32Type : AMQType
+ {
+ public AMQInt32Type() : base('i')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedIntegerLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToInt32(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadInteger(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteInteger(buffer, (int)value);
+ }
+ }
+
+ sealed class AMQInt64Type : AMQType
+ {
+ public AMQInt64Type() : base('l')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedLongLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToInt64(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadLong(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteLong(buffer, (long)value);
+ }
+ }
+
+ sealed class AMQUInt64Type : AMQType
+ {
+ public AMQUInt64Type()
+ : base('L')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedUnsignedLongLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToUInt64(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadUnsignedLong(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteUnsignedLong(buffer, (ulong)value);
+ }
+ }
+
+ sealed class AMQFloatType : AMQType
+ {
+ public AMQFloatType() : base('f')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedFloatLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToSingle(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadFloat(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteFloat(buffer, (float)value);
+ }
+ }
+
+ sealed class AMQDoubleType : AMQType
+ {
+ public AMQDoubleType() : base('d')
+ {
+ }
+
+ public override uint GetEncodingSize(object value)
+ {
+ return EncodingUtils.EncodedDoubleLength();
+ }
+
+ public override object ToNativeValue(object value)
+ {
+ return Convert.ToDouble(value);
+ }
+
+ public override object ReadValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.ReadDouble(buffer);
+ }
+
+ protected override void WriteValueImpl(object value, ByteBuffer buffer)
+ {
+ EncodingUtils.WriteDouble(buffer, (double)value);
+ }
+ }
+
+ #endregion // Type Implementation
+
+ } // class AMQType
+}
diff --git a/dotnet/Qpid.Common/Framing/AMQTypeMap.cs b/dotnet/Qpid.Common/Framing/AMQTypeMap.cs new file mode 100644 index 0000000000..56d0119a16 --- /dev/null +++ b/dotnet/Qpid.Common/Framing/AMQTypeMap.cs @@ -0,0 +1,75 @@ +/*
+ *
+ * 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.Collections;
+
+namespace Qpid.Framing
+{
+ public sealed class AMQTypeMap
+ {
+ private static Hashtable _reverseTypeMap;
+
+ private AMQTypeMap()
+ {
+ }
+
+ static AMQTypeMap()
+ {
+ _reverseTypeMap = Hashtable.Synchronized(new Hashtable());
+
+ Add(AMQType.LONG_STRING);
+ Add(AMQType.BOOLEAN);
+ Add(AMQType.BYTE);
+ Add(AMQType.SBYTE);
+ Add(AMQType.INT16);
+ // not supported for now as type code conflicts
+ // with LONG_STRING
+ //Add(AMQType.UINT16);
+ Add(AMQType.INT32);
+ Add(AMQType.UINT32);
+ Add(AMQType.INT64);
+ Add(AMQType.UINT64);
+ Add(AMQType.FLOAT);
+ Add(AMQType.DOUBLE);
+ Add(AMQType.DECIMAL);
+ Add(AMQType.BINARY);
+ Add(AMQType.ASCII_STRING);
+ Add(AMQType.WIDE_STRING);
+ Add(AMQType.ASCII_CHARACTER);
+ Add(AMQType.TIMESTAMP);
+ Add(AMQType.FIELD_TABLE);
+ Add(AMQType.VOID);
+ }
+
+ public static AMQType GetType(byte identifier)
+ {
+ AMQType type = (AMQType)_reverseTypeMap[identifier];
+ if ( type == null )
+ throw new ArgumentOutOfRangeException(string.Format("No such type code: {0:x}", identifier));
+ return type;
+ }
+
+ private static void Add(AMQType type)
+ {
+ _reverseTypeMap.Add(type.Identifier, type);
+ }
+ }
+}
diff --git a/dotnet/Qpid.Common/Framing/AMQTypedValue.cs b/dotnet/Qpid.Common/Framing/AMQTypedValue.cs new file mode 100644 index 0000000000..08b5c1e6e9 --- /dev/null +++ b/dotnet/Qpid.Common/Framing/AMQTypedValue.cs @@ -0,0 +1,76 @@ +/*
+ *
+ * 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 Qpid.Buffer;
+
+namespace Qpid.Framing
+{
+ public class AMQTypedValue
+ {
+ private readonly AMQType _type;
+ private readonly object _value;
+
+ public AMQType Type
+ {
+ get { return _type; }
+ }
+
+ public object Value
+ {
+ get { return _value; }
+ }
+
+ public uint EncodingLength
+ {
+ get { return _type.GetEncodingSize(_value); }
+ }
+
+ public AMQTypedValue(AMQType type, object value)
+ {
+ if ( type == null )
+ throw new ArgumentNullException("type");
+ _type = type;
+ _value = type.ToNativeValue(value);
+ }
+
+ public AMQTypedValue(AMQType type, ByteBuffer buffer)
+ {
+ _type = type;
+ _value = type.ReadValueFromBuffer(buffer);
+ }
+
+ public void WriteToBuffer(ByteBuffer buffer)
+ {
+ _type.WriteToBuffer(_value, buffer);
+ }
+
+ public static AMQTypedValue ReadFromBuffer(ByteBuffer buffer)
+ {
+ AMQType type = AMQTypeMap.GetType(buffer.get());
+ return new AMQTypedValue(type, buffer);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}: {1}", Type, Value);
+ }
+ }
+}
diff --git a/dotnet/Qpid.Common/Framing/EncodingUtils.cs b/dotnet/Qpid.Common/Framing/EncodingUtils.cs index bb51f14b18..564c9d87ef 100644 --- a/dotnet/Qpid.Common/Framing/EncodingUtils.cs +++ b/dotnet/Qpid.Common/Framing/EncodingUtils.cs @@ -24,235 +24,428 @@ using Qpid.Buffer; namespace Qpid.Framing { - public class EncodingUtils - { - private static readonly Encoding DEFAULT_ENCODER = Encoding.ASCII; - - public static ushort EncodedShortStringLength(string s) - { - if (s == null) - { - return 1; - } - else - { - return (ushort)(1 + s.Length); - } - } + public class EncodingUtils + { + private static readonly Encoding DEFAULT_ENCODER = Encoding.ASCII; - public static uint EncodedLongStringLength(string s) - { - if (s == null) + // SHORT STRING + public static ushort EncodedShortStringLength(string s) + { + if ( s == null ) + { + return 1; + } else + { + return (ushort)(1 + s.Length); + } + } + public static void WriteShortStringBytes(ByteBuffer buffer, string s) + { + if ( s != null ) + { + //try + //{ + //final byte[] encodedString = s.getBytes(STRING_ENCODING); + byte[] encodedString; + lock ( DEFAULT_ENCODER ) { - return 4; + encodedString = DEFAULT_ENCODER.GetBytes(s); } - else - { - return (uint)(4 + s.Length); - } - } + // TODO: check length fits in an unsigned byte + buffer.put((byte)encodedString.Length); + buffer.put(encodedString); - public static int EncodedLongstrLength(byte[] bytes) - { - if (bytes == null) - { - return 4; - } - else - { - return 4 + bytes.Length; - } - } + } else + { + // really writing out unsigned byte + buffer.put((byte)0); + } + } - public static uint EncodedFieldTableLength(FieldTable table) - { - if (table == null) - { - // size is encoded as 4 octets - return 4; - } - else - { - // size of the table plus 4 octets for the size - return table.EncodedSize + 4; - } - } + // ASCII STRINGS + public static uint EncodedAsciiStringLength(string s) + { + // TODO: move this to 2-byte length once the proposed encodings + // have been approved. Also, validate length! + if ( s == null ) + return 4; + else + return (uint) (4 + s.Length); + } + public static string ReadAsciiString(ByteBuffer buffer) + { + return ReadLongString(buffer, DEFAULT_ENCODER); + } + public static void WriteAsciiString(ByteBuffer buffer, string s) + { + WriteLongStringBytes(buffer, s, DEFAULT_ENCODER); + } - public static void WriteShortStringBytes(ByteBuffer buffer, string s) - { - if (s != null) - { - //try - //{ - //final byte[] encodedString = s.getBytes(STRING_ENCODING); - byte[] encodedString; - lock (DEFAULT_ENCODER) - { - encodedString = DEFAULT_ENCODER.GetBytes(s); - } - // TODO: check length fits in an unsigned byte - buffer.put((byte) encodedString.Length); - buffer.put(encodedString); - - } - else - { - // really writing out unsigned byte - buffer.put((byte) 0); - } - } + // LONG STRING + public static uint EncodedLongStringLength(string s) + { + return EncodedLongStringLength(s, DEFAULT_ENCODER); + } - public static void WriteLongStringBytes(ByteBuffer buffer, string s) - { - if (!(s == null || s.Length <= 0xFFFE)) - { - throw new ArgumentException("String too long"); - } - if (s != null) + public static uint EncodedLongStringLength(string s, Encoding encoding) + { + if ( s == null ) + { + return 4; + } else + { + return (uint)(4 + encoding.GetByteCount(s)); + } + } + public static string ReadLongString(ByteBuffer buffer) + { + return ReadLongString(buffer, DEFAULT_ENCODER); + } + public static string ReadLongString(ByteBuffer buffer, Encoding encoding) + { + uint length = buffer.getUnsignedInt(); + if ( length == 0 ) + { + return null; + } else + { + byte[] data = new byte[length]; + buffer.get(data); + lock ( encoding ) { - buffer.put((uint)s.Length); - byte[] encodedString = null; - lock (DEFAULT_ENCODER) - { - encodedString = DEFAULT_ENCODER.GetBytes(s); - } - buffer.put(encodedString); - } - else - { - buffer.put((uint) 0); + return encoding.GetString(data); } - } + } + } + public static void WriteLongStringBytes(ByteBuffer buffer, string s) + { + WriteLongStringBytes(buffer, s, DEFAULT_ENCODER); + } - public static void WriteFieldTableBytes(ByteBuffer buffer, FieldTable table) - { - if (table != null) - { - table.WriteToBuffer(buffer); - } - else + public static void WriteLongStringBytes(ByteBuffer buffer, string s, Encoding encoding) + { + if ( !(s == null || s.Length <= 0xFFFE) ) + { + throw new ArgumentException("String too long"); + } + if ( s != null ) + { + lock ( encoding ) { - buffer.put((uint) 0); + byte[] encodedString = null; + encodedString = encoding.GetBytes(s); + buffer.put((uint)encodedString.Length); + buffer.put(encodedString); } - } + } else + { + buffer.put((uint)0); + } + } - public static void WriteBooleans(ByteBuffer buffer, bool[] values) - { - byte packedValue = 0; - for (int i = 0; i < values.Length; i++) - { - if (values[i]) - { - packedValue = (byte) (packedValue | (1 << i)); - } - } + // BINARY + public static uint EncodedLongstrLength(byte[] bytes) + { + if ( bytes == null ) + { + return 4; + } else + { + return (uint)(4 + bytes.Length); + } + } + public static byte[] ReadLongstr(ByteBuffer buffer) + { + uint length = buffer.getUnsignedInt(); + if ( length == 0 ) + { + return null; + } else + { + byte[] result = new byte[length]; + buffer.get(result); + return result; + } + } + public static void WriteLongstr(ByteBuffer buffer, byte[] data) + { + if ( data != null ) + { + buffer.put((uint)data.Length); + buffer.put(data); + } else + { + buffer.put((uint)0); + } + } - buffer.put(packedValue); - } + // BOOLEANS + public static bool[] ReadBooleans(ByteBuffer buffer) + { + byte packedValue = buffer.get(); + bool[] result = new bool[8]; - public static void WriteLongstr(ByteBuffer buffer, byte[] data) - { - if (data != null) - { - buffer.put((uint) data.Length); - buffer.put(data); - } - else + for ( int i = 0; i < 8; i++ ) + { + result[i] = ((packedValue & (1 << i)) != 0); + } + return result; + } + public static void WriteBooleans(ByteBuffer buffer, bool[] values) + { + byte packedValue = 0; + for ( int i = 0; i < values.Length; i++ ) + { + if ( values[i] ) { - buffer.put((uint) 0); + packedValue = (byte)(packedValue | (1 << i)); } - } + } - public static bool[] ReadBooleans(ByteBuffer buffer) - { - byte packedValue = buffer.get(); - bool[] result = new bool[8]; + buffer.put(packedValue); + } - for (int i = 0; i < 8; i++) - { - result[i] = ((packedValue & (1 << i)) != 0); - } - return result; - } - - /// <summary> - /// Reads the field table uaing the data in the specified buffer - /// </summary> - /// <param name="buffer">The buffer to read from.</param> - /// <returns>a populated field table</returns> - /// <exception cref="AMQFrameDecodingException">if the buffer does not contain a decodable field table</exception> - public static FieldTable ReadFieldTable(ByteBuffer buffer) - { - uint length = buffer.GetUnsignedInt(); - if (length == 0) - { - return null; - } - else - { - return new FieldTable(buffer, length); - } - } - - /// <summary> - /// Read a short string from the buffer - /// </summary> - /// <param name="buffer">The buffer to read from.</param> - /// <returns>a string</returns> - /// <exception cref="AMQFrameDecodingException">if the buffer does not contain a decodable short string</exception> - public static string ReadShortString(ByteBuffer buffer) - { - byte length = buffer.get(); - if (length == 0) - { - return null; - } - else - { - byte[] data = new byte[length]; - buffer.get(data); - - lock (DEFAULT_ENCODER) - { - return DEFAULT_ENCODER.GetString(data); -// return buffer.GetString(length, DEFAULT_ENCODER); - } - } - } + // FIELD TABLES + public static uint EncodedFieldTableLength(FieldTable table) + { + if ( table == null ) + { + // size is encoded as 4 octets + return 4; + } else + { + // size of the table plus 4 octets for the size + return table.EncodedSize + 4; + } + } + /// <summary> + /// Reads the field table using the data in the specified buffer + /// </summary> + /// <param name="buffer">The buffer to read from.</param> + /// <returns>a populated field table</returns> + /// <exception cref="AMQFrameDecodingException">if the buffer does not contain a decodable field table</exception> + public static FieldTable ReadFieldTable(ByteBuffer buffer) + { + uint length = buffer.GetUnsignedInt(); + if ( length == 0 ) + { + return null; + } else + { + return new FieldTable(buffer, length); + } + } + public static void WriteFieldTableBytes(ByteBuffer buffer, FieldTable table) + { + if ( table != null ) + { + table.WriteToBuffer(buffer); + } else + { + buffer.put((uint)0); + } + } - public static string ReadLongString(ByteBuffer buffer) - { - uint length = buffer.getUnsignedInt(); - if (length == 0) - { - return null; - } - else - { - byte[] data = new byte[length]; - buffer.get(data); - lock (DEFAULT_ENCODER) - { - return DEFAULT_ENCODER.GetString(data); - //return buffer.GetString(length, DEFAULT_ENCODER); - } - } - } - public static byte[] ReadLongstr(ByteBuffer buffer) - { - uint length = buffer.getUnsignedInt(); - if (length == 0) - { - return null; - } - else + /// <summary> + /// Read a short string from the buffer + /// </summary> + /// <param name="buffer">The buffer to read from.</param> + /// <returns>a string</returns> + /// <exception cref="AMQFrameDecodingException">if the buffer does not contain a decodable short string</exception> + public static string ReadShortString(ByteBuffer buffer) + { + byte length = buffer.get(); + if ( length == 0 ) + { + return null; + } else + { + byte[] data = new byte[length]; + buffer.get(data); + + lock ( DEFAULT_ENCODER ) { - byte[] result = new byte[length]; - buffer.get(result); - return result; + return DEFAULT_ENCODER.GetString(data); } - } - } + } + } + + + + // BOOLEAN + public static uint EncodedBooleanLength() + { + return 1; + } + public static bool ReadBoolean(ByteBuffer buffer) + { + byte packedValue = buffer.get(); + return (packedValue == 1); + } + public static void WriteBoolean(ByteBuffer buffer, bool value) + { + buffer.put((byte)(value ? 1 : 0)); + } + + + // CHAR + public static uint EncodedCharLength() + { + return EncodedByteLength(); + } + public static char ReadChar(ByteBuffer buffer) + { + return (char)buffer.get(); + } + public static void WriteChar(ByteBuffer buffer, char value) + { + buffer.put((byte)value); + } + + // BYTE + public static uint EncodedByteLength() + { + return 1; + } + public static byte ReadByte(ByteBuffer buffer) + { + return buffer.get(); + } + public static void WriteByte(ByteBuffer buffer, byte value) + { + buffer.put(value); + } + + // SBYTE + public static uint EncodedSByteLength() + { + return 1; + } + public static sbyte ReadSByte(ByteBuffer buffer) + { + return (sbyte)buffer.get(); + } + public static void WriteSByte(ByteBuffer buffer, sbyte value) + { + buffer.put((byte)value); + } + + // INT16 + public static uint EncodedShortLength() + { + return 2; + } + + public static short ReadShort(ByteBuffer buffer) + { + return buffer.getShort(); + } + public static void WriteShort(ByteBuffer buffer, short value) + { + buffer.putShort(value); + } + + // UINT16 + public static uint EncodedUnsignedShortLength() + { + return 2; + } + + public static ushort ReadUnsignedShort(ByteBuffer buffer) + { + return buffer.GetUnsignedShort(); + } + public static void WriteUnsignedShort(ByteBuffer buffer, ushort value) + { + buffer.put(value); + } + + + // INT32 + public static uint EncodedIntegerLength() + { + return 4; + } + public static int ReadInteger(ByteBuffer buffer) + { + return buffer.getInt(); + } + public static void WriteInteger(ByteBuffer buffer, int value) + { + buffer.putInt(value); + } + + // UINT32 + public static uint UnsignedIntegerLength() + { + return 4; + } + public static void WriteUnsignedInteger(ByteBuffer buffer, uint value) + { + buffer.put(value); + } + public static uint ReadUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + + // INT64 + public static uint EncodedUnsignedLongLength() + { + return 8; + } + public static ulong ReadUnsignedLong(ByteBuffer buffer) + { + return buffer.GetUnsignedLong(); + } + public static void WriteUnsignedLong(ByteBuffer buffer, ulong value) + { + buffer.put(value); + } + + // UINT64 + public static uint EncodedLongLength() + { + return 8; + } + public static long ReadLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + public static void WriteLong(ByteBuffer buffer, long value) + { + buffer.putLong(value); + } + + // FLOAT + public static uint EncodedFloatLength() + { + return 4; + } + public static void WriteFloat(ByteBuffer buffer, float value) + { + buffer.putFloat(value); + } + public static float ReadFloat(ByteBuffer buffer) + { + return buffer.getFloat(); + } + + // DOUBLE + public static uint EncodedDoubleLength() + { + return 8; + } + public static void WriteDouble(ByteBuffer buffer, double value) + { + buffer.putDouble(value); + } + public static double ReadDouble(ByteBuffer buffer) + { + return buffer.getDouble(); + } + + } } diff --git a/dotnet/Qpid.Common/Framing/FieldTable.cs b/dotnet/Qpid.Common/Framing/FieldTable.cs index 193d96f6cd..b977bef0ba 100644 --- a/dotnet/Qpid.Common/Framing/FieldTable.cs +++ b/dotnet/Qpid.Common/Framing/FieldTable.cs @@ -21,153 +21,274 @@ using System; using System.Collections; using System.Text; +using log4net; using Qpid.Buffer; using Qpid.Collections; using Qpid.Messaging; namespace Qpid.Framing { - /// - /// From the protocol document: - /// field-table = short-integer *field-value-pair - /// field-value-pair = field-name field-value - /// field-name = short-string - /// field-value = 'S' long-string - /// 'I' long-integer - /// 'D' decimal-value - /// 'T' long-integer - /// decimal-value = decimals long-integer - /// decimals = OCTET - public class FieldTable : IFieldTable + public class FieldTable : IFieldTable, IEnumerable { - IDictionary _hash = new LinkedHashtable(); - - private uint _encodedSize = 0; + private static readonly ILog _log = LogManager.GetLogger(typeof(FieldTable)); + + IDictionary _properties; + private ByteBuffer _encodedForm; + private object _syncLock; + private uint _encodedSize; public FieldTable() { + _syncLock = new object(); } - /** - * Construct a new field table. - * @param buffer the buffer from which to read data. The length byte must be read already - * @param length the length of the field table. Must be > 0. - * @throws AMQFrameDecodingException if there is an error decoding the table - */ - public FieldTable(ByteBuffer buffer, uint length) - { - _encodedSize = length; - int sizeRead = 0; - while (sizeRead < _encodedSize) - { - int sizeRemaining = buffer.remaining(); - string key = EncodingUtils.ReadShortString(buffer); - // TODO: use proper charset decoder - char type = (char)buffer.get(); - object value; - switch (type) - { - case 'S': - value = EncodingUtils.ReadLongString(buffer); - break; - case 'I': - value = buffer.GetUnsignedInt(); - break; - default: - throw new AMQFrameDecodingException("Unsupported field table type: '" + type + "' charcode" + (int)type); - } - sizeRead += (sizeRemaining - buffer.remaining()); - - _hash.Add(key, value); - } + /// <summary> + /// Construct a new field table. + /// </summary> + /// <param name="buffer">the buffer from which to read data. The length byte must be read already</param> + /// <param name="length">the length of the field table. Must be > 0.</param> + /// <exception cref="AMQFrameDecodingException">if there is an error decoding the table</exception> + public FieldTable(ByteBuffer buffer, uint length) : this() + { + _encodedForm = buffer.slice(); + _encodedForm.limit((int)length); + _encodedSize = length; + buffer.skip((int)length); } + /// <summary> + /// The set of all property names + /// </summary> + public ICollection Keys + { + get + { + InitMapIfNecessary(); + return _properties.Keys; + } + } + + /// <summary> + /// Calculated size of this field table once encoded + /// </summary> public uint EncodedSize { - get - { - return _encodedSize; - } + get { return _encodedSize; } } + /// <summary> + /// Number of properties in the field table + /// </summary> public int Count { - get { return _hash.Count; } + get + { + InitMapIfNecessary(); + return _properties.Count; + } } + /// <summary> + /// Gets or sets the specified property. + /// </summary> + /// <param name="key">Property name</param> + /// <returns>The specified property value</returns> public object this[string key] - { - get - { - CheckKey(key); - return _hash[key]; - } + { + get { return GetObject(key); } + set { SetObject(key, value); } + } - set - { - CheckKey(key); - CheckValue(value); + #region Typed Setters and Getters + // + // Typed Setters and Getters + // + public bool GetBoolean(string key) + { + return (bool)this[key]; + } + public void SetBoolean(string key, bool value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.BOOLEAN.AsTypedValue(value)); + } + public byte GetByte(string key) + { + return (byte)this[key]; + } + public void SetByte(string key, byte value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.BYTE.AsTypedValue(value)); + } + public sbyte GetSByte(string key) + { + return (sbyte)this[key]; + } + public void SetSByte(string key, sbyte value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.SBYTE.AsTypedValue(value)); + } + public short GetInt16(string key) + { + return (short)this[key]; + } + public void SetInt16(string key, short value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.INT16.AsTypedValue(value)); + } + public int GetInt32(string key) + { + return (int)this[key]; + } + public void SetInt32(string key, int value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.INT32.AsTypedValue(value)); + } + public long GetInt64(string key) + { + return (long)this[key]; + } + public void SetInt64(string key, long value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.INT64.AsTypedValue(value)); + } + public char GetChar(string key) + { + return (char)this[key]; + } + public void SetChar(string key, char value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.ASCII_CHARACTER.AsTypedValue(value)); + } + public float GetFloat(string key) + { + return (float)this[key]; + } + public void SetFloat(string key, float value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.FLOAT.AsTypedValue(value)); + } + public double GetDouble(string key) + { + return (double)this[key]; + } + public void SetDouble(string key, double value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.DOUBLE.AsTypedValue(value)); + } + public decimal GetDecimal(string key) + { + return (decimal)this[key]; + } + public void SetDecimal(string key, decimal value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.DECIMAL.AsTypedValue(value)); + } + public string GetString(string key) + { + return (string)this[key]; + } + public void SetString(string key, string value) + { + CheckPropertyName(key); + if ( value == null ) + SetProperty(key, AMQType.VOID.AsTypedValue(null)); + else + SetProperty(key, AMQType.LONG_STRING.AsTypedValue(value)); + } + public byte[] GetBytes(string key) + { + return (byte[])this[key]; + } + public void SetBytes(string key, byte[] value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.BINARY.AsTypedValue(value)); + } + public ushort GetUInt16(string key) + { + return (ushort)this[key]; + } + public void SetUInt16(string key, ushort value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.UINT16.AsTypedValue(value)); + } + public uint GetUInt32(string key) + { + return (uint)this[key]; + } + public void SetUInt32(string key, uint value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.UINT32.AsTypedValue(value)); + } + public ulong GetUInt64(string key) + { + return (ulong)this[key]; + } + public void SetUInt64(string key, ulong value) + { + CheckPropertyName(key); + SetProperty(key, AMQType.UINT64.AsTypedValue(value)); + } + #endregion // Typed Setters and Getters - object oldValue = _hash[key]; - if (oldValue != null) - { - AdjustEncodingSizeWhenRemoving(key, oldValue); - } + #region Public Methods + // + // Public Methods + // - _hash[key] = value; - AdjustEncodingSizeWhenAdding(key, value); - } - } - - public void WriteToBuffer(ByteBuffer buffer) + /// <summary> + /// Removes the property with the specified name + /// </summary> + /// <param name="key">The name of the property to remove</param> + /// <returns>The previous value of the property or null</returns> + public AMQTypedValue RemoveKey(string key) { - // Write out the total length, which we have kept up to date as data is added. - buffer.put(_encodedSize); - WritePayload(buffer); + InitMapIfNecessary(); + _encodedForm = null; + AMQTypedValue value = (AMQTypedValue)_properties[key]; + if ( value != null ) + { + _properties.Remove(key); + _encodedSize -= EncodingUtils.EncodedShortStringLength(key); + _encodedSize--; + _encodedSize -= value.EncodingLength; + + } + return value; } + - private void WritePayload(ByteBuffer buffer) + /// <summary> + /// Remove the property with the specified name + /// </summary> + /// <param name="key">The name of the property to remove</param> + public void Remove(string key) { - foreach (DictionaryEntry lde in _hash) - { - string key = (string) lde.Key; - EncodingUtils.WriteShortStringBytes(buffer, key); - object value = lde.Value; - if (value is byte[]) - { - buffer.put((byte) 'S'); - EncodingUtils.WriteLongstr(buffer, (byte[]) value); - } - else if (value is string) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'S'); - EncodingUtils.WriteLongStringBytes(buffer, (string) value); - } - else if (value is uint) - { - // TODO: look at using proper charset encoder - buffer.put((byte) 'I'); - buffer.put((uint) value); - } - else - { - // Should never get here. - throw new InvalidOperationException("Unsupported type in FieldTable: " + value.GetType()); - } - } + RemoveKey(key); } - public byte[] GetDataAsBytes() + /// <summary> + /// Remove all properties from the table + /// </summary> + public void Clear() { - ByteBuffer buffer = ByteBuffer.allocate((int)_encodedSize); - WritePayload(buffer); - byte[] result = new byte[_encodedSize]; - buffer.flip(); - buffer.get(result); - //buffer.Release(); - return result; + InitMapIfNecessary(); + _encodedForm = null; + _properties.Clear(); + _encodedSize = 0; } /// <summary> @@ -177,124 +298,338 @@ namespace Qpid.Framing /// <param name="ft">the source field table</param> public void AddAll(IFieldTable ft) { - foreach (DictionaryEntry dictionaryEntry in ft) - { - this[(string)dictionaryEntry.Key] = dictionaryEntry.Value; - } + foreach ( DictionaryEntry dictionaryEntry in ft ) + { + this[(string)dictionaryEntry.Key] = dictionaryEntry.Value; + } } - private void CheckKey(object key) + /// <summary> + /// Get a enumerator over the internal property set. + /// Notice the enumerator will DictionaryEntry objects with + /// a string as the Key and an <see cref="AMQTypedValue"/> instance as the value + /// </summary> + /// <returns>The enumerator object</returns> + public IEnumerator GetEnumerator() { - if (key == null) - { - throw new ArgumentException("All keys must be Strings - was passed: null"); - } - else if (!(key is string)) - { - throw new ArgumentException("All keys must be Strings - was passed: " + key.GetType()); - } + return _properties.GetEnumerator(); } - private void CheckValue(object value) + /// <summary> + /// Indicates if a property with the given name exists + /// </summary> + /// <param name="s">Property name to check</param> + /// <returns>True if the property exists</returns> + public bool Contains(string s) { - if (!(value is string || value is uint || value is int || value is long)) - { - throw new ArgumentException("All values must be type string or int or long or uint, was passed: " + - value.GetType()); - } + return _properties.Contains(s); + } + + /// <summary> + /// Returns a dictionary mapping Property Names to the corresponding + /// <see cref="AMQTypedValue"/> value + /// </summary> + /// <returns>The internal dictionary</returns> + public IDictionary AsDictionary() + { + return _properties; } - void AdjustEncodingSizeWhenAdding(object key, object value) + /// <summary> + /// Returns a string representation of this field table + /// </summary> + /// <returns>A string</returns> + public override string ToString() { - _encodedSize += EncodingUtils.EncodedShortStringLength((string) key); - // the extra byte if for the type indicator what is written out - if (value is string) - { - _encodedSize += 1 + EncodingUtils.EncodedLongStringLength((string) value); - } - else if (value is int || value is uint || value is long) - { - _encodedSize += 1 + 4; - } - else + StringBuilder sb = new StringBuilder("FieldTable {"); + + bool first = true; + InitMapIfNecessary(); + foreach ( DictionaryEntry entry in _properties ) + { + if ( !first ) + { + sb.Append(", "); + } + first = false; + sb.Append(entry.Key).Append(" => ").Append(entry.Value); + } + + sb.Append("}"); + return sb.ToString(); + } + + /// <summary> + /// Serializes this instance to the specified <see cref="ByteBuffer"/>. + /// </summary> + /// <param name="buffer">The buffer to write to</param> + public void WriteToBuffer(ByteBuffer buffer) + { + if ( _log.IsDebugEnabled ) { - // Should never get here since was already checked - throw new Exception("Unsupported value type: " + value.GetType()); + _log.Debug("FieldTable::writeToBuffer: Writing encoded length of " + EncodedSize + "..."); } + + EncodingUtils.WriteUnsignedInteger(buffer, EncodedSize); + WritePayload(buffer); } - private void AdjustEncodingSizeWhenRemoving(object key, object value) + /// <summary> + /// Returns a byte array with the serialized representation + /// of this field table + /// </summary> + /// <returns>An array of bytes</returns> + public byte[] GetDataAsBytes() { - _encodedSize -= EncodingUtils.EncodedShortStringLength((string) key); - if (value != null) - { - if (value is string) - { - _encodedSize -= 1 + EncodingUtils.EncodedLongStringLength((string) value); - } - else if (value is int || value is uint || value is long) - { - _encodedSize -= 5; - } - else - { - // Should never get here - throw new Exception("Illegal value type: " + value.GetType()); - } - } + ByteBuffer buffer = ByteBuffer.allocate((int)_encodedSize); + WritePayload(buffer); + byte[] result = new byte[_encodedSize]; + buffer.flip(); + buffer.get(result); + //buffer.Release(); + return result; } - public IEnumerator GetEnumerator() + #endregion // Public Methods + + #region Private Methods + // + // Private Methods + // + + private static void CheckPropertyName(string propertyName) { - return _hash.GetEnumerator(); + if ( propertyName == null || propertyName.Length == 0 ) + throw new ArgumentNullException("propertyName"); + CheckIdentifierFormat(propertyName); } - public bool Contains(string s) + private static void CheckIdentifierFormat(string propertyName) { - return _hash.Contains(s); + // AMQP Spec: 4.2.5.5 Field Tables + // Guidelines for implementers: + // * Field names MUST start with a letter, '$' or '#' and may continue with + // letters, '$' or '#', digits, or underlines, to a maximum length of 128 + // characters. + // * The server SHOULD validate field names and upon receiving an invalid + // field name, it SHOULD signal a connection exception with reply code + // 503 (syntax error). Conformance test: amq_wlp_table_01. + // * A peer MUST handle duplicate fields by using only the first instance. + + + // AMQP length limit + if ( propertyName.Length > 128 ) + { + throw new ArgumentException("AMQP limits property names to 128 characters"); + } + + // AMQ start character + if ( !(Char.IsLetter(propertyName[0]) + || propertyName[0] == '$' + || propertyName[0] == '#' + || propertyName[0] == '_' ) )// Not official AMQP added for JMS. + { + throw new ArgumentException("Identifier '" + propertyName + "' does not start with a valid AMQP start character"); + } } - public void Clear() + private object GetObject(string key) + { + AMQTypedValue value = GetProperty(key); + return value != null ? value.Value : null; + } + + private void SetObject(string key, object value) + { + if ( value is bool ) + { + SetBoolean(key, (bool)value); + } else if ( value is byte ) + { + SetByte(key, (byte)value); + } else if ( value is sbyte ) + { + SetSByte(key, (sbyte)value); + } else if ( value is short ) + { + SetInt16(key, (short)value); + } else if ( value is ushort ) + { + SetUInt16(key, (ushort)value); + } else if ( value is int ) + { + SetInt32(key, (int) value); + } else if ( value is uint ) + { + SetUInt32(key, (uint)value); + } else if ( value is long ) + { + SetInt64(key, (long) value); + } else if ( value is ulong ) + { + SetUInt64(key, (ulong)value); + } else if ( value is char ) + { + SetChar(key, (char) value); + } else if ( value is float ) + { + SetFloat(key, (float) value); + } else if ( value is double ) + { + SetDouble(key, (double) value); + } else if ( value is decimal ) + { + SetDecimal(key, (decimal) value); + } else if ( value is string ) + { + SetString(key, (string) value); + } else if ( value is byte[] ) + { + SetBytes(key, (byte[])value); + } else + { + throw new ArgumentException("Data type not supported yet"); + } + } + + private AMQTypedValue GetProperty(string name) { - _hash.Clear(); - _encodedSize = 0; + lock ( _syncLock ) + { + if ( _properties == null ) + { + if ( _encodedForm == null ) + { + return null; + } else + { + PopulateFromBuffer(); + } + } + return (AMQTypedValue) _properties[name]; + } } - public void Remove(string key) + private void PopulateFromBuffer() { - object value = _hash[key]; - if (value != null) - { - AdjustEncodingSizeWhenRemoving(key, value); - } - _hash.Remove(key); + try + { + SetFromBuffer(_encodedForm, _encodedSize); + } catch ( AMQFrameDecodingException e ) + { + _log.Error("Error decoding FieldTable in deferred decoding mode ", e); + throw; + } } - public IDictionary AsDictionary() + private void SetFromBuffer(ByteBuffer buffer, uint length) { - return _hash; + bool trace = _log.IsDebugEnabled; + if ( length > 0 ) + { + int expectedRemaining = buffer.remaining() - (int)length; + _properties = new LinkedHashtable(); + + do + { + string key = EncodingUtils.ReadShortString(buffer); + AMQTypedValue value = AMQTypedValue.ReadFromBuffer(buffer); + if ( trace ) + { + _log.Debug(string.Format("FieldTable::PropFieldTable(buffer,{0}): Read type '{1}', key '{2}', value '{3}'", length, value.Type, key, value.Value)); + } + _properties.Add(key, value); + + } while ( buffer.remaining() > expectedRemaining ); + _encodedSize = length; + } + if ( trace ) + { + _log.Debug("FieldTable::FieldTable(buffer," + length + "): Done."); + } } - public override string ToString() + private void InitMapIfNecessary() { - StringBuilder sb = new StringBuilder("FieldTable{"); + lock ( _syncLock ) + { + if ( _properties == null ) + { + if ( _encodedForm == null ) + { + _properties = new LinkedHashtable(); + } else + { + PopulateFromBuffer(); + } + } + } + } - bool first = true; - foreach (DictionaryEntry entry in _hash) - { - if (first) - { - first = !first; - } - else - { - sb.Append(", "); - } - sb.Append(entry.Key).Append(" => ").Append(entry.Value); - } + private AMQTypedValue SetProperty(string key, AMQTypedValue value) + { + InitMapIfNecessary(); + _encodedForm = null; + if ( value == null ) + { + RemoveKey(key); + } + AMQTypedValue oldVal = (AMQTypedValue)_properties[key]; + _properties.Add(key, value); + if ( oldVal != null ) + { + _encodedSize -= oldVal.EncodingLength; + } else + { + _encodedSize += EncodingUtils.EncodedShortStringLength(key) + (uint)1; + } + if ( value != null ) + { + _encodedSize += value.EncodingLength; + } - sb.Append("}"); - return sb.ToString(); + return oldVal; + } + + public void WritePayload(ByteBuffer buffer) + { + if ( _encodedForm != null ) + { + buffer.put(_encodedForm); + } else if ( _properties != null ) + { + foreach ( DictionaryEntry de in _properties ) + { + string key = (string)de.Key; + AMQTypedValue value = (AMQTypedValue)de.Value; + try + { + if ( _log.IsDebugEnabled ) + { + _log.Debug("Writing Property:" + key + + " Type:" + value.Type + + " Value:" + value.Value); + _log.Debug("Buffer Position:" + buffer.position() + + " Remaining:" + buffer.remaining()); + } + //Write the actual parameter name + EncodingUtils.WriteShortStringBytes(buffer, key); + value.WriteToBuffer(buffer); + } catch ( Exception ex ) + { + if ( _log.IsDebugEnabled ) + { + _log.Debug("Exception thrown:" + ex); + _log.Debug("Writing Property:" + key + + " Type:" + value.Type + + " Value:" + value.Value); + _log.Debug("Buffer Position:" + buffer.position() + + " Remaining:" + buffer.remaining()); + } + } + } + } } + #endregion // Private Methods } } diff --git a/dotnet/Qpid.Common/Qpid.Common.csproj b/dotnet/Qpid.Common/Qpid.Common.csproj index e0b5d22efb..626a889e0a 100644 --- a/dotnet/Qpid.Common/Qpid.Common.csproj +++ b/dotnet/Qpid.Common/Qpid.Common.csproj @@ -53,6 +53,7 @@ <Compile Include="Framing\AMQMethodBody.cs" />
<Compile Include="Framing\AMQMethodBodyFactory.cs" />
<Compile Include="Framing\AMQProtocolHeaderException.cs" />
+ <Compile Include="Framing\AMQType.cs" />
<Compile Include="Framing\BasicContentHeaderProperties.cs" />
<Compile Include="Framing\CompositeAMQDataBlock.cs" />
<Compile Include="Framing\ContentBody.cs" />
@@ -60,6 +61,8 @@ <Compile Include="Framing\ContentHeaderBody.cs" />
<Compile Include="Framing\ContentHeaderBodyFactory.cs" />
<Compile Include="Framing\ContentHeaderPropertiesFactory.cs" />
+ <Compile Include="Framing\AMQTypedValue.cs" />
+ <Compile Include="Framing\AMQTypeMap.cs" />
<Compile Include="Framing\EncodingUtils.cs" />
<Compile Include="Framing\FieldTable.cs" />
<Compile Include="Framing\HeartbeatBody.cs" />
diff --git a/dotnet/Qpid.Common/Qpid.Common.mdp b/dotnet/Qpid.Common/Qpid.Common.mdp index aa334cbb71..310b063d63 100644 --- a/dotnet/Qpid.Common/Qpid.Common.mdp +++ b/dotnet/Qpid.Common/Qpid.Common.mdp @@ -27,6 +27,9 @@ <File name="./Framing/AMQMethodBody.cs" subtype="Code" buildaction="Compile" /> <File name="./Framing/AMQMethodBodyFactory.cs" subtype="Code" buildaction="Compile" /> <File name="./Framing/AMQProtocolHeaderException.cs" subtype="Code" buildaction="Compile" /> + <File name="./Framing/AMQType.cs" subtype="Code" buildaction="Compile" /> + <File name="./Framing/AMQTypedValue.cs" subtype="Code" buildaction="Compile" /> + <File name="./Framing/AMQTypeMap.cs" subtype="Code" buildaction="Compile" /> <File name="./Framing/BasicContentHeaderProperties.cs" subtype="Code" buildaction="Compile" /> <File name="./Framing/CompositeAMQDataBlock.cs" subtype="Code" buildaction="Compile" /> <File name="./Framing/ContentBody.cs" subtype="Code" buildaction="Compile" /> @@ -151,4 +154,4 @@ <ProjectReference type="Project" localcopy="True" refto="Qpid.Messaging" /> <ProjectReference type="Gac" localcopy="True" refto="System.Xml, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </References> -</Project>
\ No newline at end of file +</Project> |
