diff options
Diffstat (limited to 'qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java')
-rw-r--r-- | qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java | 1246 |
1 files changed, 1246 insertions, 0 deletions
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java new file mode 100644 index 0000000000..22205d49f8 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -0,0 +1,1246 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.AMQPInvalidClassException; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +// extends FieldTable +public class FieldTable +{ + private static final Logger _logger = LoggerFactory.getLogger(FieldTable.class); + private static final String STRICT_AMQP = "STRICT_AMQP"; + private final boolean _strictAMQP = Boolean.valueOf(System.getProperty(STRICT_AMQP, "false")); + + private ByteBuffer _encodedForm; + private LinkedHashMap<AMQShortString, AMQTypedValue> _properties; + private long _encodedSize; + private static final int INITIAL_HASHMAP_CAPACITY = 16; + private static final int INITIAL_ENCODED_FORM_SIZE = 256; + + public FieldTable() + { + super(); + // _encodedForm = ByteBuffer.allocate(INITIAL_ENCODED_FORM_SIZE); + // _encodedForm.setAutoExpand(true); + // _encodedForm.limit(0); + } + + /** + * 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. + */ + public FieldTable(ByteBuffer buffer, long length) + { + this(); + ByteBuffer encodedForm = buffer.slice(); + encodedForm.limit((int) length); + _encodedForm = ByteBuffer.allocate((int)length); + _encodedForm.put(encodedForm); + _encodedForm.flip(); + _encodedSize = length; + buffer.skip((int) length); + } + + public AMQTypedValue getProperty(AMQShortString string) + { + checkPropertyName(string); + + synchronized (this) + { + if (_properties == null) + { + if (_encodedForm == null) + { + return null; + } + else + { + populateFromBuffer(); + } + } + } + + if (_properties == null) + { + return null; + } + else + { + return _properties.get(string); + } + } + + private void populateFromBuffer() + { + try + { + setFromBuffer(_encodedForm, _encodedSize); + } + catch (AMQFrameDecodingException e) + { + _logger.error("Error decoding FieldTable in deferred decoding mode ", e); + throw new IllegalArgumentException(e); + } + } + + private AMQTypedValue setProperty(AMQShortString key, AMQTypedValue val) + { + checkPropertyName(key); + initMapIfNecessary(); + if (_properties.containsKey(key)) + { + _encodedForm = null; + + if (val == null) + { + return removeKey(key); + } + } + else if ((_encodedForm != null) && (val != null)) + { + // We have updated data to store in the buffer + // So clear the _encodedForm to allow it to be rebuilt later + // this is safer than simply appending to any existing buffer. + _encodedForm = null; + } + else if (val == null) + { + return null; + } + + AMQTypedValue oldVal = _properties.put(key, val); + if (oldVal != null) + { + _encodedSize -= oldVal.getEncodingSize(); + } + else + { + _encodedSize += EncodingUtils.encodedShortStringLength(key) + 1; + } + + _encodedSize += val.getEncodingSize(); + + return oldVal; + } + + private void initMapIfNecessary() + { + synchronized (this) + { + if (_properties == null) + { + if ((_encodedForm == null) || (_encodedSize == 0)) + { + _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(); + } + else + { + populateFromBuffer(); + } + } + + } + } + + public Boolean getBoolean(String string) + { + return getBoolean(new AMQShortString(string)); + } + + public Boolean getBoolean(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BOOLEAN)) + { + return (Boolean) value.getValue(); + } + else + { + return null; + } + } + + public Byte getByte(String string) + { + return getByte(new AMQShortString(string)); + } + + public Byte getByte(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BYTE)) + { + return (Byte) value.getValue(); + } + else + { + return null; + } + } + + public Short getShort(String string) + { + return getShort(new AMQShortString(string)); + } + + public Short getShort(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.SHORT)) + { + return (Short) value.getValue(); + } + else + { + return null; + } + } + + public Integer getInteger(String string) + { + return getInteger(new AMQShortString(string)); + } + + public Integer getInteger(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.INT)) + { + return (Integer) value.getValue(); + } + else + { + return null; + } + } + + public Long getLong(String string) + { + return getLong(new AMQShortString(string)); + } + + public Long getLong(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.LONG)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public Float getFloat(String string) + { + return getFloat(new AMQShortString(string)); + } + + public Float getFloat(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.FLOAT)) + { + return (Float) value.getValue(); + } + else + { + return null; + } + } + + public Double getDouble(String string) + { + return getDouble(new AMQShortString(string)); + } + + public Double getDouble(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.DOUBLE)) + { + return (Double) value.getValue(); + } + else + { + return null; + } + } + + public String getString(String string) + { + return getString(new AMQShortString(string)); + } + + public String getString(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && ((value.getType() == AMQType.WIDE_STRING) || (value.getType() == AMQType.ASCII_STRING))) + { + return (String) value.getValue(); + } + else if ((value != null) && (value.getValue() != null) && !(value.getValue() instanceof byte[])) + { + return String.valueOf(value.getValue()); + } + else + { + return null; + } + + } + + public Character getCharacter(String string) + { + return getCharacter(new AMQShortString(string)); + } + + public Character getCharacter(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.ASCII_CHARACTER)) + { + return (Character) value.getValue(); + } + else + { + return null; + } + } + + public byte[] getBytes(String string) + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BINARY)) + { + return (byte[]) value.getValue(); + } + else + { + return null; + } + } + + /** + * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name. + * + * @param string The name of the parameter to get the associated FieldTable value for. + * + * @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or + * not present in the field table at all. + */ + public FieldTable getFieldTable(String string) + { + return getFieldTable(new AMQShortString(string)); + } + + /** + * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name. + * + * @param string The name of the parameter to get the associated FieldTable value for. + * + * @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or + * not present in the field table at all. + */ + public FieldTable getFieldTable(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + + if ((value != null) && (value.getType() == AMQType.FIELD_TABLE)) + { + return (FieldTable) value.getValue(); + } + else + { + return null; + } + } + + public Object getObject(String string) + { + return getObject(new AMQShortString(string)); + } + + public Object getObject(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if (value != null) + { + return value.getValue(); + } + else + { + return value; + } + + } + + public Long getTimestamp(AMQShortString name) + { + AMQTypedValue value = getProperty(name); + if ((value != null) && (value.getType() == AMQType.TIMESTAMP)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public BigDecimal getDecimal(AMQShortString propertyName) + { + AMQTypedValue value = getProperty(propertyName); + if ((value != null) && (value.getType() == AMQType.DECIMAL)) + { + return (BigDecimal) value.getValue(); + } + else + { + return null; + } + } + + // ************ Setters + public Object setBoolean(String string, Boolean b) + { + return setBoolean(new AMQShortString(string), b); + } + + public Object setBoolean(AMQShortString string, Boolean b) + { + return setProperty(string, AMQType.BOOLEAN.asTypedValue(b)); + } + + public Object setByte(String string, Byte b) + { + return setByte(new AMQShortString(string), b); + } + + public Object setByte(AMQShortString string, Byte b) + { + return setProperty(string, AMQType.BYTE.asTypedValue(b)); + } + + public Object setShort(String string, Short i) + { + return setShort(new AMQShortString(string), i); + } + + public Object setShort(AMQShortString string, Short i) + { + return setProperty(string, AMQType.SHORT.asTypedValue(i)); + } + + public Object setInteger(String string, Integer i) + { + return setInteger(new AMQShortString(string), i); + } + + public Object setInteger(AMQShortString string, Integer i) + { + return setProperty(string, AMQType.INT.asTypedValue(i)); + } + + public Object setLong(String string, Long l) + { + return setLong(new AMQShortString(string), l); + } + + public Object setLong(AMQShortString string, Long l) + { + return setProperty(string, AMQType.LONG.asTypedValue(l)); + } + + public Object setFloat(String string, Float f) + { + return setFloat(new AMQShortString(string), f); + } + + public Object setFloat(AMQShortString string, Float v) + { + return setProperty(string, AMQType.FLOAT.asTypedValue(v)); + } + + public Object setDouble(String string, Double d) + { + return setDouble(new AMQShortString(string), d); + } + + public Object setDouble(AMQShortString string, Double v) + { + return setProperty(string, AMQType.DOUBLE.asTypedValue(v)); + } + + public Object setString(String string, String s) + { + return setString(new AMQShortString(string), s); + } + + public Object setAsciiString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.ASCII_STRING.asTypedValue(value)); + } + } + + public Object setString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.LONG_STRING.asTypedValue(value)); + } + } + + public Object setChar(String string, char c) + { + return setChar(new AMQShortString(string), c); + } + + public Object setChar(AMQShortString string, char c) + { + return setProperty(string, AMQType.ASCII_CHARACTER.asTypedValue(c)); + } + + public Object setBytes(String string, byte[] b) + { + return setBytes(new AMQShortString(string), b); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + return setProperty(string, AMQType.BINARY.asTypedValue(bytes)); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + return setBytes(new AMQShortString(string), bytes, start, length); + } + + public Object setBytes(AMQShortString string, byte[] bytes, int start, int length) + { + byte[] newBytes = new byte[length]; + System.arraycopy(bytes, start, newBytes, 0, length); + + return setBytes(string, bytes); + } + + public Object setObject(String string, Object o) + { + return setObject(new AMQShortString(string), o); + } + + public Object setTimestamp(AMQShortString string, long datetime) + { + return setProperty(string, AMQType.TIMESTAMP.asTypedValue(datetime)); + } + + public Object setDecimal(AMQShortString string, BigDecimal decimal) + { + if (decimal.longValue() > Integer.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimals larger than " + Integer.MAX_VALUE); + } + + if (decimal.scale() > Byte.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimal scales larger than " + Byte.MAX_VALUE); + } + + return setProperty(string, AMQType.DECIMAL.asTypedValue(decimal)); + } + + public Object setVoid(AMQShortString string) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + + /** + * Associates a nested field table with the specified parameter name. + * + * @param string The name of the parameter to store in the table. + * @param ftValue The field table value to associate with the parameter name. + * + * @return The stored value. + */ + public Object setFieldTable(String string, FieldTable ftValue) + { + return setFieldTable(new AMQShortString(string), ftValue); + } + + /** + * Associates a nested field table with the specified parameter name. + * + * @param string The name of the parameter to store in the table. + * @param ftValue The field table value to associate with the parameter name. + * + * @return The stored value. + */ + public Object setFieldTable(AMQShortString string, FieldTable ftValue) + { + return setProperty(string, AMQType.FIELD_TABLE.asTypedValue(ftValue)); + } + + public Object setObject(AMQShortString string, Object object) + { + if (object instanceof Boolean) + { + return setBoolean(string, (Boolean) object); + } + else if (object instanceof Byte) + { + return setByte(string, (Byte) object); + } + else if (object instanceof Short) + { + return setShort(string, (Short) object); + } + else if (object instanceof Integer) + { + return setInteger(string, (Integer) object); + } + else if (object instanceof Long) + { + return setLong(string, (Long) object); + } + else if (object instanceof Float) + { + return setFloat(string, (Float) object); + } + else if (object instanceof Double) + { + return setDouble(string, (Double) object); + } + else if (object instanceof String) + { + return setString(string, (String) object); + } + else if (object instanceof Character) + { + return setChar(string, (Character) object); + } + else if (object instanceof byte[]) + { + return setBytes(string, (byte[]) object); + } + + throw new AMQPInvalidClassException(AMQPInvalidClassException.INVALID_OBJECT_MSG + (object == null ? "null" : object.getClass())); + } + + public boolean isNullStringValue(String name) + { + AMQTypedValue value = getProperty(new AMQShortString(name)); + + return (value != null) && (value.getType() == AMQType.VOID); + } + + // ***** Methods + + public Enumeration getPropertyNames() + { + return Collections.enumeration(keys()); + } + + public boolean propertyExists(AMQShortString propertyName) + { + return itemExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + return itemExists(propertyName); + } + + public boolean itemExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + initMapIfNecessary(); + + return _properties.containsKey(propertyName); + } + + public boolean itemExists(String string) + { + return itemExists(new AMQShortString(string)); + } + + public String toString() + { + initMapIfNecessary(); + + return _properties.toString(); + } + + private void checkPropertyName(AMQShortString propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + if (_strictAMQP) + { + checkIdentiferFormat(propertyName); + } + } + + protected static void checkIdentiferFormat(AMQShortString 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 IllegalArgumentException("AMQP limits property names to 128 characters"); + } + + // AMQ start character + if (!(Character.isLetter(propertyName.charAt(0)) || (propertyName.charAt(0) == '$') + || (propertyName.charAt(0) == '#') || (propertyName.charAt(0) == '_'))) // Not official AMQP added for JMS. + { + throw new IllegalArgumentException("Identifier '" + propertyName + + "' does not start with a valid AMQP start character"); + } + } + + // ************************* Byte Buffer Processing + + public void writeToBuffer(ByteBuffer buffer) + { + final boolean trace = _logger.isDebugEnabled(); + + if (trace) + { + _logger.debug("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "..."); + if (_properties != null) + { + _logger.debug(_properties.toString()); + } + } + + EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize()); + + putDataInBuffer(buffer); + } + + public byte[] getDataAsBytes() + { + final int encodedSize = (int) getEncodedSize(); + final ByteBuffer buffer = ByteBuffer.allocate(encodedSize); // FIXME XXX: Is cast a problem? + + putDataInBuffer(buffer); + + final byte[] result = new byte[encodedSize]; + buffer.flip(); + buffer.get(result); + buffer.release(); + + return result; + } + + public long getEncodedSize() + { + return _encodedSize; + } + + private void recalculateEncodedSize() + { + + int encodedSize = 0; + if (_properties != null) + { + for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet()) + { + encodedSize += EncodingUtils.encodedShortStringLength(e.getKey()); + encodedSize++; // the byte for the encoding Type + encodedSize += e.getValue().getEncodingSize(); + + } + } + + _encodedSize = encodedSize; + } + + public void addAll(FieldTable fieldTable) + { + initMapIfNecessary(); + _encodedForm = null; + _properties.putAll(fieldTable._properties); + recalculateEncodedSize(); + } + + public static Map<String, Object> convertToMap(final FieldTable fieldTable) + { + final Map<String, Object> map = new HashMap<String, Object>(); + + if(fieldTable != null) + { + fieldTable.processOverElements( + new FieldTableElementProcessor() + { + + public boolean processElement(String propertyName, AMQTypedValue value) + { + Object val = value.getValue(); + if(val instanceof AMQShortString) + { + val = val.toString(); + } + map.put(propertyName, val); + return true; + } + + public Object getResult() + { + return map; + } + }); + } + return map; + } + + + public static interface FieldTableElementProcessor + { + public boolean processElement(String propertyName, AMQTypedValue value); + + public Object getResult(); + } + + public Object processOverElements(FieldTableElementProcessor processor) + { + initMapIfNecessary(); + if (_properties != null) + { + for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet()) + { + boolean result = processor.processElement(e.getKey().toString(), e.getValue()); + if (!result) + { + break; + } + } + } + + return processor.getResult(); + + } + + public int size() + { + initMapIfNecessary(); + + return _properties.size(); + + } + + public boolean isEmpty() + { + return size() == 0; + } + + public boolean containsKey(AMQShortString key) + { + initMapIfNecessary(); + + return _properties.containsKey(key); + } + + public boolean containsKey(String key) + { + return containsKey(new AMQShortString(key)); + } + + public Set<String> keys() + { + initMapIfNecessary(); + Set<String> keys = new LinkedHashSet<String>(); + for (AMQShortString key : _properties.keySet()) + { + keys.add(key.toString()); + } + + return keys; + } + + public Iterator<Map.Entry<AMQShortString, AMQTypedValue>> iterator() + { + if(_encodedForm != null) + { + return new FieldTableIterator(_encodedForm.duplicate().rewind(),(int)_encodedSize); + } + else + { + initMapIfNecessary(); + return _properties.entrySet().iterator(); + } + } + + public Object get(String key) + { + return get(new AMQShortString(key)); + } + + public Object get(AMQShortString key) + { + return getObject(key); + } + + public Object put(AMQShortString key, Object value) + { + return setObject(key, value); + } + + public Object remove(String key) + { + + return remove(new AMQShortString(key)); + + } + + public Object remove(AMQShortString key) + { + AMQTypedValue val = removeKey(key); + + return (val == null) ? null : val.getValue(); + + } + + public AMQTypedValue removeKey(AMQShortString key) + { + initMapIfNecessary(); + _encodedForm = null; + AMQTypedValue value = _properties.remove(key); + if (value == null) + { + return null; + } + else + { + _encodedSize -= EncodingUtils.encodedShortStringLength(key); + _encodedSize--; + _encodedSize -= value.getEncodingSize(); + + return value; + } + + } + + public void clear() + { + initMapIfNecessary(); + _encodedForm = null; + _properties.clear(); + _encodedSize = 0; + } + + public Set<AMQShortString> keySet() + { + initMapIfNecessary(); + + return _properties.keySet(); + } + + private void putDataInBuffer(ByteBuffer buffer) + { + + if (_encodedForm != null) + { + if(buffer.isDirect() || buffer.isReadOnly()) + { + ByteBuffer encodedForm = _encodedForm.duplicate(); + + if (encodedForm.position() != 0) + { + encodedForm.flip(); + } + + buffer.put(encodedForm); + } + else + { + buffer.put(_encodedForm.array(),_encodedForm.arrayOffset(),(int)_encodedSize); + } + } + else if (_properties != null) + { + final Iterator<Map.Entry<AMQShortString, AMQTypedValue>> it = _properties.entrySet().iterator(); + + // If there are values then write out the encoded Size... could check _encodedSize != 0 + // write out the total length, which we have kept up to date as data is added + + while (it.hasNext()) + { + final Map.Entry<AMQShortString, AMQTypedValue> me = it.next(); + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + // Write the actual parameter name + EncodingUtils.writeShortStringBytes(buffer, me.getKey()); + me.getValue().writeToBuffer(buffer); + } + catch (Exception e) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Exception thrown:" + e); + _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + throw new RuntimeException(e); + } + } + } + } + + private void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + + final boolean trace = _logger.isDebugEnabled(); + if (length > 0) + { + + final int expectedRemaining = buffer.remaining() - (int) length; + + _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(INITIAL_HASHMAP_CAPACITY); + + do + { + + final AMQShortString key = EncodingUtils.readAMQShortString(buffer); + + _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read key '" + key); + + AMQTypedValue value = AMQTypedValue.readFromBuffer(buffer); + + if (trace) + { + _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType() + + "', key '" + key + "', value '" + value.getValue() + "'"); + } + + _properties.put(key, value); + + } + while (buffer.remaining() > expectedRemaining); + + } + + _encodedSize = length; + + if (trace) + { + _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done."); + } + } + + private static final class FieldTableEntry implements Map.Entry<AMQShortString, AMQTypedValue> + { + private final AMQTypedValue _value; + private final AMQShortString _key; + + public FieldTableEntry(final AMQShortString key, final AMQTypedValue value) + { + _key = key; + _value = value; + } + + public AMQShortString getKey() + { + return _key; + } + + public AMQTypedValue getValue() + { + return _value; + } + + public AMQTypedValue setValue(final AMQTypedValue value) + { + throw new UnsupportedOperationException(); + } + + public boolean equals(Object o) + { + if(o instanceof FieldTableEntry) + { + FieldTableEntry other = (FieldTableEntry) o; + return (_key == null ? other._key == null : _key.equals(other._key)) + && (_value == null ? other._value == null : _value.equals(other._value)); + } + else + { + return false; + } + } + + public int hashCode() + { + return (getKey()==null ? 0 : getKey().hashCode()) + ^ (getValue()==null ? 0 : getValue().hashCode()); + } + + } + + + private static final class FieldTableIterator implements Iterator<Map.Entry<AMQShortString, AMQTypedValue>> + { + + private final ByteBuffer _buffer; + private int _expectedRemaining; + + public FieldTableIterator(ByteBuffer buffer, int length) + { + _buffer = buffer; + _expectedRemaining = buffer.remaining() - length; + } + + public boolean hasNext() + { + return (_buffer.remaining() > _expectedRemaining); + } + + public Map.Entry<AMQShortString, AMQTypedValue> next() + { + if(hasNext()) + { + final AMQShortString key = EncodingUtils.readAMQShortString(_buffer); + AMQTypedValue value = AMQTypedValue.readFromBuffer(_buffer); + return new FieldTableEntry(key, value); + } + else + { + return null; + } + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + + + + public int hashCode() + { + initMapIfNecessary(); + + return _properties.hashCode(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o == null) + { + return false; + } + + if (!(o instanceof FieldTable)) + { + return false; + } + + initMapIfNecessary(); + + FieldTable f = (FieldTable) o; + f.initMapIfNecessary(); + + return _properties.equals(f._properties); + } + + public static FieldTable convertToFieldTable(Map<String, Object> map) + { + if (map != null) + { + FieldTable table = new FieldTable(); + for(Map.Entry<String,Object> entry : map.entrySet()) + { + table.put(new AMQShortString(entry.getKey()), entry.getValue()); + } + + return table; + } + else + { + return null; + } + } + + +} |