2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.impl.codec;
10 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT16_QNAME;
11 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT32_QNAME;
12 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT64_QNAME;
13 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT8_QNAME;
14 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT16_QNAME;
15 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT32_QNAME;
16 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT64_QNAME;
17 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT8_QNAME;
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
25 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
26 import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
27 import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
28 import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
29 import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
30 import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
31 import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
32 import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
33 import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
34 import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
35 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
36 import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
37 import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
38 import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
39 import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
40 import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
41 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
42 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
49 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
54 import com.google.common.base.Joiner;
55 import com.google.common.base.Optional;
56 import com.google.common.base.Preconditions;
57 import com.google.common.base.Splitter;
58 import com.google.common.base.Strings;
59 import com.google.common.collect.ImmutableSet;
60 import com.google.common.collect.Sets;
61 import com.google.common.io.BaseEncoding;
63 public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> implements DataStringCodec<J> {
65 private static final Pattern intPattern = Pattern.compile("[+-]?[1-9][0-9]*$");
66 private static final Pattern hexPattern = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
67 private static final Pattern octalPattern = Pattern.compile("[+-]?0[1-7][0-7]*$");
69 private final Optional<T> typeDefinition;
70 private final Class<J> inputClass;
72 private static final int provideBase(final String integer) {
73 if (integer == null) {
74 throw new IllegalArgumentException("String representing integer number cannot be NULL");
77 if ((integer.length() == 1) && (integer.charAt(0) == '0')) {
81 final Matcher intMatcher = intPattern.matcher(integer);
82 if (intMatcher.matches()) {
85 final Matcher hexMatcher = hexPattern.matcher(integer);
86 if (hexMatcher.matches()) {
89 final Matcher octMatcher = octalPattern.matcher(integer);
90 if (octMatcher.matches()) {
93 String formatedMessage = String.format("Incorrect lexical representation of integer value: %s."
94 + "\nAn integer value can be defined as: "
95 + "\n - a decimal number,"
96 + "\n - a hexadecimal number (prefix 0x),"
97 + "\n - an octal number (prefix 0)."
98 + "\nSigned values are allowed. Spaces between digits are NOT allowed.", integer);
99 throw new NumberFormatException(formatedMessage);
105 private static String normalizeHexadecimal(final String hexInt) {
106 if (hexInt == null) {
107 throw new IllegalArgumentException(
108 "String representing integer number in Hexadecimal format cannot be NULL!");
110 final String normalizedString;
111 if (hexInt.contains("x")) {
112 normalizedString = hexInt.replace("x", "");
113 } else if (hexInt.contains("X")) {
114 normalizedString = hexInt.replace("X", "");
116 normalizedString = hexInt;
118 return normalizedString;
121 private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
122 Optional.<BinaryTypeDefinition> absent());
124 private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
125 Optional.<BooleanTypeDefinition> absent());
127 private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
128 Optional.<DecimalTypeDefinition> absent());
130 private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
131 Optional.<EmptyTypeDefinition> absent());
133 private static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl(
134 Optional.<IntegerTypeDefinition> absent());
136 private static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl(
137 Optional.<IntegerTypeDefinition> absent());
139 private static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl(
140 Optional.<IntegerTypeDefinition> absent());
142 private static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl(
143 Optional.<IntegerTypeDefinition> absent());
145 private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
146 Optional.<StringTypeDefinition> absent());
148 private static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl(
149 Optional.<UnsignedIntegerTypeDefinition> absent());
151 private static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl(
152 Optional.<UnsignedIntegerTypeDefinition> absent());
154 private static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl(
155 Optional.<UnsignedIntegerTypeDefinition> absent());
157 private static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl(
158 Optional.<UnsignedIntegerTypeDefinition> absent());
161 public Class<J> getInputClass() {
165 protected TypeDefinitionAwareCodec(Optional<T> typeDefinition, Class<J> outputClass) {
166 Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
167 this.typeDefinition = typeDefinition;
168 this.inputClass = outputClass;
171 public Optional<T> getTypeDefinition() {
172 return typeDefinition;
175 @SuppressWarnings({ "rawtypes", "unchecked" })
176 public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(TypeDefinition typeDefinition) {
177 return fromType(typeDefinition);
180 @SuppressWarnings("unchecked")
181 public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(T typeDefinition) {
182 T superType = typeDefinition;
183 while (superType.getBaseType() != null) {
184 superType = superType.getBaseType();
187 @SuppressWarnings("rawtypes")
188 TypeDefinitionAwareCodec codec = null;
190 if (superType instanceof BinaryTypeDefinition) {
191 codec = BINARY_DEFAULT_CODEC;
192 } else if (superType instanceof BitsTypeDefinition) {
193 codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)superType ) );
194 } else if (superType instanceof BooleanTypeDefinition) {
195 codec = BOOLEAN_DEFAULT_CODEC;
196 } else if (superType instanceof DecimalTypeDefinition) {
197 codec = DECIMAL64_DEFAULT_CODEC;
198 } else if (superType instanceof EmptyTypeDefinition) {
199 codec = EMPTY_DEFAULT_CODEC;
200 } else if (superType instanceof EnumTypeDefinition) {
201 codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)superType ) );
202 } else if (superType instanceof IntegerTypeDefinition) {
203 if (INT8_QNAME.equals(superType.getQName())) {
204 codec = INT8_DEFAULT_CODEC;
205 } else if (INT16_QNAME.equals(superType.getQName())) {
206 codec = INT16_DEFAULT_CODEC;
207 } else if (INT32_QNAME.equals(superType.getQName())) {
208 codec = INT32_DEFAULT_CODEC;
209 } else if (INT64_QNAME.equals(superType.getQName())) {
210 codec = INT64_DEFAULT_CODEC;
212 } else if (superType instanceof StringTypeDefinition) {
213 codec = STRING_DEFAULT_CODEC;
214 } else if (superType instanceof UnionTypeDefinition) {
215 codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)superType ) );
216 } else if (superType instanceof UnsignedIntegerTypeDefinition) {
217 if (UINT8_QNAME.equals(superType.getQName())) {
218 codec = UINT8_DEFAULT_CODEC;
220 if (UINT16_QNAME.equals(superType.getQName())) {
221 codec = UINT16_DEFAULT_CODEC;
223 if (UINT32_QNAME.equals(superType.getQName())) {
224 codec = UINT32_DEFAULT_CODEC;
226 if (UINT64_QNAME.equals(superType.getQName())) {
227 codec = UINT64_DEFAULT_CODEC;
234 public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
235 implements BooleanCodec<String> {
237 protected BooleanCodecStringImpl(Optional<BooleanTypeDefinition> typeDef) {
238 super(typeDef, Boolean.class);
242 public String serialize(Boolean data) {
243 return data == null ? "" : data.toString();
247 public Boolean deserialize(String stringRepresentation) {
248 return Boolean.valueOf(stringRepresentation);
252 public static class Uint8CodecStringImpl extends TypeDefinitionAwareCodec<Short, UnsignedIntegerTypeDefinition>
253 implements Uint8Codec<String> {
255 protected Uint8CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
256 super(typeDef, Short.class);
260 public String serialize(Short data) {
261 return data == null ? "" : data.toString();
265 public Short deserialize(String stringRepresentation) {
266 int base = provideBase(stringRepresentation);
268 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
270 return Short.valueOf(stringRepresentation, base);
274 public static class Uint16CodecStringImpl extends TypeDefinitionAwareCodec<Integer, UnsignedIntegerTypeDefinition>
275 implements Uint16Codec<String> {
276 protected Uint16CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
277 super(typeDef, Integer.class);
281 public Integer deserialize(String stringRepresentation) {
282 int base = provideBase(stringRepresentation);
284 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
286 return Integer.valueOf(stringRepresentation, base);
290 public String serialize(Integer data) {
291 return data == null ? "" : data.toString();
295 public static class Uint32CodecStringImpl extends TypeDefinitionAwareCodec<Long, UnsignedIntegerTypeDefinition>
296 implements Uint32Codec<String> {
298 protected Uint32CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
299 super(typeDef, Long.class);
303 public Long deserialize(String stringRepresentation) {
304 int base = provideBase(stringRepresentation);
306 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
308 return Long.valueOf(stringRepresentation, base);
312 public String serialize(Long data) {
313 return data == null ? "" : data.toString();
317 public static class Uint64CodecStringImpl extends
318 TypeDefinitionAwareCodec<BigInteger, UnsignedIntegerTypeDefinition> implements Uint64Codec<String> {
320 protected Uint64CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
321 super(typeDef, BigInteger.class);
325 public BigInteger deserialize(String stringRepresentation) {
326 int base = provideBase(stringRepresentation);
328 return new BigInteger(normalizeHexadecimal(stringRepresentation), base);
330 return new BigInteger(stringRepresentation, base);
334 public String serialize(BigInteger data) {
335 return data == null ? "" : data.toString();
339 public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
340 StringCodec<String> {
342 protected StringCodecStringImpl(Optional<StringTypeDefinition> typeDef) {
343 super(typeDef, String.class);
347 public String deserialize(String stringRepresentation) {
348 return stringRepresentation == null ? "" :stringRepresentation;
352 public String serialize(String data) {
353 return data == null ? "" : data.toString();
357 public static class Int16CodecStringImpl extends TypeDefinitionAwareCodec<Short, IntegerTypeDefinition> implements
360 protected Int16CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
361 super(typeDef, Short.class);
365 public Short deserialize(String stringRepresentation) {
366 int base = provideBase(stringRepresentation);
368 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
370 return Short.valueOf(stringRepresentation, base);
374 public String serialize(Short data) {
375 return data == null ? "" : data.toString();
379 public static class Int32CodecStringImpl extends TypeDefinitionAwareCodec<Integer, IntegerTypeDefinition> implements
382 protected Int32CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
383 super(typeDef, Integer.class);
387 public Integer deserialize(String stringRepresentation) {
388 int base = provideBase(stringRepresentation);
390 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
392 return Integer.valueOf(stringRepresentation, base);
396 public String serialize(Integer data) {
397 return data == null ? "" : data.toString();
401 public static class Int64CodecStringImpl extends TypeDefinitionAwareCodec<Long, IntegerTypeDefinition> implements
404 protected Int64CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
405 super(typeDef, Long.class);
409 public Long deserialize(String stringRepresentation) {
410 int base = provideBase(stringRepresentation);
412 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
414 return Long.valueOf(stringRepresentation, base);
418 public String serialize(Long data) {
419 return data == null ? "" : data.toString();
423 public static class Int8CodecStringImpl extends TypeDefinitionAwareCodec<Byte, IntegerTypeDefinition> implements
426 protected Int8CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
427 super(typeDef, Byte.class);
431 public Byte deserialize(String stringRepresentation) {
432 int base = provideBase(stringRepresentation);
434 return Byte.valueOf(normalizeHexadecimal(stringRepresentation), base);
436 return Byte.valueOf(stringRepresentation, base);
440 public String serialize(Byte data) {
441 return data == null ? "" : data.toString();
445 public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
448 protected EmptyCodecStringImpl(Optional<EmptyTypeDefinition> typeDef) {
449 super(typeDef, Void.class);
453 public String serialize(Void data) {
458 public Void deserialize(String stringRepresentation) {
459 Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ),
460 "The value must be empty" );
465 public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
466 implements BinaryCodec<String> {
468 protected BinaryCodecStringImpl(Optional<BinaryTypeDefinition> typeDef) {
469 super(typeDef, byte[].class);
473 public String serialize(byte[] data) {
474 return data == null ? "" : BaseEncoding.base64().encode(data);
478 public byte[] deserialize(String stringRepresentation) {
479 return stringRepresentation == null ? null :
480 BaseEncoding.base64().decode(stringRepresentation);
484 public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
485 implements BitsCodec<String> {
487 public static final Joiner JOINER = Joiner.on(" ").skipNulls();
488 public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
490 @SuppressWarnings("unchecked")
491 protected BitsCodecStringImpl(Optional<BitsTypeDefinition> typeDef) {
492 super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
496 public String serialize(Set<String> data) {
497 return data == null ? "" : JOINER.join(data);
501 public Set<String> deserialize(String stringRepresentation) {
502 if (stringRepresentation == null) {
503 return ImmutableSet.of();
506 Iterable<String> strings = SPLITTER.split(stringRepresentation);
508 if( getTypeDefinition().isPresent() ) {
509 Set<String> allowedNames = Sets.newHashSet();
510 for( BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) {
511 allowedNames.add( bit.getName() );
514 for( String bit: strings ) {
515 if( !allowedNames.contains( bit ) ) {
516 throw new IllegalArgumentException(
517 "Invalid value \"" + bit + "\" for bits type. Allowed values are: " +
523 return ImmutableSet.copyOf(strings);
527 public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
530 protected EnumCodecStringImpl(Optional<EnumTypeDefinition> typeDef) {
531 super(typeDef, String.class);
535 public String deserialize(String stringRepresentation) {
536 if( getTypeDefinition().isPresent() ) {
537 Set<String> allowedNames = Sets.newHashSet();
538 for( EnumPair pair: getTypeDefinition().get().getValues() ) {
539 allowedNames.add( pair.getName() );
542 if( !allowedNames.contains( stringRepresentation ) ) {
543 throw new IllegalArgumentException(
544 "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " +
549 return stringRepresentation;
553 public String serialize(String data) {
554 return data == null ? "" : data;
558 public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
559 implements DecimalCodec<String> {
561 protected DecimalCodecStringImpl(Optional<DecimalTypeDefinition> typeDef) {
562 super(typeDef, BigDecimal.class);
566 public String serialize(BigDecimal data) {
567 return data == null ? "" : data.toString();
571 public BigDecimal deserialize(String stringRepresentation) {
572 Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" );
573 return new BigDecimal(stringRepresentation);
577 public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition> implements
580 protected UnionCodecStringImpl(Optional<UnionTypeDefinition> typeDef) {
581 super(typeDef, Object.class);
585 public String serialize(Object data) {
586 return data == null ? "" : data.toString();
590 public Object deserialize(String stringRepresentation) {
591 Object returnValue = stringRepresentation;
592 if( getTypeDefinition().isPresent() ) {
593 boolean valid = false;
594 for( TypeDefinition<?> type: getTypeDefinition().get().getTypes() ) {
595 TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwareCodec = from( type );
596 if( typeAwareCodec == null ) {
597 // This is a type for which we have no codec (eg identity ref) so we'll say it's valid
598 // but we'll continue in case there's another type for which we do have a codec.
604 returnValue = typeAwareCodec.deserialize( stringRepresentation );
608 catch( Exception e ) {
609 // invalid - try the next union type.
614 throw new IllegalArgumentException(
615 "Invalid value \"" + stringRepresentation + "\" for union type." );