X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=yang%2Fyang-data-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fimpl%2Fcodec%2FTypeDefinitionAwareCodec.java;h=e735b28d61ec1192ad8383430dc21b3d0a06d337;hb=300aed51915840a41a1483980ff7d045bde905cb;hp=0939f5281acbc25551b855d9882b4f91df775b34;hpb=dea46b20bfec80fe9d84dd1768219b497d57cc15;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java index 0939f5281a..e735b28d61 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java @@ -1,303 +1,616 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.yangtools.yang.data.impl.codec; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT16_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT32_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT64_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT8_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT16_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT32_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT64_QNAME; +import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT8_QNAME; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.common.io.BaseEncoding; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Set; - +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.bind.DatatypeConverter; +import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec; +import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec; +import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec; +import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec; +import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec; +import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec; +import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec; +import org.opendaylight.yangtools.yang.data.api.codec.StringCodec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec; +import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair; +import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition; + +public abstract class TypeDefinitionAwareCodec> implements DataStringCodec { + + private static final Pattern intPattern = Pattern.compile("[+-]?[1-9][0-9]*$"); + private static final Pattern hexPattern = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+"); + private static final Pattern octalPattern = Pattern.compile("[+-]?0[1-7][0-7]*$"); + + // For up to two characters, this is very fast + private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX"); + + private final Optional typeDefinition; + private final Class inputClass; + + private static final int provideBase(final String integer) { + if (integer == null) { + throw new IllegalArgumentException("String representing integer number cannot be NULL"); + } + + if ((integer.length() == 1) && (integer.charAt(0) == '0')) { + return 10; + } + + final Matcher intMatcher = intPattern.matcher(integer); + if (intMatcher.matches()) { + return 10; + } else { + final Matcher hexMatcher = hexPattern.matcher(integer); + if (hexMatcher.matches()) { + return 16; + } else { + final Matcher octMatcher = octalPattern.matcher(integer); + if (octMatcher.matches()) { + return 8; + } else { + String formatedMessage = String.format("Incorrect lexical representation of integer value: %s." + + "%nAn integer value can be defined as: " + + "%n - a decimal number," + + "%n - a hexadecimal number (prefix 0x)," + + "%n - an octal number (prefix 0)." + + "%nSigned values are allowed. Spaces between digits are NOT allowed.", integer); + throw new NumberFormatException(formatedMessage); + } + } + } + } -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableSet; + private static String normalizeHexadecimal(final String hexInt) { + if (hexInt == null) { + throw new IllegalArgumentException( + "String representing integer number in Hexadecimal format cannot be NULL!"); + } + + return X_MATCHER.removeFrom(hexInt); + } + + private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl( + Optional. absent()); + + private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl( + Optional. absent()); + + private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl( + Optional. absent()); + + private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl( + Optional. absent()); + + private static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl( + Optional. absent()); + + private static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl( + Optional. absent()); + + private static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl( + Optional. absent()); + + private static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl( + Optional. absent()); + + private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl( + Optional. absent()); + + private static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl( + Optional. absent()); -import static org.opendaylight.yangtools.yang.model.util.BaseTypes.*; + private static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl( + Optional. absent()); -public abstract class TypeDefinitionAwareCodec implements StringCodec { + private static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl( + Optional. absent()); - private final TypeDefinition typeDefinition; - private final Class inputClass; + private static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl( + Optional. absent()); - public Class getInputClass() { + @Override + public Class getInputClass() { return inputClass; } - protected TypeDefinitionAwareCodec(TypeDefinition typeDefinition, Class outputClass) { + protected TypeDefinitionAwareCodec(final Optional typeDefinition, final Class outputClass) { + Preconditions.checkArgument(outputClass != null, "Output class must be specified."); this.typeDefinition = typeDefinition; this.inputClass = outputClass; } - public TypeDefinition getTypeDefinition() { + public Optional getTypeDefinition() { return typeDefinition; } - @Override - public String serialize(T data) { - return data.toString(); + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static final TypeDefinitionAwareCodec> from(final TypeDefinition typeDefinition) { + return fromType(typeDefinition); } - public static final TypeDefinitionAwareCodec from(TypeDefinition typeDefinition) { - while(typeDefinition.getBaseType() != null) { - typeDefinition = typeDefinition.getBaseType(); - } - - if (BINARY_QNAME.equals(typeDefinition.getQName())) { - return new BinaryCodec(typeDefinition); - } - if (BITS_QNAME.equals(typeDefinition.getQName())) { - return new BitsCodec(typeDefinition); - } - if (BOOLEAN_QNAME.equals(typeDefinition.getQName())) { - return new BooleanCodec(typeDefinition); - } - if (DECIMAL64_QNAME.equals(typeDefinition.getQName())) { - return new DecimalCodec(typeDefinition); - } - if (EMPTY_QNAME.equals(typeDefinition.getQName())) { - return new EmptyCodec(typeDefinition); - } - if (ENUMERATION_QNAME.equals(typeDefinition.getQName())) { - return new EnumCodec(typeDefinition); - } - if (INT8_QNAME.equals(typeDefinition.getQName())) { - return new Int8Codec(typeDefinition); - } - if (INT16_QNAME.equals(typeDefinition.getQName())) { - return new Int16Codec(typeDefinition); - } - if (INT32_QNAME.equals(typeDefinition.getQName())) { - return new Int32Codec(typeDefinition); - } - if (INT64_QNAME.equals(typeDefinition.getQName())) { - return new Int64Codec(typeDefinition); - } - if (STRING_QNAME.equals(typeDefinition.getQName())) { - return new StringCodec(typeDefinition); - } - if (UINT8_QNAME.equals(typeDefinition.getQName())) { - return new Uint8Codec(typeDefinition); - } - if (UINT16_QNAME.equals(typeDefinition.getQName())) { - return new Uint16Codec(typeDefinition); - } - if (UINT32_QNAME.equals(typeDefinition.getQName())) { - return new Uint32Codec(typeDefinition); - } - if (UINT64_QNAME.equals(typeDefinition.getQName())) { - return new Uint64Codec(typeDefinition); - } - return null; + @SuppressWarnings("unchecked") + public static final > TypeDefinitionAwareCodec fromType(final T typeDefinition) { + T superType = typeDefinition; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + + @SuppressWarnings("rawtypes") + TypeDefinitionAwareCodec codec = null; + + if (superType instanceof BinaryTypeDefinition) { + codec = BINARY_DEFAULT_CODEC; + } else if (superType instanceof BitsTypeDefinition) { + codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)superType ) ); + } else if (superType instanceof BooleanTypeDefinition) { + codec = BOOLEAN_DEFAULT_CODEC; + } else if (superType instanceof DecimalTypeDefinition) { + codec = DECIMAL64_DEFAULT_CODEC; + } else if (superType instanceof EmptyTypeDefinition) { + codec = EMPTY_DEFAULT_CODEC; + } else if (superType instanceof EnumTypeDefinition) { + codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)superType ) ); + } else if (superType instanceof IntegerTypeDefinition) { + if (INT8_QNAME.equals(superType.getQName())) { + codec = INT8_DEFAULT_CODEC; + } else if (INT16_QNAME.equals(superType.getQName())) { + codec = INT16_DEFAULT_CODEC; + } else if (INT32_QNAME.equals(superType.getQName())) { + codec = INT32_DEFAULT_CODEC; + } else if (INT64_QNAME.equals(superType.getQName())) { + codec = INT64_DEFAULT_CODEC; + } + } else if (superType instanceof StringTypeDefinition) { + codec = STRING_DEFAULT_CODEC; + } else if (superType instanceof UnionTypeDefinition) { + codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)superType ) ); + } else if (superType instanceof UnsignedIntegerTypeDefinition) { + if (UINT8_QNAME.equals(superType.getQName())) { + codec = UINT8_DEFAULT_CODEC; + } + if (UINT16_QNAME.equals(superType.getQName())) { + codec = UINT16_DEFAULT_CODEC; + } + if (UINT32_QNAME.equals(superType.getQName())) { + codec = UINT32_DEFAULT_CODEC; + } + if (UINT64_QNAME.equals(superType.getQName())) { + codec = UINT64_DEFAULT_CODEC; + } + } + + return codec; } - public static class BooleanCodec extends TypeDefinitionAwareCodec { + public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec + implements BooleanCodec { - protected BooleanCodec(TypeDefinition typeDefinition) { - super(typeDefinition, Boolean.class); + protected BooleanCodecStringImpl(final Optional typeDef) { + super(typeDef, Boolean.class); } @Override - public String serialize(Boolean data) { - return data.toString(); + public String serialize(final Boolean data) { + return data == null ? "" : data.toString(); } @Override - public Boolean deserialize(String stringRepresentation) { - return Boolean.parseBoolean(stringRepresentation); + public Boolean deserialize(final String stringRepresentation) { + return Boolean.valueOf(stringRepresentation); } }; - public static class Uint8Codec extends TypeDefinitionAwareCodec { + public static class Uint8CodecStringImpl extends TypeDefinitionAwareCodec + implements Uint8Codec { - protected Uint8Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Short.class); + protected Uint8CodecStringImpl(final Optional typeDef) { + super(typeDef, Short.class); } @Override - public String serialize(Short data) { - return data.toString(); + public String serialize(final Short data) { + return data == null ? "" : data.toString(); } @Override - public Short deserialize(String stringRepresentation) { - return Short.parseShort(stringRepresentation); + public Short deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Short.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Short.valueOf(stringRepresentation, base); } }; - public static class Uint16Codec extends TypeDefinitionAwareCodec { - protected Uint16Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Integer.class); + public static class Uint16CodecStringImpl extends TypeDefinitionAwareCodec + implements Uint16Codec { + protected Uint16CodecStringImpl(final Optional typeDef) { + super(typeDef, Integer.class); + } + + @Override + public Integer deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Integer.valueOf(stringRepresentation, base); } @Override - public Integer deserialize(String stringRepresentation) { - return Integer.parseInt(stringRepresentation); + public String serialize(final Integer data) { + return data == null ? "" : data.toString(); } }; - public static class Uint32Codec extends TypeDefinitionAwareCodec { + public static class Uint32CodecStringImpl extends TypeDefinitionAwareCodec + implements Uint32Codec { - protected Uint32Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Long.class); + protected Uint32CodecStringImpl(final Optional typeDef) { + super(typeDef, Long.class); } @Override - public Long deserialize(String stringRepresentation) { - return Long.parseLong(stringRepresentation); + public Long deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Long.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Long.valueOf(stringRepresentation, base); + } + + @Override + public String serialize(final Long data) { + return data == null ? "" : data.toString(); } }; - public static class Uint64Codec extends TypeDefinitionAwareCodec { + public static class Uint64CodecStringImpl extends + TypeDefinitionAwareCodec implements Uint64Codec { - protected Uint64Codec(TypeDefinition typeDefinition) { - super(typeDefinition, BigInteger.class); + protected Uint64CodecStringImpl(final Optional typeDef) { + super(typeDef, BigInteger.class); + } + + @Override + public BigInteger deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return new BigInteger(normalizeHexadecimal(stringRepresentation), base); + } + return new BigInteger(stringRepresentation, base); } @Override - public BigInteger deserialize(String stringRepresentation) { - // FIXME: Implement codec correctly - return BigInteger.valueOf(Long.valueOf(stringRepresentation)); + public String serialize(final BigInteger data) { + return data == null ? "" : data.toString(); } }; - public static class StringCodec extends TypeDefinitionAwareCodec { + public static class StringCodecStringImpl extends TypeDefinitionAwareCodec implements + StringCodec { - protected StringCodec(TypeDefinition typeDefinition) { - super(typeDefinition, String.class); + protected StringCodecStringImpl(final Optional typeDef) { + super(typeDef, String.class); } @Override - public String deserialize(String stringRepresentation) { - return stringRepresentation; + public String deserialize(final String stringRepresentation) { + return stringRepresentation == null ? "" : stringRepresentation; + } + + @Override + public String serialize(final String data) { + return data == null ? "" : data; } }; - public static class Int16Codec extends TypeDefinitionAwareCodec { + public static class Int16CodecStringImpl extends TypeDefinitionAwareCodec implements + Int16Codec { - protected Int16Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Short.class); + protected Int16CodecStringImpl(final Optional typeDef) { + super(typeDef, Short.class); } @Override - public Short deserialize(String stringRepresentation) { - return Short.valueOf(stringRepresentation); + public Short deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Short.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Short.valueOf(stringRepresentation, base); + } + + @Override + public String serialize(final Short data) { + return data == null ? "" : data.toString(); } }; - public static class Int32Codec extends TypeDefinitionAwareCodec { + public static class Int32CodecStringImpl extends TypeDefinitionAwareCodec implements + Int32Codec { - protected Int32Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Integer.class); + protected Int32CodecStringImpl(final Optional typeDef) { + super(typeDef, Integer.class); + } + + @Override + public Integer deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Integer.valueOf(stringRepresentation, base); } @Override - public Integer deserialize(String stringRepresentation) { - return Integer.valueOf(stringRepresentation); + public String serialize(final Integer data) { + return data == null ? "" : data.toString(); } }; - public static class Int64Codec extends TypeDefinitionAwareCodec { + public static class Int64CodecStringImpl extends TypeDefinitionAwareCodec implements + Int64Codec { - protected Int64Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Long.class); + protected Int64CodecStringImpl(final Optional typeDef) { + super(typeDef, Long.class); } @Override - public Long deserialize(String stringRepresentation) { - return Long.parseLong(stringRepresentation); + public Long deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Long.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Long.valueOf(stringRepresentation, base); + } + + @Override + public String serialize(final Long data) { + return data == null ? "" : data.toString(); } }; - public static class Int8Codec extends TypeDefinitionAwareCodec { + public static class Int8CodecStringImpl extends TypeDefinitionAwareCodec implements + Int8Codec { + + protected Int8CodecStringImpl(final Optional typeDef) { + super(typeDef, Byte.class); + } - protected Int8Codec(TypeDefinition typeDefinition) { - super(typeDefinition, Byte.class); + @Override + public Byte deserialize(final String stringRepresentation) { + int base = provideBase(stringRepresentation); + if (base == 16) { + return Byte.valueOf(normalizeHexadecimal(stringRepresentation), base); + } + return Byte.valueOf(stringRepresentation, base); } @Override - public Byte deserialize(String stringRepresentation) { - return Byte.parseByte(stringRepresentation); + public String serialize(final Byte data) { + return data == null ? "" : data.toString(); } }; - public static class EmptyCodec extends TypeDefinitionAwareCodec { + public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec implements + EmptyCodec { - protected EmptyCodec(TypeDefinition typeDefinition) { - super(typeDefinition, Void.class); + protected EmptyCodecStringImpl(final Optional typeDef) { + super(typeDef, Void.class); } @Override - public String serialize(Void data) { + public String serialize(final Void data) { return ""; } @Override - public Void deserialize(String stringRepresentation) { + public Void deserialize(final String stringRepresentation) { + Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ), + "The value must be empty" ); return null; } }; - public static final class BinaryCodec extends TypeDefinitionAwareCodec { + public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec + implements BinaryCodec { - protected BinaryCodec(TypeDefinition typeDefinition) { - super(typeDefinition, byte[].class); + protected BinaryCodecStringImpl(final Optional typeDef) { + super(typeDef, byte[].class); } @Override - public String serialize(byte[] data) { - // FIXME By YANG Spec - return null; + public String serialize(final byte[] data) { + return data == null ? "" : BaseEncoding.base64().encode(data); } @Override - public byte[] deserialize(String stringRepresentation) { - // FIXME By YANG Spec - return null; + public byte[] deserialize(final String stringRepresentation) { + return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation); } }; - public static final class BitsCodec extends TypeDefinitionAwareCodec> { + public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec, BitsTypeDefinition> + implements BitsCodec { + + public static final Joiner JOINER = Joiner.on(" ").skipNulls(); + public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults(); @SuppressWarnings("unchecked") - protected BitsCodec(TypeDefinition typeDefinition) { - super(typeDefinition, (Class>) ((Class) Set.class)); + protected BitsCodecStringImpl(final Optional typeDef) { + super(typeDef, (Class>) ((Class) Set.class)); } @Override - public String serialize(Set data) { - return Joiner.on(" ").join(data).toString(); + public String serialize(final Set data) { + return data == null ? "" : JOINER.join(data); } @Override - public Set deserialize(String stringRepresentation) { - String[] strings = stringRepresentation.split(" "); + public Set deserialize(final String stringRepresentation) { + if (stringRepresentation == null) { + return ImmutableSet.of(); + } + + Iterable strings = SPLITTER.split(stringRepresentation); + + if( getTypeDefinition().isPresent() ) { + Set allowedNames = Sets.newHashSet(); + for( BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) { + allowedNames.add( bit.getName() ); + } + + for( String bit: strings ) { + if( !allowedNames.contains( bit ) ) { + throw new IllegalArgumentException( + "Invalid value \"" + bit + "\" for bits type. Allowed values are: " + + allowedNames ); + } + } + } + return ImmutableSet.copyOf(strings); } }; - public static class EnumCodec extends TypeDefinitionAwareCodec { + public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec implements + EnumCodec { - protected EnumCodec(TypeDefinition typeDefinition) { - super(typeDefinition, String.class); + protected EnumCodecStringImpl(final Optional typeDef) { + super(typeDef, String.class); } @Override - public String deserialize(String stringRepresentation) { + public String deserialize(final String stringRepresentation) { + if( getTypeDefinition().isPresent() ) { + Set allowedNames = Sets.newHashSet(); + for( EnumPair pair: getTypeDefinition().get().getValues() ) { + allowedNames.add( pair.getName() ); + } + + if( !allowedNames.contains( stringRepresentation ) ) { + throw new IllegalArgumentException( + "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " + + allowedNames ); + } + } + return stringRepresentation; } + + @Override + public String serialize(final String data) { + return data == null ? "" : data; + } }; - public static class DecimalCodec extends TypeDefinitionAwareCodec { + public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec + implements DecimalCodec { - protected DecimalCodec(TypeDefinition typeDefinition) { - super(typeDefinition, BigDecimal.class); + protected DecimalCodecStringImpl(final Optional typeDef) { + super(typeDef, BigDecimal.class); } @Override - public String serialize(BigDecimal data) { - return data.toString(); + public String serialize(final BigDecimal data) { + return data == null ? "" : data.toString(); } @Override - public BigDecimal deserialize(String stringRepresentation) { + public BigDecimal deserialize(final String stringRepresentation) { + Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" ); return new BigDecimal(stringRepresentation); } }; + + public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec implements + UnionCodec { + + protected UnionCodecStringImpl(final Optional typeDef) { + super(typeDef, Object.class); + } + + @Override + public String serialize(final Object data) { + return data == null ? "" : data.toString(); + } + + @Override + public Object deserialize(final String stringRepresentation) { + if( getTypeDefinition().isPresent() ) { + boolean valid = false; + for( TypeDefinition type: getTypeDefinition().get().getTypes() ) { + TypeDefinitionAwareCodec> typeAwareCodec = from( type ); + if( typeAwareCodec == null ) { + // This is a type for which we have no codec (eg identity ref) so we'll say it's valid + // but we'll continue in case there's another type for which we do have a codec. + valid = true; + continue; + } + + try { + typeAwareCodec.deserialize( stringRepresentation ); + valid = true; + break; + } + catch( Exception e ) { + // invalid - try the next union type. + } + } + + if( !valid ) { + throw new IllegalArgumentException( + "Invalid value \"" + stringRepresentation + "\" for union type." ); + } + } + + return stringRepresentation; + } + }; }