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;
29 import java.math.BigDecimal;
30 import java.math.BigInteger;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
35 import javax.xml.bind.DatatypeConverter;
37 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
38 import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
39 import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
40 import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
41 import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
42 import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
43 import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
44 import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
45 import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
46 import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
47 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
48 import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
49 import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
50 import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
51 import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
52 import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
53 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
55 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
58 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
59 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
61 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
66 public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> implements DataStringCodec<J> {
68 private static final Pattern intPattern = Pattern.compile("[+-]?[1-9][0-9]*$");
69 private static final Pattern hexPattern = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
70 private static final Pattern octalPattern = Pattern.compile("[+-]?0[1-7][0-7]*$");
72 // For up to two characters, this is very fast
73 private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX");
75 private final Optional<T> typeDefinition;
76 private final Class<J> inputClass;
78 private static final int provideBase(final String integer) {
79 if (integer == null) {
80 throw new IllegalArgumentException("String representing integer number cannot be NULL");
83 if ((integer.length() == 1) && (integer.charAt(0) == '0')) {
87 final Matcher intMatcher = intPattern.matcher(integer);
88 if (intMatcher.matches()) {
91 final Matcher hexMatcher = hexPattern.matcher(integer);
92 if (hexMatcher.matches()) {
95 final Matcher octMatcher = octalPattern.matcher(integer);
96 if (octMatcher.matches()) {
99 String formatedMessage = String.format("Incorrect lexical representation of integer value: %s."
100 + "\nAn integer value can be defined as: "
101 + "\n - a decimal number,"
102 + "\n - a hexadecimal number (prefix 0x),"
103 + "\n - an octal number (prefix 0)."
104 + "\nSigned values are allowed. Spaces between digits are NOT allowed.", integer);
105 throw new NumberFormatException(formatedMessage);
111 private static String normalizeHexadecimal(final String hexInt) {
112 if (hexInt == null) {
113 throw new IllegalArgumentException(
114 "String representing integer number in Hexadecimal format cannot be NULL!");
117 return X_MATCHER.removeFrom(hexInt);
120 private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
121 Optional.<BinaryTypeDefinition> absent());
123 private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
124 Optional.<BooleanTypeDefinition> absent());
126 private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
127 Optional.<DecimalTypeDefinition> absent());
129 private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
130 Optional.<EmptyTypeDefinition> absent());
132 private static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl(
133 Optional.<IntegerTypeDefinition> absent());
135 private static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl(
136 Optional.<IntegerTypeDefinition> absent());
138 private static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl(
139 Optional.<IntegerTypeDefinition> absent());
141 private static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl(
142 Optional.<IntegerTypeDefinition> absent());
144 private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
145 Optional.<StringTypeDefinition> absent());
147 private static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl(
148 Optional.<UnsignedIntegerTypeDefinition> absent());
150 private static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl(
151 Optional.<UnsignedIntegerTypeDefinition> absent());
153 private static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl(
154 Optional.<UnsignedIntegerTypeDefinition> absent());
156 private static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl(
157 Optional.<UnsignedIntegerTypeDefinition> absent());
160 public Class<J> getInputClass() {
164 protected TypeDefinitionAwareCodec(final Optional<T> typeDefinition, final Class<J> outputClass) {
165 Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
166 this.typeDefinition = typeDefinition;
167 this.inputClass = outputClass;
170 public Optional<T> getTypeDefinition() {
171 return typeDefinition;
174 @SuppressWarnings({ "rawtypes", "unchecked" })
175 public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(final TypeDefinition typeDefinition) {
176 return fromType(typeDefinition);
179 @SuppressWarnings("unchecked")
180 public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(final T typeDefinition) {
181 T superType = typeDefinition;
182 while (superType.getBaseType() != null) {
183 superType = superType.getBaseType();
186 @SuppressWarnings("rawtypes")
187 TypeDefinitionAwareCodec codec = null;
189 if (superType instanceof BinaryTypeDefinition) {
190 codec = BINARY_DEFAULT_CODEC;
191 } else if (superType instanceof BitsTypeDefinition) {
192 codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)superType ) );
193 } else if (superType instanceof BooleanTypeDefinition) {
194 codec = BOOLEAN_DEFAULT_CODEC;
195 } else if (superType instanceof DecimalTypeDefinition) {
196 codec = DECIMAL64_DEFAULT_CODEC;
197 } else if (superType instanceof EmptyTypeDefinition) {
198 codec = EMPTY_DEFAULT_CODEC;
199 } else if (superType instanceof EnumTypeDefinition) {
200 codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)superType ) );
201 } else if (superType instanceof IntegerTypeDefinition) {
202 if (INT8_QNAME.equals(superType.getQName())) {
203 codec = INT8_DEFAULT_CODEC;
204 } else if (INT16_QNAME.equals(superType.getQName())) {
205 codec = INT16_DEFAULT_CODEC;
206 } else if (INT32_QNAME.equals(superType.getQName())) {
207 codec = INT32_DEFAULT_CODEC;
208 } else if (INT64_QNAME.equals(superType.getQName())) {
209 codec = INT64_DEFAULT_CODEC;
211 } else if (superType instanceof StringTypeDefinition) {
212 codec = STRING_DEFAULT_CODEC;
213 } else if (superType instanceof UnionTypeDefinition) {
214 codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)superType ) );
215 } else if (superType instanceof UnsignedIntegerTypeDefinition) {
216 if (UINT8_QNAME.equals(superType.getQName())) {
217 codec = UINT8_DEFAULT_CODEC;
219 if (UINT16_QNAME.equals(superType.getQName())) {
220 codec = UINT16_DEFAULT_CODEC;
222 if (UINT32_QNAME.equals(superType.getQName())) {
223 codec = UINT32_DEFAULT_CODEC;
225 if (UINT64_QNAME.equals(superType.getQName())) {
226 codec = UINT64_DEFAULT_CODEC;
233 public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
234 implements BooleanCodec<String> {
236 protected BooleanCodecStringImpl(final Optional<BooleanTypeDefinition> typeDef) {
237 super(typeDef, Boolean.class);
241 public String serialize(final Boolean data) {
242 return data == null ? "" : data.toString();
246 public Boolean deserialize(final String stringRepresentation) {
247 return Boolean.valueOf(stringRepresentation);
251 public static class Uint8CodecStringImpl extends TypeDefinitionAwareCodec<Short, UnsignedIntegerTypeDefinition>
252 implements Uint8Codec<String> {
254 protected Uint8CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
255 super(typeDef, Short.class);
259 public String serialize(final Short data) {
260 return data == null ? "" : data.toString();
264 public Short deserialize(final String stringRepresentation) {
265 int base = provideBase(stringRepresentation);
267 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
269 return Short.valueOf(stringRepresentation, base);
273 public static class Uint16CodecStringImpl extends TypeDefinitionAwareCodec<Integer, UnsignedIntegerTypeDefinition>
274 implements Uint16Codec<String> {
275 protected Uint16CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
276 super(typeDef, Integer.class);
280 public Integer deserialize(final String stringRepresentation) {
281 int base = provideBase(stringRepresentation);
283 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
285 return Integer.valueOf(stringRepresentation, base);
289 public String serialize(final Integer data) {
290 return data == null ? "" : data.toString();
294 public static class Uint32CodecStringImpl extends TypeDefinitionAwareCodec<Long, UnsignedIntegerTypeDefinition>
295 implements Uint32Codec<String> {
297 protected Uint32CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
298 super(typeDef, Long.class);
302 public Long deserialize(final String stringRepresentation) {
303 int base = provideBase(stringRepresentation);
305 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
307 return Long.valueOf(stringRepresentation, base);
311 public String serialize(final Long data) {
312 return data == null ? "" : data.toString();
316 public static class Uint64CodecStringImpl extends
317 TypeDefinitionAwareCodec<BigInteger, UnsignedIntegerTypeDefinition> implements Uint64Codec<String> {
319 protected Uint64CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
320 super(typeDef, BigInteger.class);
324 public BigInteger deserialize(final String stringRepresentation) {
325 int base = provideBase(stringRepresentation);
327 return new BigInteger(normalizeHexadecimal(stringRepresentation), base);
329 return new BigInteger(stringRepresentation, base);
333 public String serialize(final BigInteger data) {
334 return data == null ? "" : data.toString();
338 public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
339 StringCodec<String> {
341 protected StringCodecStringImpl(final Optional<StringTypeDefinition> typeDef) {
342 super(typeDef, String.class);
346 public String deserialize(final String stringRepresentation) {
347 return stringRepresentation == null ? "" :stringRepresentation;
351 public String serialize(final String data) {
352 return data == null ? "" : data.toString();
356 public static class Int16CodecStringImpl extends TypeDefinitionAwareCodec<Short, IntegerTypeDefinition> implements
359 protected Int16CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
360 super(typeDef, Short.class);
364 public Short deserialize(final String stringRepresentation) {
365 int base = provideBase(stringRepresentation);
367 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
369 return Short.valueOf(stringRepresentation, base);
373 public String serialize(final Short data) {
374 return data == null ? "" : data.toString();
378 public static class Int32CodecStringImpl extends TypeDefinitionAwareCodec<Integer, IntegerTypeDefinition> implements
381 protected Int32CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
382 super(typeDef, Integer.class);
386 public Integer deserialize(final String stringRepresentation) {
387 int base = provideBase(stringRepresentation);
389 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
391 return Integer.valueOf(stringRepresentation, base);
395 public String serialize(final Integer data) {
396 return data == null ? "" : data.toString();
400 public static class Int64CodecStringImpl extends TypeDefinitionAwareCodec<Long, IntegerTypeDefinition> implements
403 protected Int64CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
404 super(typeDef, Long.class);
408 public Long deserialize(final String stringRepresentation) {
409 int base = provideBase(stringRepresentation);
411 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
413 return Long.valueOf(stringRepresentation, base);
417 public String serialize(final Long data) {
418 return data == null ? "" : data.toString();
422 public static class Int8CodecStringImpl extends TypeDefinitionAwareCodec<Byte, IntegerTypeDefinition> implements
425 protected Int8CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
426 super(typeDef, Byte.class);
430 public Byte deserialize(final String stringRepresentation) {
431 int base = provideBase(stringRepresentation);
433 return Byte.valueOf(normalizeHexadecimal(stringRepresentation), base);
435 return Byte.valueOf(stringRepresentation, base);
439 public String serialize(final Byte data) {
440 return data == null ? "" : data.toString();
444 public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
447 protected EmptyCodecStringImpl(final Optional<EmptyTypeDefinition> typeDef) {
448 super(typeDef, Void.class);
452 public String serialize(final Void data) {
457 public Void deserialize(final String stringRepresentation) {
458 Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ),
459 "The value must be empty" );
464 public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
465 implements BinaryCodec<String> {
467 protected BinaryCodecStringImpl(final Optional<BinaryTypeDefinition> typeDef) {
468 super(typeDef, byte[].class);
472 public String serialize(final byte[] data) {
473 return data == null ? "" : BaseEncoding.base64().encode(data);
477 public byte[] deserialize(final String stringRepresentation) {
478 return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation);
482 public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
483 implements BitsCodec<String> {
485 public static final Joiner JOINER = Joiner.on(" ").skipNulls();
486 public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
488 @SuppressWarnings("unchecked")
489 protected BitsCodecStringImpl(final Optional<BitsTypeDefinition> typeDef) {
490 super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
494 public String serialize(final Set<String> data) {
495 return data == null ? "" : JOINER.join(data);
499 public Set<String> deserialize(final String stringRepresentation) {
500 if (stringRepresentation == null) {
501 return ImmutableSet.of();
504 Iterable<String> strings = SPLITTER.split(stringRepresentation);
506 if( getTypeDefinition().isPresent() ) {
507 Set<String> allowedNames = Sets.newHashSet();
508 for( BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) {
509 allowedNames.add( bit.getName() );
512 for( String bit: strings ) {
513 if( !allowedNames.contains( bit ) ) {
514 throw new IllegalArgumentException(
515 "Invalid value \"" + bit + "\" for bits type. Allowed values are: " +
521 return ImmutableSet.copyOf(strings);
525 public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
528 protected EnumCodecStringImpl(final Optional<EnumTypeDefinition> typeDef) {
529 super(typeDef, String.class);
533 public String deserialize(final String stringRepresentation) {
534 if( getTypeDefinition().isPresent() ) {
535 Set<String> allowedNames = Sets.newHashSet();
536 for( EnumPair pair: getTypeDefinition().get().getValues() ) {
537 allowedNames.add( pair.getName() );
540 if( !allowedNames.contains( stringRepresentation ) ) {
541 throw new IllegalArgumentException(
542 "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " +
547 return stringRepresentation;
551 public String serialize(final String data) {
552 return data == null ? "" : data;
556 public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
557 implements DecimalCodec<String> {
559 protected DecimalCodecStringImpl(final Optional<DecimalTypeDefinition> typeDef) {
560 super(typeDef, BigDecimal.class);
564 public String serialize(final BigDecimal data) {
565 return data == null ? "" : data.toString();
569 public BigDecimal deserialize(final String stringRepresentation) {
570 Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" );
571 return new BigDecimal(stringRepresentation);
575 public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition> implements
578 protected UnionCodecStringImpl(final Optional<UnionTypeDefinition> typeDef) {
579 super(typeDef, Object.class);
583 public String serialize(final Object data) {
584 return data == null ? "" : data.toString();
588 public Object deserialize(final String stringRepresentation) {
589 if( getTypeDefinition().isPresent() ) {
590 boolean valid = false;
591 for( TypeDefinition<?> type: getTypeDefinition().get().getTypes() ) {
592 TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwareCodec = from( type );
593 if( typeAwareCodec == null ) {
594 // This is a type for which we have no codec (eg identity ref) so we'll say it's valid
595 // but we'll continue in case there's another type for which we do have a codec.
601 typeAwareCodec.deserialize( stringRepresentation );
605 catch( Exception e ) {
606 // invalid - try the next union type.
611 throw new IllegalArgumentException(
612 "Invalid value \"" + stringRepresentation + "\" for union type." );
616 return stringRepresentation;