Introduced AbstractIntegerStringCodec.
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / TypeDefinitionAwareCodec.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.impl.codec;
9
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;
18
19 import com.google.common.base.Joiner;
20 import com.google.common.base.Optional;
21 import com.google.common.base.Preconditions;
22 import com.google.common.base.Splitter;
23 import com.google.common.base.Strings;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.collect.Sets;
26 import com.google.common.io.BaseEncoding;
27 import java.math.BigDecimal;
28 import java.math.BigInteger;
29 import java.util.Set;
30 import javax.xml.bind.DatatypeConverter;
31 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
32 import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
33 import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
34 import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
35 import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
36 import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
37 import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
38 import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
39 import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
40 import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
41 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
42 import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
43 import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
44 import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
45 import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
46 import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
47 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
53 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
55 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
58 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
59
60 public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> implements DataStringCodec<J> {
61
62
63
64     private final Optional<T> typeDefinition;
65     private final Class<J> inputClass;
66
67
68
69     private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
70             Optional.<BinaryTypeDefinition> absent());
71
72     private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
73             Optional.<BooleanTypeDefinition> absent());
74
75     private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
76             Optional.<DecimalTypeDefinition> absent());
77
78     private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
79             Optional.<EmptyTypeDefinition> absent());
80
81     private static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl(
82             Optional.<IntegerTypeDefinition> absent());
83
84     private static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl(
85             Optional.<IntegerTypeDefinition> absent());
86
87     private static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl(
88             Optional.<IntegerTypeDefinition> absent());
89
90     private static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl(
91             Optional.<IntegerTypeDefinition> absent());
92
93     private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
94             Optional.<StringTypeDefinition> absent());
95
96     private static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl(
97             Optional.<UnsignedIntegerTypeDefinition> absent());
98
99     private static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl(
100             Optional.<UnsignedIntegerTypeDefinition> absent());
101
102     private static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl(
103             Optional.<UnsignedIntegerTypeDefinition> absent());
104
105     private static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl(
106             Optional.<UnsignedIntegerTypeDefinition> absent());
107
108     @Override
109     public Class<J> getInputClass() {
110         return inputClass;
111     }
112
113     protected TypeDefinitionAwareCodec(final Optional<T> typeDefinition, final Class<J> outputClass) {
114         Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
115         this.typeDefinition = typeDefinition;
116         this.inputClass = outputClass;
117     }
118
119     public Optional<T> getTypeDefinition() {
120         return typeDefinition;
121     }
122
123     @SuppressWarnings({ "rawtypes", "unchecked" })
124     public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(final TypeDefinition typeDefinition) {
125         return fromType(typeDefinition);
126     }
127
128     @SuppressWarnings("unchecked")
129     public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(final T typeDefinition) {
130         T superType = typeDefinition;
131         while (superType.getBaseType() != null) {
132             superType = superType.getBaseType();
133         }
134
135         @SuppressWarnings("rawtypes")
136         TypeDefinitionAwareCodec codec = null;
137
138         if (superType instanceof BinaryTypeDefinition) {
139             codec = BINARY_DEFAULT_CODEC;
140         } else if (superType instanceof BitsTypeDefinition) {
141             codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)superType ) );
142         } else if (superType instanceof BooleanTypeDefinition) {
143             codec = BOOLEAN_DEFAULT_CODEC;
144         } else if (superType instanceof DecimalTypeDefinition) {
145             codec = DECIMAL64_DEFAULT_CODEC;
146         } else if (superType instanceof EmptyTypeDefinition) {
147             codec = EMPTY_DEFAULT_CODEC;
148         } else if (superType instanceof EnumTypeDefinition) {
149             codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)superType ) );
150         } else if (superType instanceof IntegerTypeDefinition) {
151             if (INT8_QNAME.equals(superType.getQName())) {
152                 codec = INT8_DEFAULT_CODEC;
153             } else if (INT16_QNAME.equals(superType.getQName())) {
154                 codec = INT16_DEFAULT_CODEC;
155             } else if (INT32_QNAME.equals(superType.getQName())) {
156                 codec = INT32_DEFAULT_CODEC;
157             } else if (INT64_QNAME.equals(superType.getQName())) {
158                 codec = INT64_DEFAULT_CODEC;
159             }
160         } else if (superType instanceof StringTypeDefinition) {
161             codec = STRING_DEFAULT_CODEC;
162         } else if (superType instanceof UnionTypeDefinition) {
163             codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)superType ) );
164         } else if (superType instanceof UnsignedIntegerTypeDefinition) {
165             if (UINT8_QNAME.equals(superType.getQName())) {
166                 codec = UINT8_DEFAULT_CODEC;
167             }
168             if (UINT16_QNAME.equals(superType.getQName())) {
169                 codec = UINT16_DEFAULT_CODEC;
170             }
171             if (UINT32_QNAME.equals(superType.getQName())) {
172                 codec = UINT32_DEFAULT_CODEC;
173             }
174             if (UINT64_QNAME.equals(superType.getQName())) {
175                 codec = UINT64_DEFAULT_CODEC;
176             }
177         }
178
179         return codec;
180     }
181
182     public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
183             implements BooleanCodec<String> {
184
185         protected BooleanCodecStringImpl(final Optional<BooleanTypeDefinition> typeDef) {
186             super(typeDef, Boolean.class);
187         }
188
189         @Override
190         public String serialize(final Boolean data) {
191             return data == null ? "" : data.toString();
192         }
193
194         @Override
195         public Boolean deserialize(final String stringRepresentation) {
196             return Boolean.valueOf(stringRepresentation);
197         }
198     }
199
200     public static class Uint8CodecStringImpl extends AbstractIntegerStringCodec<Short, UnsignedIntegerTypeDefinition>
201             implements Uint8Codec<String> {
202
203         protected Uint8CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
204             super(typeDef, Short.class);
205         }
206
207         @Override
208         public String serialize(final Short data) {
209             return data == null ? "" : data.toString();
210         }
211
212         @Override
213         public Short deserialize(final String stringRepresentation,final int base) {
214             return Short.valueOf(stringRepresentation, base);
215         }
216     }
217
218     public static class Uint16CodecStringImpl extends AbstractIntegerStringCodec<Integer, UnsignedIntegerTypeDefinition>
219             implements Uint16Codec<String> {
220         protected Uint16CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
221             super(typeDef, Integer.class);
222         }
223
224         @Override
225         public Integer deserialize(final String stringRepresentation, final int base) {
226             return Integer.valueOf(stringRepresentation, base);
227         }
228
229         @Override
230         public String serialize(final Integer data) {
231             return data == null ? "" : data.toString();
232         }
233     }
234
235     public static class Uint32CodecStringImpl extends AbstractIntegerStringCodec<Long, UnsignedIntegerTypeDefinition>
236             implements Uint32Codec<String> {
237
238         protected Uint32CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
239             super(typeDef, Long.class);
240         }
241
242         @Override
243         public Long deserialize(final String stringRepresentation, final int base) {
244             return Long.valueOf(stringRepresentation, base);
245         }
246
247         @Override
248         public String serialize(final Long data) {
249             return data == null ? "" : data.toString();
250         }
251     }
252
253     public static class Uint64CodecStringImpl extends AbstractIntegerStringCodec<BigInteger, UnsignedIntegerTypeDefinition> implements Uint64Codec<String> {
254
255         protected Uint64CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
256             super(typeDef, BigInteger.class);
257         }
258
259         @Override
260         public BigInteger deserialize(final String stringRepresentation, final int base) {
261             return new BigInteger(stringRepresentation, base);
262         }
263
264         @Override
265         public String serialize(final BigInteger data) {
266             return data == null ? "" : data.toString();
267         }
268     }
269
270     public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
271             StringCodec<String> {
272
273         protected StringCodecStringImpl(final Optional<StringTypeDefinition> typeDef) {
274             super(typeDef, String.class);
275         }
276
277         @Override
278         public String deserialize(final String stringRepresentation) {
279             return stringRepresentation == null ? "" : stringRepresentation;
280         }
281
282         @Override
283         public String serialize(final String data) {
284             return data == null ? "" : data;
285         }
286     }
287
288     public static class Int16CodecStringImpl extends AbstractIntegerStringCodec<Short, IntegerTypeDefinition> implements
289             Int16Codec<String> {
290
291         protected Int16CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
292             super(typeDef, Short.class);
293         }
294
295         @Override
296         public Short deserialize(final String stringRepresentation, final int base) {
297             return Short.valueOf(stringRepresentation, base);
298         }
299
300         @Override
301         public String serialize(final Short data) {
302             return data == null ? "" : data.toString();
303         }
304     }
305
306     public static class Int32CodecStringImpl extends AbstractIntegerStringCodec<Integer, IntegerTypeDefinition> implements
307             Int32Codec<String> {
308
309         protected Int32CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
310             super(typeDef, Integer.class);
311         }
312
313         @Override
314         public Integer deserialize(final String stringRepresentation, final int base) {
315             return Integer.valueOf(stringRepresentation, base);
316         }
317
318         @Override
319         public String serialize(final Integer data) {
320             return data == null ? "" : data.toString();
321         }
322     }
323
324     public static class Int64CodecStringImpl extends AbstractIntegerStringCodec<Long, IntegerTypeDefinition> implements
325             Int64Codec<String> {
326
327         protected Int64CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
328             super(typeDef, Long.class);
329         }
330
331         @Override
332         public Long deserialize(final String stringRepresentation, final int base) {
333             return Long.valueOf(stringRepresentation, base);
334         }
335
336         @Override
337         public String serialize(final Long data) {
338             return data == null ? "" : data.toString();
339         }
340     }
341
342     public static class Int8CodecStringImpl extends AbstractIntegerStringCodec<Byte, IntegerTypeDefinition> implements
343             Int8Codec<String> {
344
345         protected Int8CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
346             super(typeDef, Byte.class);
347         }
348
349         @Override
350         public Byte deserialize(final String stringRepresentation, final int base) {
351             return Byte.valueOf(stringRepresentation, base);
352         }
353
354         @Override
355         public String serialize(final Byte data) {
356             return data == null ? "" : data.toString();
357         }
358     }
359
360     public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
361             EmptyCodec<String> {
362
363         protected EmptyCodecStringImpl(final Optional<EmptyTypeDefinition> typeDef) {
364             super(typeDef, Void.class);
365         }
366
367         @Override
368         public String serialize(final Void data) {
369             return "";
370         }
371
372         @Override
373         public Void deserialize(final String stringRepresentation) {
374             Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ),
375                                          "The value must be empty" );
376             return null;
377         }
378     }
379
380     public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
381             implements BinaryCodec<String> {
382
383         protected BinaryCodecStringImpl(final Optional<BinaryTypeDefinition> typeDef) {
384             super(typeDef, byte[].class);
385         }
386
387         @Override
388         public String serialize(final byte[] data) {
389             return data == null ? "" : BaseEncoding.base64().encode(data);
390         }
391
392         @Override
393         public byte[] deserialize(final String stringRepresentation) {
394             return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation);
395         }
396     }
397
398     public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
399             implements BitsCodec<String> {
400
401         public static final Joiner JOINER = Joiner.on(" ").skipNulls();
402         public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
403
404         @SuppressWarnings("unchecked")
405         protected BitsCodecStringImpl(final Optional<BitsTypeDefinition> typeDef) {
406             super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
407         }
408
409         @Override
410         public String serialize(final Set<String> data) {
411             return data == null ? "" : JOINER.join(data);
412         }
413
414         @Override
415         public Set<String> deserialize(final String stringRepresentation) {
416             if (stringRepresentation == null) {
417                 return ImmutableSet.of();
418             }
419
420             final Iterable<String> strings = SPLITTER.split(stringRepresentation);
421
422             if( getTypeDefinition().isPresent() ) {
423                 final Set<String> allowedNames = Sets.newHashSet();
424                 for( final BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) {
425                     allowedNames.add( bit.getName() );
426                 }
427
428                 for( final String bit: strings ) {
429                     if( !allowedNames.contains( bit ) ) {
430                         throw new IllegalArgumentException(
431                             "Invalid value \"" + bit + "\" for bits type. Allowed values are: " +
432                             allowedNames );
433                     }
434                 }
435             }
436
437             return ImmutableSet.copyOf(strings);
438         }
439     }
440
441     public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
442             EnumCodec<String> {
443
444         protected EnumCodecStringImpl(final Optional<EnumTypeDefinition> typeDef) {
445             super(typeDef, String.class);
446         }
447
448         @Override
449         public String deserialize(final String stringRepresentation) {
450             if( getTypeDefinition().isPresent() ) {
451                 final Set<String> allowedNames = Sets.newHashSet();
452                 for( final EnumPair pair: getTypeDefinition().get().getValues() ) {
453                     allowedNames.add( pair.getName() );
454                 }
455
456                 if( !allowedNames.contains( stringRepresentation ) ) {
457                     throw new IllegalArgumentException(
458                         "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " +
459                         allowedNames );
460                 }
461             }
462
463             return stringRepresentation;
464         }
465
466         @Override
467         public String serialize(final String data) {
468             return data == null ? "" : data;
469         }
470     }
471
472     public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
473             implements DecimalCodec<String> {
474
475         protected DecimalCodecStringImpl(final Optional<DecimalTypeDefinition> typeDef) {
476             super(typeDef, BigDecimal.class);
477         }
478
479         @Override
480         public String serialize(final BigDecimal data) {
481             return data == null ? "" : data.toString();
482         }
483
484         @Override
485         public BigDecimal deserialize(final String stringRepresentation) {
486             Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" );
487             return new BigDecimal(stringRepresentation);
488         }
489     }
490
491     public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition> implements
492             UnionCodec<String> {
493
494         protected UnionCodecStringImpl(final Optional<UnionTypeDefinition> typeDef) {
495             super(typeDef, Object.class);
496         }
497
498         @Override
499         public String serialize(final Object data) {
500             return data == null ? "" : data.toString();
501         }
502
503         @Override
504         public Object deserialize(final String stringRepresentation) {
505             if( getTypeDefinition().isPresent() ) {
506                 boolean valid = false;
507                 for( final TypeDefinition<?> type: getTypeDefinition().get().getTypes() ) {
508                     final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwareCodec = from( type );
509                     if( typeAwareCodec == null ) {
510                         // This is a type for which we have no codec (eg identity ref) so we'll say it's valid
511                         // but we'll continue in case there's another type for which we do have a codec.
512                         valid = true;
513                         continue;
514                     }
515
516                     try {
517                         typeAwareCodec.deserialize( stringRepresentation );
518                         valid = true;
519                         break;
520                     }
521                     catch( final Exception e ) {
522                         // invalid - try the next union type.
523                     }
524                 }
525
526                 if( !valid ) {
527                     throw new IllegalArgumentException(
528                                         "Invalid value \"" + stringRepresentation + "\" for union type." );
529                 }
530             }
531
532             return stringRepresentation;
533         }
534     }
535 }