blob: 1b159f61dfc1f9c3dc194c5157a8d1f35228ce5a [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.server.protocol.v1_0.codec;
import java.lang.reflect.Array;
import java.util.Map;
import org.apache.qpid.server.bytebuffer.QpidByteBuffer;
import org.apache.qpid.server.protocol.v1_0.type.AmqpErrorException;
import org.apache.qpid.server.protocol.v1_0.type.transport.AmqpError;
import org.apache.qpid.server.protocol.v1_0.type.transport.Error;
public abstract class AbstractCompositeTypeConstructor<T> implements DescribedTypeConstructor<T>
{
@Override
public TypeConstructor<T> construct(final Object descriptor,
final QpidByteBuffer in,
final int originalPosition,
final ValueHandler valueHandler) throws AmqpErrorException
{
return new FieldValueReader();
}
protected abstract String getTypeName();
protected abstract T construct(FieldValueReader x) throws AmqpErrorException;
protected class FieldValueReader implements TypeConstructor<T>
{
private QpidByteBuffer _in;
private ValueHandler _valueHandler;
private int _count;
@Override
public T construct(final QpidByteBuffer in, final ValueHandler handler) throws AmqpErrorException
{
_in = in;
_valueHandler = handler;
return constructType();
}
private T constructType() throws AmqpErrorException
{
int size;
final TypeConstructor typeConstructor = _valueHandler.readConstructor(_in);
long remaining = _in.remaining();
if (typeConstructor instanceof ListConstructor)
{
ListConstructor listConstructor = (ListConstructor) typeConstructor;
if (remaining < listConstructor.getSize() * 2)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Not sufficient data for deserialization of '%s'."
+ " Expected at least %d bytes. Got %d bytes.",
getTypeName(),
listConstructor.getSize(),
remaining));
}
if (listConstructor.getSize() == 1)
{
size = _in.getUnsignedByte();
_count = _in.getUnsignedByte();
}
else
{
size = _in.getInt();
_count = _in.getInt();
}
remaining -= listConstructor.getSize();
if (remaining < size)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Not sufficient data for deserialization of '%s'."
+ " Expected at least %d bytes. Got %d bytes.",
getTypeName(),
size,
remaining));
}
}
else if (typeConstructor instanceof ZeroListConstructor)
{
size = 0;
_count = 0;
}
else
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Unexpected format when deserializing of '%s'",
getTypeName()));
}
final T constructedObject = AbstractCompositeTypeConstructor.this.construct(this);
long expectedRemaining = remaining - size;
long unconsumedBytes = _in.remaining() - expectedRemaining;
if(unconsumedBytes > 0)
{
final String msg =
String.format("%s incorrectly encoded, %d bytes remaining after decoding %d elements",
getTypeName(), unconsumedBytes, _count);
throw new AmqpErrorException(AmqpError.DECODE_ERROR, msg);
}
else if (unconsumedBytes < 0)
{
final String msg = String.format(
"%s incorrectly encoded, %d bytes beyond provided size consumed after decoding %d elements",
getTypeName(),
-unconsumedBytes,
_count);
throw new AmqpErrorException(AmqpError.DECODE_ERROR, msg);
}
return constructedObject;
}
public <F> F readValue(final int fieldIndex,
final String fieldName,
final boolean mandatory,
final Class<F> expectedType) throws AmqpErrorException
{
if (fieldIndex >= _count)
{
if (mandatory)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName()));
}
return null;
}
Object value = _valueHandler.parse(_in);
if (value == null && mandatory)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName()));
}
if (value != null && !expectedType.isAssignableFrom(value.getClass()))
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format(
"Wrong type for field '%s' of '%s'. Expected '%s' but got '%s'.",
fieldName,
getTypeName(),
expectedType.getSimpleName(),
value.getClass().getSimpleName()));
}
return (F) value;
}
public <K, V> Map<K, V> readMapValue(final int fieldIndex,
final String fieldName,
final boolean mandatory,
final Class<K> expectedKeyType,
final Class<V> expectedValueType)
throws AmqpErrorException
{
if (fieldIndex >= _count)
{
if (mandatory)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName()));
}
return null;
}
TypeConstructor typeConstructor = _valueHandler.readConstructor(_in);
if (typeConstructor instanceof MapConstructor)
{
MapConstructor mapConstructor = ((MapConstructor) typeConstructor);
return mapConstructor.construct(_in,
_valueHandler,
expectedKeyType,
expectedValueType);
}
else if (typeConstructor instanceof NullTypeConstructor)
{
if (mandatory)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
"Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName());
}
}
else
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Could not decode value field '%s' of '%s'",
fieldName,
getTypeName()));
}
return null;
}
public <F> F[] readArrayValue(final int fieldIndex,
final String fieldName,
final boolean mandatory,
final Class<F> expectedType,
final Converter<F> converter) throws AmqpErrorException
{
if (fieldIndex >= _count)
{
if (mandatory)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName()));
}
return null;
}
Object value = _valueHandler.parse(_in);
if (mandatory && value == null)
{
throw new AmqpErrorException(AmqpError.DECODE_ERROR,
String.format("Mandatory field '%s' of '%s' was not provided",
fieldName,
getTypeName()));
}
if (value != null)
{
if (value.getClass().isArray())
{
if (expectedType.isAssignableFrom(value.getClass().getComponentType()))
{
return (F[]) value;
}
else
{
final Object[] objects = (Object[]) value;
F[] array = (F[]) Array.newInstance(expectedType, objects.length);
try
{
for (int i = 0; i < objects.length; ++i)
{
array[i] = converter.convert(objects[i]);
}
}
catch (RuntimeException e)
{
Error error = new Error(AmqpError.DECODE_ERROR,
String.format("Could not decode value field '%s' of '%s'", fieldName, getTypeName()));
throw new AmqpErrorException(error, e);
}
return array;
}
}
else if (expectedType.isAssignableFrom(value.getClass()))
{
F[] array = (F[]) Array.newInstance(expectedType, 1);
array[0] = (F) value;
return array;
}
else
{
try
{
final F convertedValue = converter.convert(value);
F[] array = (F[]) Array.newInstance(expectedType, 1);
array[0] = convertedValue;
return array;
}
catch (RuntimeException e)
{
Error error = new Error(AmqpError.DECODE_ERROR,
String.format("Could not decode value field '%s' of '%s'", fieldName, getTypeName()));
throw new AmqpErrorException(error, e);
}
}
}
return null;
}
}
public interface Converter<T>
{
T convert(Object o) throws AmqpErrorException;
}
}