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 length) throws AMQFrameDecodingException
0069 {
0070 this();
0071 _encodedForm = buffer.slice();
0072 _encodedForm.limit((int) length);
0073 _encodedSize = length;
0074 buffer.skip((int) length);
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(key) + 1;
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 (Boolean) value.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 (Byte) value.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 (Short) value.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 (Integer) value.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 (Long) value.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 (Float) value.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 (Double) value.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 (String) value.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 (Character) value.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 (FieldTable) value.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 (Long) value.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 (BigDecimal) value.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, (Boolean) object);
0636 }
0637 else if (object instanceof Byte)
0638 {
0639 return setByte(string, (Byte) object);
0640 }
0641 else if (object instanceof Short)
0642 {
0643 return setShort(string, (Short) object);
0644 }
0645 else if (object instanceof Integer)
0646 {
0647 return setInteger(string, (Integer) object);
0648 }
0649 else if (object instanceof Long)
0650 {
0651 return setLong(string, (Long) object);
0652 }
0653 else if (object instanceof Float)
0654 {
0655 return setFloat(string, (Float) object);
0656 }
0657 else if (object instanceof Double)
0658 {
0659 return setDouble(string, (Double) object);
0660 }
0661 else if (object instanceof String)
0662 {
0663 return setString(string, (String) object);
0664 }
0665 else if (object instanceof Character)
0666 {
0667 return setChar(string, (Character) object);
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 = (int) getEncodedSize();
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 == null) ? null : 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 length) throws AMQFrameDecodingException
1030 {
1031
1032 final boolean trace = _logger.isDebugEnabled();
1033 if (length > 0)
1034 {
1035
1036 final int expectedRemaining = buffer.remaining() - (int) length;
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(o instanceof FieldTableEntry)
1096 {
1097 FieldTableEntry other = (FieldTableEntry) o;
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 ? 0 : getKey().hashCode())
1110 ^ (getValue()==null ? 0 : 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 (!(o instanceof FieldTable))
1176 {
1177 return false;
1178 }
1179
1180 initMapIfNecessary();
1181
1182 FieldTable f = (FieldTable) o;
1183 f.initMapIfNecessary();
1184
1185 return _properties.equals(f._properties);
1186 }
1187 }
|