| /* |
| * |
| * 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.proton.codec.impl; |
| |
| import java.nio.ByteBuffer; |
| |
| import org.apache.qpid.proton.amqp.DescribedType; |
| import org.apache.qpid.proton.amqp.Symbol; |
| import org.apache.qpid.proton.codec.Data; |
| |
| class ArrayElement extends AbstractElement<Object[]> |
| { |
| |
| private final boolean _described; |
| private final Data.DataType _arrayType; |
| private ConstructorType _constructorType; |
| private Element _first; |
| |
| |
| static enum ConstructorType { TINY, SMALL, LARGE } |
| |
| |
| static ConstructorType TINY = ConstructorType.TINY; |
| static ConstructorType SMALL = ConstructorType.SMALL; |
| static ConstructorType LARGE = ConstructorType.LARGE; |
| |
| ArrayElement(Element parent, Element prev, boolean described, Data.DataType type) |
| { |
| super(parent, prev); |
| _described = described; |
| _arrayType = type; |
| if(_arrayType == null) |
| { |
| throw new NullPointerException("Array type cannot be null"); |
| } |
| else if(_arrayType == Data.DataType.DESCRIBED) |
| { |
| throw new IllegalArgumentException("Array type cannot be DESCRIBED"); |
| } |
| switch(_arrayType) |
| { |
| case UINT: |
| case ULONG: |
| case LIST: |
| setConstructorType(TINY); |
| break; |
| default: |
| setConstructorType(SMALL); |
| } |
| } |
| |
| ConstructorType constructorType() |
| { |
| return _constructorType; |
| } |
| |
| void setConstructorType(ConstructorType type) |
| { |
| _constructorType = type; |
| } |
| |
| @Override |
| public int size() |
| { |
| ConstructorType oldConstructorType; |
| int bodySize; |
| int count = 0; |
| do |
| { |
| bodySize = 1; // data type constructor |
| oldConstructorType = _constructorType; |
| Element element = _first; |
| while(element != null) |
| { |
| count++; |
| bodySize += element.size(); |
| element = element.next(); |
| } |
| } |
| while (oldConstructorType != constructorType()); |
| |
| if(isDescribed()) |
| { |
| bodySize++; // 00 instruction |
| if(count != 0) |
| { |
| count--; |
| } |
| } |
| |
| if(isElementOfArray()) |
| { |
| ArrayElement parent = (ArrayElement)parent(); |
| if(parent.constructorType()==SMALL) |
| { |
| if(count<=255 && bodySize<=254) |
| { |
| bodySize+=2; |
| } |
| else |
| { |
| parent.setConstructorType(LARGE); |
| bodySize+=8; |
| } |
| } |
| else |
| { |
| bodySize+=8; |
| } |
| } |
| else |
| { |
| |
| if(count<=255 && bodySize<=254) |
| { |
| bodySize+=3; |
| } |
| else |
| { |
| bodySize+=9; |
| } |
| |
| } |
| |
| |
| return bodySize; |
| } |
| |
| @Override |
| public Object[] getValue() |
| { |
| if(isDescribed()) |
| { |
| DescribedType[] rVal = new DescribedType[(int) count()]; |
| Object descriptor = _first == null ? null : _first.getValue(); |
| Element element = _first == null ? null : _first.next(); |
| int i = 0; |
| while(element != null) |
| { |
| rVal[i++] = new DescribedTypeImpl(descriptor, element.getValue()); |
| element = element.next(); |
| } |
| return rVal; |
| } |
| else if(_arrayType == Data.DataType.SYMBOL) |
| { |
| Symbol[] rVal = new Symbol[(int) count()]; |
| SymbolElement element = (SymbolElement) _first; |
| int i = 0; |
| while (element!=null) |
| { |
| rVal[i++] = element.getValue(); |
| element = (SymbolElement) element.next(); |
| } |
| return rVal; |
| } |
| else |
| { |
| Object[] rVal = new Object[(int) count()]; |
| Element element = _first; |
| int i = 0; |
| while (element!=null) |
| { |
| rVal[i++] = element.getValue(); |
| element = element.next(); |
| } |
| return rVal; |
| } |
| } |
| |
| @Override |
| public Data.DataType getDataType() |
| { |
| return Data.DataType.ARRAY; |
| } |
| |
| @Override |
| public int encode(ByteBuffer b) |
| { |
| int size = size(); |
| |
| final int count = (int) count(); |
| |
| if(b.remaining()>=size) |
| { |
| if(!isElementOfArray()) |
| { |
| if(size>257 || count >255) |
| { |
| b.put((byte)0xf0); |
| b.putInt(size-5); |
| b.putInt(count); |
| } |
| else |
| { |
| b.put((byte)0xe0); |
| b.put((byte)(size-2)); |
| b.put((byte)count); |
| } |
| } |
| else |
| { |
| ArrayElement parent = (ArrayElement)parent(); |
| if(parent.constructorType()==SMALL) |
| { |
| b.put((byte)(size-1)); |
| b.put((byte)count); |
| } |
| else |
| { |
| b.putInt(size-4); |
| b.putInt(count); |
| } |
| } |
| Element element = _first; |
| if(isDescribed()) |
| { |
| b.put((byte)0); |
| if(element == null) |
| { |
| b.put((byte)0x40); |
| } |
| else |
| { |
| element.encode(b); |
| element = element.next(); |
| } |
| } |
| switch(_arrayType) |
| { |
| case NULL: |
| b.put((byte)0x40); |
| break; |
| case BOOL: |
| b.put((byte)0x56); |
| break; |
| case UBYTE: |
| b.put((byte)0x50); |
| break; |
| case BYTE: |
| b.put((byte)0x51); |
| break; |
| case USHORT: |
| b.put((byte)0x60); |
| break; |
| case SHORT: |
| b.put((byte)0x61); |
| break; |
| case UINT: |
| switch (constructorType()) |
| { |
| case TINY: |
| b.put((byte)0x43); |
| break; |
| case SMALL: |
| b.put((byte)0x52); |
| break; |
| case LARGE: |
| b.put((byte)0x70); |
| break; |
| } |
| break; |
| case INT: |
| b.put(_constructorType == SMALL ? (byte)0x54 : (byte)0x71); |
| break; |
| case CHAR: |
| b.put((byte)0x73); |
| break; |
| case ULONG: |
| switch (constructorType()) |
| { |
| case TINY: |
| b.put((byte)0x44); |
| break; |
| case SMALL: |
| b.put((byte)0x53); |
| break; |
| case LARGE: |
| b.put((byte)0x80); |
| break; |
| } |
| break; |
| case LONG: |
| b.put(_constructorType == SMALL ? (byte)0x55 : (byte)0x81); |
| break; |
| case TIMESTAMP: |
| b.put((byte)0x83); |
| break; |
| case FLOAT: |
| b.put((byte)0x72); |
| break; |
| case DOUBLE: |
| b.put((byte)0x82); |
| break; |
| case DECIMAL32: |
| b.put((byte)0x74); |
| break; |
| case DECIMAL64: |
| b.put((byte)0x84); |
| break; |
| case DECIMAL128: |
| b.put((byte)0x94); |
| break; |
| case UUID: |
| b.put((byte)0x98); |
| break; |
| case BINARY: |
| b.put(_constructorType == SMALL ? (byte)0xa0 : (byte)0xb0); |
| break; |
| case STRING: |
| b.put(_constructorType == SMALL ? (byte)0xa1 : (byte)0xb1); |
| break; |
| case SYMBOL: |
| b.put(_constructorType == SMALL ? (byte)0xa3 : (byte)0xb3); |
| break; |
| case ARRAY: |
| b.put(_constructorType == SMALL ? (byte)0xe0 : (byte)0xf0); |
| break; |
| case LIST: |
| b.put(_constructorType == TINY ? (byte)0x45 :_constructorType == SMALL ? (byte)0xc0 : (byte)0xd0); |
| break; |
| case MAP: |
| b.put(_constructorType == SMALL ? (byte)0xc1 : (byte)0xd1); |
| break; |
| } |
| while(element!=null) |
| { |
| element.encode(b); |
| element = element.next(); |
| } |
| return size; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| @Override |
| public boolean canEnter() |
| { |
| return true; |
| } |
| |
| @Override |
| public Element child() |
| { |
| return _first; |
| } |
| |
| @Override |
| public void setChild(Element elt) |
| { |
| _first = elt; |
| } |
| |
| @Override |
| public Element addChild(Element element) |
| { |
| if(isDescribed() || element.getDataType() == _arrayType) |
| { |
| _first = element; |
| return element; |
| } |
| else |
| { |
| Element replacement = coerce(element); |
| if(replacement != null) |
| { |
| _first = replacement; |
| return replacement; |
| } |
| throw new IllegalArgumentException("Attempting to add instance of " + element.getDataType() + " to array of " + _arrayType); |
| } |
| } |
| |
| private Element coerce(Element element) |
| { |
| switch (_arrayType) |
| { |
| case INT: |
| int i; |
| switch (element.getDataType()) |
| { |
| case BYTE: |
| i = ((ByteElement)element).getValue().intValue(); |
| break; |
| case SHORT: |
| i = ((ShortElement)element).getValue().intValue(); |
| break; |
| case LONG: |
| i = ((LongElement)element).getValue().intValue(); |
| break; |
| default: |
| return null; |
| } |
| return new IntegerElement(element.parent(),element.prev(),i); |
| |
| case LONG: |
| long l; |
| switch (element.getDataType()) |
| { |
| case BYTE: |
| l = ((ByteElement)element).getValue().longValue(); |
| break; |
| case SHORT: |
| l = ((ShortElement)element).getValue().longValue(); |
| break; |
| case INT: |
| l = ((IntegerElement)element).getValue().longValue(); |
| break; |
| default: |
| return null; |
| } |
| return new LongElement(element.parent(),element.prev(),l); |
| } |
| return null; |
| } |
| |
| @Override |
| public Element checkChild(Element element) |
| { |
| if(element.getDataType() != _arrayType) |
| { |
| Element replacement = coerce(element); |
| if(replacement != null) |
| { |
| return replacement; |
| } |
| throw new IllegalArgumentException("Attempting to add instance of " + element.getDataType() + " to array of " + _arrayType); |
| } |
| return element; |
| } |
| |
| |
| public long count() |
| { |
| int count = 0; |
| Element elt = _first; |
| while(elt != null) |
| { |
| count++; |
| elt = elt.next(); |
| } |
| if(isDescribed() && count != 0) |
| { |
| count--; |
| } |
| return count; |
| } |
| |
| public boolean isDescribed() |
| { |
| return _described; |
| } |
| |
| |
| public Data.DataType getArrayDataType() |
| { |
| return _arrayType; |
| } |
| |
| @Override |
| String startSymbol() { |
| return String.format("%s%s[", isDescribed() ? "D" : "", getArrayDataType()); |
| } |
| |
| @Override |
| String stopSymbol() { |
| return "]"; |
| } |
| |
| } |