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 com.google.common.base.CharMatcher;
20 import com.google.common.base.Joiner;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import com.google.common.base.Splitter;
24 import com.google.common.base.Strings;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.collect.Sets;
27 import com.google.common.io.BaseEncoding;
28 import java.math.BigDecimal;
29 import java.math.BigInteger;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import javax.xml.bind.DatatypeConverter;
34 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
35 import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
36 import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
37 import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
38 import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
39 import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
40 import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
41 import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
42 import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
43 import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
44 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
45 import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
46 import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
47 import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
48 import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
49 import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
50 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
53 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
55 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
58 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
59 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
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 // For up to two characters, this is very fast
70 private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX");
72 private final Optional<T> typeDefinition;
73 private final Class<J> inputClass;
75 private static final int provideBase(final String integer) {
76 if (integer == null) {
77 throw new IllegalArgumentException("String representing integer number cannot be NULL");
80 if ((integer.length() == 1) && (integer.charAt(0) == '0')) {
84 final Matcher intMatcher = intPattern.matcher(integer);
85 if (intMatcher.matches()) {
88 final Matcher hexMatcher = hexPattern.matcher(integer);
89 if (hexMatcher.matches()) {
92 final Matcher octMatcher = octalPattern.matcher(integer);
93 if (octMatcher.matches()) {
96 String formatedMessage = String.format("Incorrect lexical representation of integer value: %s."
97 + "%nAn integer value can be defined as: "
98 + "%n - a decimal number,"
99 + "%n - a hexadecimal number (prefix 0x),"
100 + "%n - an octal number (prefix 0)."
101 + "%nSigned values are allowed. Spaces between digits are NOT allowed.", integer);
102 throw new NumberFormatException(formatedMessage);
108 private static String normalizeHexadecimal(final String hexInt) {
109 if (hexInt == null) {
110 throw new IllegalArgumentException(
111 "String representing integer number in Hexadecimal format cannot be NULL!");
114 return X_MATCHER.removeFrom(hexInt);
117 private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
118 Optional.<BinaryTypeDefinition> absent());
120 private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
121 Optional.<BooleanTypeDefinition> absent());
123 private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
124 Optional.<DecimalTypeDefinition> absent());
126 private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
127 Optional.<EmptyTypeDefinition> absent());
129 private static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl(
130 Optional.<IntegerTypeDefinition> absent());
132 private static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl(
133 Optional.<IntegerTypeDefinition> absent());
135 private static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl(
136 Optional.<IntegerTypeDefinition> absent());
138 private static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl(
139 Optional.<IntegerTypeDefinition> absent());
141 private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
142 Optional.<StringTypeDefinition> absent());
144 private static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl(
145 Optional.<UnsignedIntegerTypeDefinition> absent());
147 private static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl(
148 Optional.<UnsignedIntegerTypeDefinition> absent());
150 private static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl(
151 Optional.<UnsignedIntegerTypeDefinition> absent());
153 private static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl(
154 Optional.<UnsignedIntegerTypeDefinition> absent());
157 public Class<J> getInputClass() {
161 protected TypeDefinitionAwareCodec(final Optional<T> typeDefinition, final Class<J> outputClass) {
162 Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
163 this.typeDefinition = typeDefinition;
164 this.inputClass = outputClass;
167 public Optional<T> getTypeDefinition() {
168 return typeDefinition;
171 @SuppressWarnings({ "rawtypes", "unchecked" })
172 public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(final TypeDefinition typeDefinition) {
173 return fromType(typeDefinition);
176 @SuppressWarnings("unchecked")
177 public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(final T typeDefinition) {
178 T superType = typeDefinition;
179 while (superType.getBaseType() != null) {
180 superType = superType.getBaseType();
183 @SuppressWarnings("rawtypes")
184 TypeDefinitionAwareCodec codec = null;
186 if (superType instanceof BinaryTypeDefinition) {
187 codec = BINARY_DEFAULT_CODEC;
188 } else if (superType instanceof BitsTypeDefinition) {
189 codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)superType ) );
190 } else if (superType instanceof BooleanTypeDefinition) {
191 codec = BOOLEAN_DEFAULT_CODEC;
192 } else if (superType instanceof DecimalTypeDefinition) {
193 codec = DECIMAL64_DEFAULT_CODEC;
194 } else if (superType instanceof EmptyTypeDefinition) {
195 codec = EMPTY_DEFAULT_CODEC;
196 } else if (superType instanceof EnumTypeDefinition) {
197 codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)superType ) );
198 } else if (superType instanceof IntegerTypeDefinition) {
199 if (INT8_QNAME.equals(superType.getQName())) {
200 codec = INT8_DEFAULT_CODEC;
201 } else if (INT16_QNAME.equals(superType.getQName())) {
202 codec = INT16_DEFAULT_CODEC;
203 } else if (INT32_QNAME.equals(superType.getQName())) {
204 codec = INT32_DEFAULT_CODEC;
205 } else if (INT64_QNAME.equals(superType.getQName())) {
206 codec = INT64_DEFAULT_CODEC;
208 } else if (superType instanceof StringTypeDefinition) {
209 codec = STRING_DEFAULT_CODEC;
210 } else if (superType instanceof UnionTypeDefinition) {
211 codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)superType ) );
212 } else if (superType instanceof UnsignedIntegerTypeDefinition) {
213 if (UINT8_QNAME.equals(superType.getQName())) {
214 codec = UINT8_DEFAULT_CODEC;
216 if (UINT16_QNAME.equals(superType.getQName())) {
217 codec = UINT16_DEFAULT_CODEC;
219 if (UINT32_QNAME.equals(superType.getQName())) {
220 codec = UINT32_DEFAULT_CODEC;
222 if (UINT64_QNAME.equals(superType.getQName())) {
223 codec = UINT64_DEFAULT_CODEC;
230 public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
231 implements BooleanCodec<String> {
233 protected BooleanCodecStringImpl(final Optional<BooleanTypeDefinition> typeDef) {
234 super(typeDef, Boolean.class);
238 public String serialize(final Boolean data) {
239 return data == null ? "" : data.toString();
243 public Boolean deserialize(final String stringRepresentation) {
244 return Boolean.valueOf(stringRepresentation);
248 public static class Uint8CodecStringImpl extends TypeDefinitionAwareCodec<Short, UnsignedIntegerTypeDefinition>
249 implements Uint8Codec<String> {
251 protected Uint8CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
252 super(typeDef, Short.class);
256 public String serialize(final Short data) {
257 return data == null ? "" : data.toString();
261 public Short deserialize(final String stringRepresentation) {
262 int base = provideBase(stringRepresentation);
264 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
266 return Short.valueOf(stringRepresentation, base);
270 public static class Uint16CodecStringImpl extends TypeDefinitionAwareCodec<Integer, UnsignedIntegerTypeDefinition>
271 implements Uint16Codec<String> {
272 protected Uint16CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
273 super(typeDef, Integer.class);
277 public Integer deserialize(final String stringRepresentation) {
278 int base = provideBase(stringRepresentation);
280 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
282 return Integer.valueOf(stringRepresentation, base);
286 public String serialize(final Integer data) {
287 return data == null ? "" : data.toString();
291 public static class Uint32CodecStringImpl extends TypeDefinitionAwareCodec<Long, UnsignedIntegerTypeDefinition>
292 implements Uint32Codec<String> {
294 protected Uint32CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
295 super(typeDef, Long.class);
299 public Long deserialize(final String stringRepresentation) {
300 int base = provideBase(stringRepresentation);
302 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
304 return Long.valueOf(stringRepresentation, base);
308 public String serialize(final Long data) {
309 return data == null ? "" : data.toString();
313 public static class Uint64CodecStringImpl extends
314 TypeDefinitionAwareCodec<BigInteger, UnsignedIntegerTypeDefinition> implements Uint64Codec<String> {
316 protected Uint64CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
317 super(typeDef, BigInteger.class);
321 public BigInteger deserialize(final String stringRepresentation) {
322 int base = provideBase(stringRepresentation);
324 return new BigInteger(normalizeHexadecimal(stringRepresentation), base);
326 return new BigInteger(stringRepresentation, base);
330 public String serialize(final BigInteger data) {
331 return data == null ? "" : data.toString();
335 public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
336 StringCodec<String> {
338 protected StringCodecStringImpl(final Optional<StringTypeDefinition> typeDef) {
339 super(typeDef, String.class);
343 public String deserialize(final String stringRepresentation) {
344 return stringRepresentation == null ? "" : stringRepresentation;
348 public String serialize(final String data) {
349 return data == null ? "" : data;
353 public static class Int16CodecStringImpl extends TypeDefinitionAwareCodec<Short, IntegerTypeDefinition> implements
356 protected Int16CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
357 super(typeDef, Short.class);
361 public Short deserialize(final String stringRepresentation) {
362 int base = provideBase(stringRepresentation);
364 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
366 return Short.valueOf(stringRepresentation, base);
370 public String serialize(final Short data) {
371 return data == null ? "" : data.toString();
375 public static class Int32CodecStringImpl extends TypeDefinitionAwareCodec<Integer, IntegerTypeDefinition> implements
378 protected Int32CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
379 super(typeDef, Integer.class);
383 public Integer deserialize(final String stringRepresentation) {
384 int base = provideBase(stringRepresentation);
386 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
388 return Integer.valueOf(stringRepresentation, base);
392 public String serialize(final Integer data) {
393 return data == null ? "" : data.toString();
397 public static class Int64CodecStringImpl extends TypeDefinitionAwareCodec<Long, IntegerTypeDefinition> implements
400 protected Int64CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
401 super(typeDef, Long.class);
405 public Long deserialize(final String stringRepresentation) {
406 int base = provideBase(stringRepresentation);
408 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
410 return Long.valueOf(stringRepresentation, base);
414 public String serialize(final Long data) {
415 return data == null ? "" : data.toString();
419 public static class Int8CodecStringImpl extends TypeDefinitionAwareCodec<Byte, IntegerTypeDefinition> implements
422 protected Int8CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
423 super(typeDef, Byte.class);
427 public Byte deserialize(final String stringRepresentation) {
428 int base = provideBase(stringRepresentation);
430 return Byte.valueOf(normalizeHexadecimal(stringRepresentation), base);
432 return Byte.valueOf(stringRepresentation, base);
436 public String serialize(final Byte data) {
437 return data == null ? "" : data.toString();
441 public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
444 protected EmptyCodecStringImpl(final Optional<EmptyTypeDefinition> typeDef) {
445 super(typeDef, Void.class);
449 public String serialize(final Void data) {
454 public Void deserialize(final String stringRepresentation) {
455 Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ),
456 "The value must be empty" );
461 public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
462 implements BinaryCodec<String> {
464 protected BinaryCodecStringImpl(final Optional<BinaryTypeDefinition> typeDef) {
465 super(typeDef, byte[].class);
469 public String serialize(final byte[] data) {
470 return data == null ? "" : BaseEncoding.base64().encode(data);
474 public byte[] deserialize(final String stringRepresentation) {
475 return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation);
479 public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
480 implements BitsCodec<String> {
482 public static final Joiner JOINER = Joiner.on(" ").skipNulls();
483 public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
485 @SuppressWarnings("unchecked")
486 protected BitsCodecStringImpl(final Optional<BitsTypeDefinition> typeDef) {
487 super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
491 public String serialize(final Set<String> data) {
492 return data == null ? "" : JOINER.join(data);
496 public Set<String> deserialize(final String stringRepresentation) {
497 if (stringRepresentation == null) {
498 return ImmutableSet.of();
501 Iterable<String> strings = SPLITTER.split(stringRepresentation);
503 if( getTypeDefinition().isPresent() ) {
504 Set<String> allowedNames = Sets.newHashSet();
505 for( BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) {
506 allowedNames.add( bit.getName() );
509 for( String bit: strings ) {
510 if( !allowedNames.contains( bit ) ) {
511 throw new IllegalArgumentException(
512 "Invalid value \"" + bit + "\" for bits type. Allowed values are: " +
518 return ImmutableSet.copyOf(strings);
522 public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
525 protected EnumCodecStringImpl(final Optional<EnumTypeDefinition> typeDef) {
526 super(typeDef, String.class);
530 public String deserialize(final String stringRepresentation) {
531 if( getTypeDefinition().isPresent() ) {
532 Set<String> allowedNames = Sets.newHashSet();
533 for( EnumPair pair: getTypeDefinition().get().getValues() ) {
534 allowedNames.add( pair.getName() );
537 if( !allowedNames.contains( stringRepresentation ) ) {
538 throw new IllegalArgumentException(
539 "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " +
544 return stringRepresentation;
548 public String serialize(final String data) {
549 return data == null ? "" : data;
553 public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
554 implements DecimalCodec<String> {
556 protected DecimalCodecStringImpl(final Optional<DecimalTypeDefinition> typeDef) {
557 super(typeDef, BigDecimal.class);
561 public String serialize(final BigDecimal data) {
562 return data == null ? "" : data.toString();
566 public BigDecimal deserialize(final String stringRepresentation) {
567 Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" );
568 return new BigDecimal(stringRepresentation);
572 public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition> implements
575 protected UnionCodecStringImpl(final Optional<UnionTypeDefinition> typeDef) {
576 super(typeDef, Object.class);
580 public String serialize(final Object data) {
581 return data == null ? "" : data.toString();
585 public Object deserialize(final String stringRepresentation) {
586 if( getTypeDefinition().isPresent() ) {
587 boolean valid = false;
588 for( TypeDefinition<?> type: getTypeDefinition().get().getTypes() ) {
589 TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwareCodec = from( type );
590 if( typeAwareCodec == null ) {
591 // This is a type for which we have no codec (eg identity ref) so we'll say it's valid
592 // but we'll continue in case there's another type for which we do have a codec.
598 typeAwareCodec.deserialize( stringRepresentation );
602 catch( Exception e ) {
603 // invalid - try the next union type.
608 throw new IllegalArgumentException(
609 "Invalid value \"" + stringRepresentation + "\" for union type." );
613 return stringRepresentation;