summaryrefslogtreecommitdiff
path: root/qpid/dotnet/Qpid.Common/Framing/FieldTable.cs
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/dotnet/Qpid.Common/Framing/FieldTable.cs')
-rw-r--r--qpid/dotnet/Qpid.Common/Framing/FieldTable.cs633
1 files changed, 633 insertions, 0 deletions
diff --git a/qpid/dotnet/Qpid.Common/Framing/FieldTable.cs b/qpid/dotnet/Qpid.Common/Framing/FieldTable.cs
new file mode 100644
index 0000000000..6567bf58ab
--- /dev/null
+++ b/qpid/dotnet/Qpid.Common/Framing/FieldTable.cs
@@ -0,0 +1,633 @@
+/*
+ *
+ * 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;
+using System.Text;
+using log4net;
+using Apache.Qpid.Buffer;
+using Apache.Qpid.Collections;
+using Apache.Qpid.Messaging;
+
+namespace Apache.Qpid.Framing
+{
+ public class FieldTable : IFieldTable, IEnumerable
+ {
+ 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();
+ }
+
+ /// <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>
+ 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; }
+ }
+
+ /// <summary>
+ /// Number of properties in the field table
+ /// </summary>
+ public int 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 { return GetObject(key); }
+ set { SetObject(key, 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
+
+ #region Public Methods
+ //
+ // Public Methods
+ //
+
+ /// <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)
+ {
+ InitMapIfNecessary();
+ _encodedForm = null;
+ AMQTypedValue value = (AMQTypedValue)_properties[key];
+ if ( value != null )
+ {
+ _properties.Remove(key);
+ _encodedSize -= EncodingUtils.EncodedShortStringLength(key);
+ _encodedSize--;
+ _encodedSize -= value.EncodingLength;
+
+ }
+ return value;
+ }
+
+
+ /// <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)
+ {
+ RemoveKey(key);
+ }
+
+ /// <summary>
+ /// Remove all properties from the table
+ /// </summary>
+ public void Clear()
+ {
+ InitMapIfNecessary();
+ _encodedForm = null;
+ _properties.Clear();
+ _encodedSize = 0;
+ }
+
+ /// <summary>
+ /// Adds all the items from one field table in this one. Will overwrite any items in the current table
+ /// with the same key.
+ /// </summary>
+ /// <param name="ft">the source field table</param>
+ public void AddAll(IFieldTable ft)
+ {
+ foreach ( DictionaryEntry dictionaryEntry in ft )
+ {
+ this[(string)dictionaryEntry.Key] = dictionaryEntry.Value;
+ }
+ }
+
+ /// <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()
+ {
+ InitMapIfNecessary();
+ return _properties.GetEnumerator();
+ }
+
+ /// <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)
+ {
+ InitMapIfNecessary();
+ 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()
+ {
+ InitMapIfNecessary();
+ return _properties;
+ }
+
+ /// <summary>
+ /// Returns a string representation of this field table
+ /// </summary>
+ /// <returns>A string</returns>
+ public override string ToString()
+ {
+ 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 )
+ {
+ _log.Debug("FieldTable::writeToBuffer: Writing encoded length of " + EncodedSize + "...");
+ }
+
+ EncodingUtils.WriteUnsignedInteger(buffer, EncodedSize);
+ WritePayload(buffer);
+ }
+
+ /// <summary>
+ /// Returns a byte array with the serialized representation
+ /// of this field table
+ /// </summary>
+ /// <returns>An array of bytes</returns>
+ public byte[] GetDataAsBytes()
+ {
+ ByteBuffer buffer = ByteBuffer.Allocate((int)_encodedSize);
+ WritePayload(buffer);
+ byte[] result = new byte[_encodedSize];
+ buffer.Flip();
+ buffer.GetBytes(result);
+ //buffer.Release();
+ return result;
+ }
+
+ #endregion // Public Methods
+
+ #region Private Methods
+ //
+ // Private Methods
+ //
+
+ private static void CheckPropertyName(string propertyName)
+ {
+ if ( propertyName == null || propertyName.Length == 0 )
+ throw new ArgumentNullException("propertyName");
+ CheckIdentifierFormat(propertyName);
+ }
+
+ private static void CheckIdentifierFormat(string propertyName)
+ {
+ // 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");
+ }
+ }
+
+ 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)
+ {
+ InitMapIfNecessary();
+ return (AMQTypedValue) _properties[name];
+ }
+
+ private void PopulateFromBuffer()
+ {
+ try
+ {
+ ByteBuffer buffer = _encodedForm;
+ _encodedForm = null;
+ if ( buffer != null )
+ SetFromBuffer(buffer, _encodedSize);
+ } catch ( AMQFrameDecodingException e )
+ {
+ _log.Error("Error decoding FieldTable in deferred decoding mode ", e);
+ throw;
+ }
+ }
+
+ private void SetFromBuffer(ByteBuffer buffer, uint length)
+ {
+ 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.");
+ }
+ }
+
+ private void InitMapIfNecessary()
+ {
+ lock ( _syncLock )
+ {
+ if ( _properties == null )
+ {
+ if ( _encodedForm == null )
+ {
+ _properties = new LinkedHashtable();
+ } else
+ {
+ PopulateFromBuffer();
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ return oldVal;
+ }
+
+ public void WritePayload(ByteBuffer buffer)
+ {
+ if ( _encodedForm != null )
+ {
+ lock ( _syncLock )
+ {
+ buffer.Put(_encodedForm);
+ _encodedForm.Flip();
+ }
+ } 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);
+ }
+ throw;
+ }
+ }
+ }
+ }
+ #endregion // Private Methods
+ }
+}