blob: 22251fe39daea0ac09a0200f6396008e5b03d1f8 [file] [log] [blame]
/*
*
* 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 "]";
}
}