diff options
Diffstat (limited to 'qpid/dotnet/Qpid.Common/Framing/FieldTable.cs')
| -rw-r--r-- | qpid/dotnet/Qpid.Common/Framing/FieldTable.cs | 633 |
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 + } +} |
