FieldTable.java
0001 /*
0002  *
0003  * Licensed to the Apache Software Foundation (ASF) under one
0004  * or more contributor license agreements.  See the NOTICE file
0005  * distributed with this work for additional information
0006  * regarding copyright ownership.  The ASF licenses this file
0007  * to you under the Apache License, Version 2.0 (the
0008  * "License"); you may not use this file except in compliance
0009  * with the License.  You may obtain a copy of the License at
0010  *
0011  *   http://www.apache.org/licenses/LICENSE-2.0
0012  *
0013  * Unless required by applicable law or agreed to in writing,
0014  * software distributed under the License is distributed on an
0015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0016  * KIND, either express or implied.  See the License for the
0017  * specific language governing permissions and limitations
0018  * under the License.
0019  *
0020  */
0021 package org.apache.qpid.framing;
0022 
0023 import org.apache.mina.common.ByteBuffer;
0024 
0025 import org.apache.qpid.AMQPInvalidClassException;
0026 
0027 import org.slf4j.Logger;
0028 import org.slf4j.LoggerFactory;
0029 
0030 import java.math.BigDecimal;
0031 import java.util.Collections;
0032 import java.util.Enumeration;
0033 import java.util.Iterator;
0034 import java.util.LinkedHashMap;
0035 import java.util.LinkedHashSet;
0036 import java.util.Map;
0037 import java.util.Set;
0038 
0039 // extends FieldTable
0040 public class FieldTable
0041 {
0042     private static final Logger _logger = LoggerFactory.getLogger(FieldTable.class);
0043     private static final String STRICT_AMQP = "STRICT_AMQP";
0044     private final boolean _strictAMQP = Boolean.valueOf(System.getProperty(STRICT_AMQP, "false"));
0045 
0046     private ByteBuffer _encodedForm;
0047     private LinkedHashMap<AMQShortString, AMQTypedValue> _properties;
0048     private long _encodedSize;
0049     private static final int INITIAL_HASHMAP_CAPACITY = 16;
0050     private static final int INITIAL_ENCODED_FORM_SIZE = 256;
0051 
0052     public FieldTable()
0053     {
0054         super();
0055         // _encodedForm = ByteBuffer.allocate(INITIAL_ENCODED_FORM_SIZE);
0056         // _encodedForm.setAutoExpand(true);
0057         // _encodedForm.limit(0);
0058     }
0059 
0060     /**
0061      * Construct a new field table.
0062      *
0063      @param buffer the buffer from which to read data. The length byte must be read already
0064      @param length the length of the field table. Must be > 0.
0065      *
0066      @throws AMQFrameDecodingException if there is an error decoding the table
0067      */
0068     public FieldTable(ByteBuffer buffer, long lengththrows AMQFrameDecodingException
0069     {
0070         this();
0071         _encodedForm = buffer.slice();
0072         _encodedForm.limit((intlength);
0073         _encodedSize = length;
0074         buffer.skip((intlength);
0075     }
0076 
0077     public AMQTypedValue getProperty(AMQShortString string)
0078     {
0079         checkPropertyName(string);
0080 
0081         synchronized (this)
0082         {
0083             if (_properties == null)
0084             {
0085                 if (_encodedForm == null)
0086                 {
0087                     return null;
0088                 }
0089                 else
0090                 {
0091                     populateFromBuffer();
0092                 }
0093             }
0094         }
0095 
0096         if (_properties == null)
0097         {
0098             return null;
0099         }
0100         else
0101         {
0102             return _properties.get(string);
0103         }
0104     }
0105 
0106     private void populateFromBuffer()
0107     {
0108         try
0109         {
0110             setFromBuffer(_encodedForm, _encodedSize);
0111         }
0112         catch (AMQFrameDecodingException e)
0113         {
0114             _logger.error("Error decoding FieldTable in deferred decoding mode ", e);
0115             throw new IllegalArgumentException(e);
0116         }
0117     }
0118 
0119     private AMQTypedValue setProperty(AMQShortString key, AMQTypedValue val)
0120     {
0121         checkPropertyName(key);
0122         initMapIfNecessary();
0123         if (_properties.containsKey(key))
0124         {
0125             _encodedForm = null;
0126 
0127             if (val == null)
0128             {
0129                 return removeKey(key);
0130             }
0131         }
0132         else if ((_encodedForm != null&& (val != null))
0133         {
0134             EncodingUtils.writeShortStringBytes(_encodedForm, key);
0135             val.writeToBuffer(_encodedForm);
0136 
0137         }
0138         else if (val == null)
0139         {
0140             return null;
0141         }
0142 
0143         AMQTypedValue oldVal = _properties.put(key, val);
0144         if (oldVal != null)
0145         {
0146             _encodedSize -= oldVal.getEncodingSize();
0147         }
0148         else
0149         {
0150             _encodedSize += EncodingUtils.encodedShortStringLength(key1;
0151         }
0152 
0153         _encodedSize += val.getEncodingSize();
0154 
0155         return oldVal;
0156     }
0157 
0158     private void initMapIfNecessary()
0159     {
0160         synchronized (this)
0161         {
0162             if (_properties == null)
0163             {
0164                 if ((_encodedForm == null|| (_encodedSize == 0))
0165                 {
0166                     _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>();
0167                 }
0168                 else
0169                 {
0170                     populateFromBuffer();
0171                 }
0172             }
0173 
0174         }
0175     }
0176 
0177     public Boolean getBoolean(String string)
0178     {
0179         return getBoolean(new AMQShortString(string));
0180     }
0181 
0182     public Boolean getBoolean(AMQShortString string)
0183     {
0184         AMQTypedValue value = getProperty(string);
0185         if ((value != null&& (value.getType() == AMQType.BOOLEAN))
0186         {
0187             return (Booleanvalue.getValue();
0188         }
0189         else
0190         {
0191             return null;
0192         }
0193     }
0194 
0195     public Byte getByte(String string)
0196     {
0197         return getByte(new AMQShortString(string));
0198     }
0199 
0200     public Byte getByte(AMQShortString string)
0201     {
0202         AMQTypedValue value = getProperty(string);
0203         if ((value != null&& (value.getType() == AMQType.BYTE))
0204         {
0205             return (Bytevalue.getValue();
0206         }
0207         else
0208         {
0209             return null;
0210         }
0211     }
0212 
0213     public Short getShort(String string)
0214     {
0215         return getShort(new AMQShortString(string));
0216     }
0217 
0218     public Short getShort(AMQShortString string)
0219     {
0220         AMQTypedValue value = getProperty(string);
0221         if ((value != null&& (value.getType() == AMQType.SHORT))
0222         {
0223             return (Shortvalue.getValue();
0224         }
0225         else
0226         {
0227             return null;
0228         }
0229     }
0230 
0231     public Integer getInteger(String string)
0232     {
0233         return getInteger(new AMQShortString(string));
0234     }
0235 
0236     public Integer getInteger(AMQShortString string)
0237     {
0238         AMQTypedValue value = getProperty(string);
0239         if ((value != null&& (value.getType() == AMQType.INT))
0240         {
0241             return (Integervalue.getValue();
0242         }
0243         else
0244         {
0245             return null;
0246         }
0247     }
0248 
0249     public Long getLong(String string)
0250     {
0251         return getLong(new AMQShortString(string));
0252     }
0253 
0254     public Long getLong(AMQShortString string)
0255     {
0256         AMQTypedValue value = getProperty(string);
0257         if ((value != null&& (value.getType() == AMQType.LONG))
0258         {
0259             return (Longvalue.getValue();
0260         }
0261         else
0262         {
0263             return null;
0264         }
0265     }
0266 
0267     public Float getFloat(String string)
0268     {
0269         return getFloat(new AMQShortString(string));
0270     }
0271 
0272     public Float getFloat(AMQShortString string)
0273     {
0274         AMQTypedValue value = getProperty(string);
0275         if ((value != null&& (value.getType() == AMQType.FLOAT))
0276         {
0277             return (Floatvalue.getValue();
0278         }
0279         else
0280         {
0281             return null;
0282         }
0283     }
0284 
0285     public Double getDouble(String string)
0286     {
0287         return getDouble(new AMQShortString(string));
0288     }
0289 
0290     public Double getDouble(AMQShortString string)
0291     {
0292         AMQTypedValue value = getProperty(string);
0293         if ((value != null&& (value.getType() == AMQType.DOUBLE))
0294         {
0295             return (Doublevalue.getValue();
0296         }
0297         else
0298         {
0299             return null;
0300         }
0301     }
0302 
0303     public String getString(String string)
0304     {
0305         return getString(new AMQShortString(string));
0306     }
0307 
0308     public String getString(AMQShortString string)
0309     {
0310         AMQTypedValue value = getProperty(string);
0311         if ((value != null&& ((value.getType() == AMQType.WIDE_STRING|| (value.getType() == AMQType.ASCII_STRING)))
0312         {
0313             return (Stringvalue.getValue();
0314         }
0315         else if ((value != null&& (value.getValue() != null&& !(value.getValue() instanceof byte[]))
0316         {
0317             return String.valueOf(value.getValue());
0318         }
0319         else
0320         {
0321             return null;
0322         }
0323 
0324     }
0325 
0326     public Character getCharacter(String string)
0327     {
0328         return getCharacter(new AMQShortString(string));
0329     }
0330 
0331     public Character getCharacter(AMQShortString string)
0332     {
0333         AMQTypedValue value = getProperty(string);
0334         if ((value != null&& (value.getType() == AMQType.ASCII_CHARACTER))
0335         {
0336             return (Charactervalue.getValue();
0337         }
0338         else
0339         {
0340             return null;
0341         }
0342     }
0343 
0344     public byte[] getBytes(String string)
0345     {
0346         return getBytes(new AMQShortString(string));
0347     }
0348 
0349     public byte[] getBytes(AMQShortString string)
0350     {
0351         AMQTypedValue value = getProperty(string);
0352         if ((value != null&& (value.getType() == AMQType.BINARY))
0353         {
0354             return (byte[]) value.getValue();
0355         }
0356         else
0357         {
0358             return null;
0359         }
0360     }
0361 
0362     /**
0363      * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name.
0364      *
0365      @param string The name of the parameter to get the associated FieldTable value for.
0366      *
0367      @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or
0368      *         not present in the field table at all.
0369      */
0370     public FieldTable getFieldTable(String string)
0371     {
0372         return getFieldTable(new AMQShortString(string));
0373     }
0374 
0375     /**
0376      * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name.
0377      *
0378      @param string The name of the parameter to get the associated FieldTable value for.
0379      *
0380      @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or
0381      *         not present in the field table at all.
0382      */
0383     public FieldTable getFieldTable(AMQShortString string)
0384     {
0385         AMQTypedValue value = getProperty(string);
0386 
0387         if ((value != null&& (value.getType() == AMQType.FIELD_TABLE))
0388         {
0389             return (FieldTablevalue.getValue();
0390         }
0391         else
0392         {
0393             return null;
0394         }
0395     }
0396 
0397     public Object getObject(String string)
0398     {
0399         return getObject(new AMQShortString(string));
0400     }
0401 
0402     public Object getObject(AMQShortString string)
0403     {
0404         AMQTypedValue value = getProperty(string);
0405         if (value != null)
0406         {
0407             return value.getValue();
0408         }
0409         else
0410         {
0411             return value;
0412         }
0413 
0414     }
0415 
0416     public Long getTimestamp(AMQShortString name)
0417     {
0418         AMQTypedValue value = getProperty(name);
0419         if ((value != null&& (value.getType() == AMQType.TIMESTAMP))
0420         {
0421             return (Longvalue.getValue();
0422         }
0423         else
0424         {
0425             return null;
0426         }
0427     }
0428 
0429     public BigDecimal getDecimal(AMQShortString propertyName)
0430     {
0431         AMQTypedValue value = getProperty(propertyName);
0432         if ((value != null&& (value.getType() == AMQType.DECIMAL))
0433         {
0434             return (BigDecimalvalue.getValue();
0435         }
0436         else
0437         {
0438             return null;
0439         }
0440     }
0441 
0442     // ************  Setters
0443     public Object setBoolean(String string, Boolean b)
0444     {
0445         return setBoolean(new AMQShortString(string), b);
0446     }
0447 
0448     public Object setBoolean(AMQShortString string, Boolean b)
0449     {
0450         return setProperty(string, AMQType.BOOLEAN.asTypedValue(b));
0451     }
0452 
0453     public Object setByte(String string, Byte b)
0454     {
0455         return setByte(new AMQShortString(string), b);
0456     }
0457 
0458     public Object setByte(AMQShortString string, Byte b)
0459     {
0460         return setProperty(string, AMQType.BYTE.asTypedValue(b));
0461     }
0462 
0463     public Object setShort(String string, Short i)
0464     {
0465         return setShort(new AMQShortString(string), i);
0466     }
0467 
0468     public Object setShort(AMQShortString string, Short i)
0469     {
0470         return setProperty(string, AMQType.SHORT.asTypedValue(i));
0471     }
0472 
0473     public Object setInteger(String string, Integer i)
0474     {
0475         return setInteger(new AMQShortString(string), i);
0476     }
0477 
0478     public Object setInteger(AMQShortString string, Integer i)
0479     {
0480         return setProperty(string, AMQType.INT.asTypedValue(i));
0481     }
0482 
0483     public Object setLong(String string, Long l)
0484     {
0485         return setLong(new AMQShortString(string), l);
0486     }
0487 
0488     public Object setLong(AMQShortString string, Long l)
0489     {
0490         return setProperty(string, AMQType.LONG.asTypedValue(l));
0491     }
0492 
0493     public Object setFloat(String string, Float f)
0494     {
0495         return setFloat(new AMQShortString(string), f);
0496     }
0497 
0498     public Object setFloat(AMQShortString string, Float v)
0499     {
0500         return setProperty(string, AMQType.FLOAT.asTypedValue(v));
0501     }
0502 
0503     public Object setDouble(String string, Double d)
0504     {
0505         return setDouble(new AMQShortString(string), d);
0506     }
0507 
0508     public Object setDouble(AMQShortString string, Double v)
0509     {
0510         return setProperty(string, AMQType.DOUBLE.asTypedValue(v));
0511     }
0512 
0513     public Object setString(String string, String s)
0514     {
0515         return setString(new AMQShortString(string), s);
0516     }
0517 
0518     public Object setAsciiString(AMQShortString string, String value)
0519     {
0520         if (value == null)
0521         {
0522             return setProperty(string, AMQType.VOID.asTypedValue(null));
0523         }
0524         else
0525         {
0526             return setProperty(string, AMQType.ASCII_STRING.asTypedValue(value));
0527         }
0528     }
0529 
0530     public Object setString(AMQShortString string, String value)
0531     {
0532         if (value == null)
0533         {
0534             return setProperty(string, AMQType.VOID.asTypedValue(null));
0535         }
0536         else
0537         {
0538             return setProperty(string, AMQType.LONG_STRING.asTypedValue(value));
0539         }
0540     }
0541 
0542     public Object setChar(String string, char c)
0543     {
0544         return setChar(new AMQShortString(string), c);
0545     }
0546 
0547     public Object setChar(AMQShortString string, char c)
0548     {
0549         return setProperty(string, AMQType.ASCII_CHARACTER.asTypedValue(c));
0550     }
0551 
0552     public Object setBytes(String string, byte[] b)
0553     {
0554         return setBytes(new AMQShortString(string), b);
0555     }
0556 
0557     public Object setBytes(AMQShortString string, byte[] bytes)
0558     {
0559         return setProperty(string, AMQType.BINARY.asTypedValue(bytes));
0560     }
0561 
0562     public Object setBytes(String string, byte[] bytes, int start, int length)
0563     {
0564         return setBytes(new AMQShortString(string), bytes, start, length);
0565     }
0566 
0567     public Object setBytes(AMQShortString string, byte[] bytes, int start, int length)
0568     {
0569         byte[] newBytes = new byte[length];
0570         System.arraycopy(bytes, start, newBytes, 0, length);
0571 
0572         return setBytes(string, bytes);
0573     }
0574 
0575     public Object setObject(String string, Object o)
0576     {
0577         return setObject(new AMQShortString(string), o);
0578     }
0579 
0580     public Object setTimestamp(AMQShortString string, long datetime)
0581     {
0582         return setProperty(string, AMQType.TIMESTAMP.asTypedValue(datetime));
0583     }
0584 
0585     public Object setDecimal(AMQShortString string, BigDecimal decimal)
0586     {
0587         if (decimal.longValue() > Integer.MAX_VALUE)
0588         {
0589             throw new UnsupportedOperationException("AMQP doesnot support decimals larger than " + Integer.MAX_VALUE);
0590         }
0591 
0592         if (decimal.scale() > Byte.MAX_VALUE)
0593         {
0594             throw new UnsupportedOperationException("AMQP doesnot support decimal scales larger than " + Byte.MAX_VALUE);
0595         }
0596 
0597         return setProperty(string, AMQType.DECIMAL.asTypedValue(decimal));
0598     }
0599 
0600     public Object setVoid(AMQShortString string)
0601     {
0602         return setProperty(string, AMQType.VOID.asTypedValue(null));
0603     }
0604 
0605     /**
0606      * Associates a nested field table with the specified parameter name.
0607      *
0608      @param string  The name of the parameter to store in the table.
0609      @param ftValue The field table value to associate with the parameter name.
0610      *
0611      @return The stored value.
0612      */
0613     public Object setFieldTable(String string, FieldTable ftValue)
0614     {
0615         return setFieldTable(new AMQShortString(string), ftValue);
0616     }
0617 
0618     /**
0619      * Associates a nested field table with the specified parameter name.
0620      *
0621      @param string  The name of the parameter to store in the table.
0622      @param ftValue The field table value to associate with the parameter name.
0623      *
0624      @return The stored value.
0625      */
0626     public Object setFieldTable(AMQShortString string, FieldTable ftValue)
0627     {
0628         return setProperty(string, AMQType.FIELD_TABLE.asTypedValue(ftValue));
0629     }
0630 
0631     public Object setObject(AMQShortString string, Object object)
0632     {
0633         if (object instanceof Boolean)
0634         {
0635             return setBoolean(string, (Booleanobject);
0636         }
0637         else if (object instanceof Byte)
0638         {
0639             return setByte(string, (Byteobject);
0640         }
0641         else if (object instanceof Short)
0642         {
0643             return setShort(string, (Shortobject);
0644         }
0645         else if (object instanceof Integer)
0646         {
0647             return setInteger(string, (Integerobject);
0648         }
0649         else if (object instanceof Long)
0650         {
0651             return setLong(string, (Longobject);
0652         }
0653         else if (object instanceof Float)
0654         {
0655             return setFloat(string, (Floatobject);
0656         }
0657         else if (object instanceof Double)
0658         {
0659             return setDouble(string, (Doubleobject);
0660         }
0661         else if (object instanceof String)
0662         {
0663             return setString(string, (Stringobject);
0664         }
0665         else if (object instanceof Character)
0666         {
0667             return setChar(string, (Characterobject);
0668         }
0669         else if (object instanceof byte[])
0670         {
0671             return setBytes(string, (byte[]) object);
0672         }
0673 
0674         throw new AMQPInvalidClassException("Only Primatives objects allowed Object is:" + object.getClass());
0675     }
0676 
0677     public boolean isNullStringValue(String name)
0678     {
0679         AMQTypedValue value = getProperty(new AMQShortString(name));
0680 
0681         return (value != null&& (value.getType() == AMQType.VOID);
0682     }
0683 
0684     // ***** Methods
0685 
0686     public Enumeration getPropertyNames()
0687     {
0688         return Collections.enumeration(keys());
0689     }
0690 
0691     public boolean propertyExists(AMQShortString propertyName)
0692     {
0693         return itemExists(propertyName);
0694     }
0695 
0696     public boolean propertyExists(String propertyName)
0697     {
0698         return itemExists(propertyName);
0699     }
0700 
0701     public boolean itemExists(AMQShortString propertyName)
0702     {
0703         checkPropertyName(propertyName);
0704         initMapIfNecessary();
0705 
0706         return _properties.containsKey(propertyName);
0707     }
0708 
0709     public boolean itemExists(String string)
0710     {
0711         return itemExists(new AMQShortString(string));
0712     }
0713 
0714     public String toString()
0715     {
0716         initMapIfNecessary();
0717 
0718         return _properties.toString();
0719     }
0720 
0721     private void checkPropertyName(AMQShortString propertyName)
0722     {
0723         if (propertyName == null)
0724         {
0725             throw new IllegalArgumentException("Property name must not be null");
0726         }
0727         else if (propertyName.length() == 0)
0728         {
0729             throw new IllegalArgumentException("Property name must not be the empty string");
0730         }
0731 
0732         if (_strictAMQP)
0733         {
0734             checkIdentiferFormat(propertyName);
0735         }
0736     }
0737 
0738     protected static void checkIdentiferFormat(AMQShortString propertyName)
0739     {
0740         // AMQP Spec: 4.2.5.5 Field Tables
0741         // Guidelines for implementers:
0742         // * Field names MUST start with a letter, '$' or '#' and may continue with
0743         // letters, '$' or '#', digits, or underlines, to a maximum length of 128
0744         // characters.
0745         // * The server SHOULD validate field names and upon receiving an invalid
0746         // field name, it SHOULD signal a connection exception with reply code
0747         // 503 (syntax error). Conformance test: amq_wlp_table_01.
0748         // * A peer MUST handle duplicate fields by using only the first instance.
0749 
0750         // AMQP length limit
0751         if (propertyName.length() 128)
0752         {
0753             throw new IllegalArgumentException("AMQP limits property names to 128 characters");
0754         }
0755 
0756         // AMQ start character
0757         if (!(Character.isLetter(propertyName.charAt(0)) || (propertyName.charAt(0== '$')
0758                     || (propertyName.charAt(0== '#'|| (propertyName.charAt(0== '_'))) // Not official AMQP added for JMS.
0759         {
0760             throw new IllegalArgumentException("Identifier '" + propertyName
0761                 "' does not start with a valid AMQP start character");
0762         }
0763     }
0764 
0765     // *************************  Byte Buffer Processing
0766 
0767     public void writeToBuffer(ByteBuffer buffer)
0768     {
0769         final boolean trace = _logger.isDebugEnabled();
0770 
0771         if (trace)
0772         {
0773             _logger.debug("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() "...");
0774             if (_properties != null)
0775             {
0776                 _logger.debug(_properties.toString());
0777             }
0778         }
0779 
0780         EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize());
0781 
0782         putDataInBuffer(buffer);
0783     }
0784 
0785     public byte[] getDataAsBytes()
0786     {
0787         final int encodedSize = (intgetEncodedSize();
0788         final ByteBuffer buffer = ByteBuffer.allocate(encodedSize)// FIXME XXX: Is cast a problem?
0789 
0790         putDataInBuffer(buffer);
0791 
0792         final byte[] result = new byte[encodedSize];
0793         buffer.flip();
0794         buffer.get(result);
0795         buffer.release();
0796 
0797         return result;
0798     }
0799 
0800     public long getEncodedSize()
0801     {
0802         return _encodedSize;
0803     }
0804 
0805     private void recalculateEncodedSize()
0806     {
0807 
0808         int encodedSize = 0;
0809         if (_properties != null)
0810         {
0811             for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet())
0812             {
0813                 encodedSize += EncodingUtils.encodedShortStringLength(e.getKey());
0814                 encodedSize++; // the byte for the encoding Type
0815                 encodedSize += e.getValue().getEncodingSize();
0816 
0817             }
0818         }
0819 
0820         _encodedSize = encodedSize;
0821     }
0822 
0823     public void addAll(FieldTable fieldTable)
0824     {
0825         initMapIfNecessary();
0826         _encodedForm = null;
0827         _properties.putAll(fieldTable._properties);
0828         recalculateEncodedSize();
0829     }
0830 
0831     public static interface FieldTableElementProcessor
0832     {
0833         public boolean processElement(String propertyName, AMQTypedValue value);
0834 
0835         public Object getResult();
0836     }
0837 
0838     public Object processOverElements(FieldTableElementProcessor processor)
0839     {
0840         initMapIfNecessary();
0841         if (_properties != null)
0842         {
0843             for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet())
0844             {
0845                 boolean result = processor.processElement(e.getKey().toString(), e.getValue());
0846                 if (!result)
0847                 {
0848                     break;
0849                 }
0850             }
0851         }
0852 
0853         return processor.getResult();
0854 
0855     }
0856 
0857     public int size()
0858     {
0859         initMapIfNecessary();
0860 
0861         return _properties.size();
0862 
0863     }
0864 
0865     public boolean isEmpty()
0866     {
0867         return size() == 0;
0868     }
0869 
0870     public boolean containsKey(AMQShortString key)
0871     {
0872         initMapIfNecessary();
0873 
0874         return _properties.containsKey(key);
0875     }
0876 
0877     public boolean containsKey(String key)
0878     {
0879         return containsKey(new AMQShortString(key));
0880     }
0881 
0882     public Set<String> keys()
0883     {
0884         initMapIfNecessary();
0885         Set<String> keys = new LinkedHashSet<String>();
0886         for (AMQShortString key : _properties.keySet())
0887         {
0888             keys.add(key.toString());
0889         }
0890 
0891         return keys;
0892     }
0893 
0894     public Iterator<Map.Entry<AMQShortString, AMQTypedValue>> iterator()
0895     {
0896         if(_encodedForm != null)
0897         {
0898             return new FieldTableIterator(_encodedForm.duplicate().rewind(),(int)_encodedSize);
0899         }
0900         else
0901         {
0902             initMapIfNecessary();
0903             return _properties.entrySet().iterator();
0904         }
0905     }
0906 
0907 
0908     public Object get(AMQShortString key)
0909     {
0910 
0911         return getObject(key);
0912     }
0913 
0914     public Object put(AMQShortString key, Object value)
0915     {
0916         return setObject(key, value);
0917     }
0918 
0919     public Object remove(String key)
0920     {
0921 
0922         return remove(new AMQShortString(key));
0923 
0924     }
0925 
0926     public Object remove(AMQShortString key)
0927     {
0928         AMQTypedValue val = removeKey(key);
0929 
0930         return (val == nullnull : val.getValue();
0931 
0932     }
0933 
0934     public AMQTypedValue removeKey(AMQShortString key)
0935     {
0936         initMapIfNecessary();
0937         _encodedForm = null;
0938         AMQTypedValue value = _properties.remove(key);
0939         if (value == null)
0940         {
0941             return null;
0942         }
0943         else
0944         {
0945             _encodedSize -= EncodingUtils.encodedShortStringLength(key);
0946             _encodedSize--;
0947             _encodedSize -= value.getEncodingSize();
0948 
0949             return value;
0950         }
0951 
0952     }
0953 
0954     public void clear()
0955     {
0956         initMapIfNecessary();
0957         _encodedForm = null;
0958         _properties.clear();
0959         _encodedSize = 0;
0960     }
0961 
0962     public Set<AMQShortString> keySet()
0963     {
0964         initMapIfNecessary();
0965 
0966         return _properties.keySet();
0967     }
0968 
0969     private void putDataInBuffer(ByteBuffer buffer)
0970     {
0971 
0972         if (_encodedForm != null)
0973         {
0974             if(buffer.isDirect() || buffer.isReadOnly())
0975             {
0976                 ByteBuffer encodedForm = _encodedForm.duplicate();
0977 
0978                 if (encodedForm.position() != 0)
0979                 {
0980                     encodedForm.flip();
0981                 }
0982 
0983                 buffer.put(encodedForm);
0984             }
0985             else
0986             {
0987                 buffer.put(_encodedForm.array(),_encodedForm.arrayOffset(),(int)_encodedSize);
0988             }
0989         }
0990         else if (_properties != null)
0991         {
0992             final Iterator<Map.Entry<AMQShortString, AMQTypedValue>> it = _properties.entrySet().iterator();
0993 
0994             // If there are values then write out the encoded Size... could check _encodedSize != 0
0995             // write out the total length, which we have kept up to date as data is added
0996 
0997             while (it.hasNext())
0998             {
0999                 final Map.Entry<AMQShortString, AMQTypedValue> me = it.next();
1000                 try
1001                 {
1002                     if (_logger.isDebugEnabled())
1003                     {
1004                         _logger.debug("Writing Property:" + me.getKey() " Type:" + me.getValue().getType() " Value:"
1005                             + me.getValue().getValue());
1006                         _logger.debug("Buffer Position:" + buffer.position() " Remaining:" + buffer.remaining());
1007                     }
1008 
1009                     // Write the actual parameter name
1010                     EncodingUtils.writeShortStringBytes(buffer, me.getKey());
1011                     me.getValue().writeToBuffer(buffer);
1012                 }
1013                 catch (Exception e)
1014                 {
1015                     if (_logger.isDebugEnabled())
1016                     {
1017                         _logger.debug("Exception thrown:" + e);
1018                         _logger.debug("Writing Property:" + me.getKey() " Type:" + me.getValue().getType() " Value:"
1019                             + me.getValue().getValue());
1020                         _logger.debug("Buffer Position:" + buffer.position() " Remaining:" + buffer.remaining());
1021                     }
1022 
1023                     throw new RuntimeException(e);
1024                 }
1025             }
1026         }
1027     }
1028 
1029     private void setFromBuffer(ByteBuffer buffer, long lengththrows AMQFrameDecodingException
1030     {
1031 
1032         final boolean trace = _logger.isDebugEnabled();
1033         if (length > 0)
1034         {
1035 
1036             final int expectedRemaining = buffer.remaining() (intlength;
1037 
1038             _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(INITIAL_HASHMAP_CAPACITY);
1039 
1040             do
1041             {
1042 
1043                 final AMQShortString key = EncodingUtils.readAMQShortString(buffer);
1044                 AMQTypedValue value = AMQTypedValue.readFromBuffer(buffer);
1045 
1046                 if (trace)
1047                 {
1048                     _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType()
1049                         "', key '" + key + "', value '" + value.getValue() "'");
1050                 }
1051 
1052                 _properties.put(key, value);
1053 
1054             }
1055             while (buffer.remaining() > expectedRemaining);
1056 
1057         }
1058 
1059         _encodedSize = length;
1060 
1061         if (trace)
1062         {
1063             _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done.");
1064         }
1065     }
1066 
1067     private static final class FieldTableEntry implements Map.Entry<AMQShortString, AMQTypedValue>
1068     {
1069         private final AMQTypedValue _value;
1070         private final AMQShortString _key;
1071 
1072         public FieldTableEntry(final AMQShortString key, final AMQTypedValue value)
1073         {
1074             _key = key;
1075             _value = value;
1076         }
1077 
1078         public AMQShortString getKey()
1079         {
1080             return _key;
1081         }
1082 
1083         public AMQTypedValue getValue()
1084         {
1085             return _value;
1086         }
1087 
1088         public AMQTypedValue setValue(final AMQTypedValue value)
1089         {
1090             throw new UnsupportedOperationException();
1091         }
1092 
1093         public boolean equals(Object o)
1094         {
1095             if(instanceof FieldTableEntry)
1096             {
1097                 FieldTableEntry other = (FieldTableEntryo;
1098                 return (_key == null ? other._key == null : _key.equals(other._key))
1099                        && (_value == null ? other._value == null : _value.equals(other._value));
1100             }
1101             else
1102             {
1103                 return false;
1104             }
1105         }
1106 
1107         public int hashCode()
1108         {
1109             return (getKey()==null   : getKey().hashCode())
1110                    (getValue()==null : getValue().hashCode());
1111         }
1112 
1113     }
1114 
1115 
1116     private static final class FieldTableIterator implements Iterator<Map.Entry<AMQShortString, AMQTypedValue>>
1117     {
1118 
1119         private final ByteBuffer _buffer;
1120         private int _expectedRemaining;
1121 
1122         public FieldTableIterator(ByteBuffer buffer, int length)
1123         {
1124             _buffer = buffer;
1125             _expectedRemaining = buffer.remaining() - length;
1126         }
1127 
1128         public boolean hasNext()
1129         {
1130             return (_buffer.remaining() > _expectedRemaining);
1131         }
1132 
1133         public Map.Entry<AMQShortString, AMQTypedValue> next()
1134         {
1135             if(hasNext())
1136             {
1137                 final AMQShortString key = EncodingUtils.readAMQShortString(_buffer);
1138                 AMQTypedValue value = AMQTypedValue.readFromBuffer(_buffer);
1139                 return new FieldTableEntry(key, value);
1140             }
1141             else
1142             {
1143                 return null;
1144             }
1145         }
1146 
1147         public void remove()
1148         {
1149             throw new UnsupportedOperationException();
1150         }
1151     }
1152 
1153 
1154 
1155 
1156     public int hashCode()
1157     {
1158         initMapIfNecessary();
1159 
1160         return _properties.hashCode();
1161     }
1162 
1163     public boolean equals(Object o)
1164     {
1165         if (o == this)
1166         {
1167             return true;
1168         }
1169 
1170         if (o == null)
1171         {
1172             return false;
1173         }
1174 
1175         if (!(instanceof FieldTable))
1176         {
1177             return false;
1178         }
1179 
1180         initMapIfNecessary();
1181 
1182         FieldTable f = (FieldTableo;
1183         f.initMapIfNecessary();
1184 
1185         return _properties.equals(f._properties);
1186     }
1187 }