BUG 274 nicer formating of output error message
[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 java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.util.Set;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
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.IntegerTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
52
53 import com.google.common.base.Joiner;
54 import com.google.common.base.Optional;
55 import com.google.common.base.Preconditions;
56 import com.google.common.base.Splitter;
57 import com.google.common.collect.ImmutableSet;
58 import com.google.common.io.BaseEncoding;
59
60 public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> implements DataStringCodec<J> {
61
62     private static final Pattern intPattern = Pattern.compile("[+-]?[1-9][0-9]*$");
63     private static final Pattern hexPattern = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
64     private static final Pattern octalPattern = Pattern.compile("[+-]?0[1-7][0-7]*$");
65
66     private final Optional<T> typeDefinition;
67     private final Class<J> inputClass;
68
69     private static final int provideBase(final String integer) {
70         if (integer == null) {
71             throw new IllegalArgumentException("String representing integer number cannot be NULL!");
72         }
73
74         if ((integer.length() == 1) && (integer.charAt(0) == '0')) {
75             return 10;
76         }
77
78         final Matcher intMatcher = intPattern.matcher(integer);
79         if (intMatcher.matches()) {
80             return 10;
81         } else {
82             final Matcher hexMatcher = hexPattern.matcher(integer);
83             if (hexMatcher.matches()) {
84                 return 16;
85             } else {
86                 final Matcher octMatcher = octalPattern.matcher(integer);
87                 if (octMatcher.matches()) {
88                     return 8;
89                 } else {
90                     String formatedMessage = String.format("Incorrect lexical representation of Integer value: %s" 
91                             + "%nThe Integer value can be defined as "
92                             + "%n- Integer Number,"
93                             + "%n- Hexadecimal Number (prefix 0x),"
94                             + "%n- Octal Number (prefix 0)."
95                             + "%nThe sign vlues are allowed. Spaces between digits are NOT allowed!", integer);
96                     throw new NumberFormatException(formatedMessage);
97                 }
98             }
99         }
100     }
101
102     private static String normalizeHexadecimal(final String hexInt) {
103         if (hexInt == null) {
104             throw new IllegalArgumentException(
105                     "String representing integer number in Hexadecimal format cannot be NULL!");
106         }
107         final String normalizedString;
108         if (hexInt.contains("x")) {
109             normalizedString = hexInt.replace("x", "");
110         } else if (hexInt.contains("X")) {
111             normalizedString = hexInt.replace("X", "");
112         } else {
113             normalizedString = hexInt;
114         }
115         return normalizedString;
116     }
117
118     public static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
119             Optional.<BinaryTypeDefinition> absent());
120
121     public static final BitsCodecStringImpl BITS_DEFAULT_CODEC = new BitsCodecStringImpl(
122             Optional.<BitsTypeDefinition> absent());
123
124     public static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
125             Optional.<BooleanTypeDefinition> absent());
126
127     public static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
128             Optional.<DecimalTypeDefinition> absent());
129
130     public static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
131             Optional.<EmptyTypeDefinition> absent());
132
133     public static final EnumCodecStringImpl ENUMERATION_DEFAULT_CODEC = new EnumCodecStringImpl(
134             Optional.<EnumTypeDefinition> absent());
135
136     public static final Int8CodecStringImpl INT8_DEFAULT_CODEC = new Int8CodecStringImpl(
137             Optional.<IntegerTypeDefinition> absent());
138
139     public static final Int16CodecStringImpl INT16_DEFAULT_CODEC = new Int16CodecStringImpl(
140             Optional.<IntegerTypeDefinition> absent());
141
142     public static final Int32CodecStringImpl INT32_DEFAULT_CODEC = new Int32CodecStringImpl(
143             Optional.<IntegerTypeDefinition> absent());
144
145     public static final Int64CodecStringImpl INT64_DEFAULT_CODEC = new Int64CodecStringImpl(
146             Optional.<IntegerTypeDefinition> absent());
147
148     public static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
149             Optional.<StringTypeDefinition> absent());
150
151     public static final Uint8CodecStringImpl UINT8_DEFAULT_CODEC = new Uint8CodecStringImpl(
152             Optional.<UnsignedIntegerTypeDefinition> absent());
153
154     public static final Uint16CodecStringImpl UINT16_DEFAULT_CODEC = new Uint16CodecStringImpl(
155             Optional.<UnsignedIntegerTypeDefinition> absent());
156
157     public static final Uint32CodecStringImpl UINT32_DEFAULT_CODEC = new Uint32CodecStringImpl(
158             Optional.<UnsignedIntegerTypeDefinition> absent());
159
160     public static final Uint64CodecStringImpl UINT64_DEFAULT_CODEC = new Uint64CodecStringImpl(
161             Optional.<UnsignedIntegerTypeDefinition> absent());
162
163     public static final UnionCodecStringImpl UNION_DEFAULT_CODEC = new UnionCodecStringImpl(
164             Optional.<UnionTypeDefinition> absent());
165
166     public Class<J> getInputClass() {
167         return inputClass;
168     }
169
170     protected TypeDefinitionAwareCodec(Optional<T> typeDefinition, Class<J> outputClass) {
171         Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
172         this.typeDefinition = typeDefinition;
173         this.inputClass = outputClass;
174     }
175
176     public Optional<T> getTypeDefinition() {
177         return typeDefinition;
178     }
179
180     @SuppressWarnings({ "rawtypes", "unchecked" })
181     public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(TypeDefinition typeDefinition) {
182         final TypeDefinitionAwareCodec codec = fromType(typeDefinition);
183         return (TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>) codec;
184     }
185
186     public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(T typeDefinition) {
187         T superType = typeDefinition;
188         while (superType.getBaseType() != null) {
189             superType = superType.getBaseType();
190         }
191
192         @SuppressWarnings("rawtypes")
193         TypeDefinitionAwareCodec codec = null;
194
195         if (superType instanceof BinaryTypeDefinition) {
196             codec = BINARY_DEFAULT_CODEC;
197         } else if (superType instanceof BitsTypeDefinition) {
198             codec = BITS_DEFAULT_CODEC;
199         } else if (superType instanceof BooleanTypeDefinition) {
200             codec = BOOLEAN_DEFAULT_CODEC;
201         } else if (superType instanceof DecimalTypeDefinition) {
202             codec = DECIMAL64_DEFAULT_CODEC;
203         } else if (superType instanceof EmptyTypeDefinition) {
204             codec = EMPTY_DEFAULT_CODEC;
205         } else if (superType instanceof EnumTypeDefinition) {
206             codec = ENUMERATION_DEFAULT_CODEC;
207         } else if (superType instanceof IntegerTypeDefinition) {
208             if (INT8_QNAME.equals(superType.getQName())) {
209                 codec = INT8_DEFAULT_CODEC;
210             } else if (INT16_QNAME.equals(superType.getQName())) {
211                 codec = INT16_DEFAULT_CODEC;
212             } else if (INT32_QNAME.equals(superType.getQName())) {
213                 codec = INT32_DEFAULT_CODEC;
214             } else if (INT64_QNAME.equals(superType.getQName())) {
215                 codec = INT64_DEFAULT_CODEC;
216             }
217         } else if (superType instanceof StringTypeDefinition) {
218             codec = STRING_DEFAULT_CODEC;
219         } else if (superType instanceof UnionTypeDefinition) {
220             codec = UNION_DEFAULT_CODEC;
221         } else if (superType instanceof UnsignedIntegerTypeDefinition) {
222             if (UINT8_QNAME.equals(superType.getQName())) {
223                 codec = UINT8_DEFAULT_CODEC;
224             }
225             if (UINT16_QNAME.equals(superType.getQName())) {
226                 codec = UINT16_DEFAULT_CODEC;
227             }
228             if (UINT32_QNAME.equals(superType.getQName())) {
229                 codec = UINT32_DEFAULT_CODEC;
230             }
231             if (UINT64_QNAME.equals(superType.getQName())) {
232                 codec = UINT64_DEFAULT_CODEC;
233             }
234         }
235         @SuppressWarnings("unchecked")
236         TypeDefinitionAwareCodec<?, T> ret = (TypeDefinitionAwareCodec<?, T>) codec;
237         return ret;
238     }
239
240     public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
241             implements BooleanCodec<String> {
242
243         protected BooleanCodecStringImpl(Optional<BooleanTypeDefinition> typeDef) {
244             super(typeDef, Boolean.class);
245         }
246
247         @Override
248         public String serialize(Boolean data) {
249             return data == null ? "" : data.toString();
250         }
251
252         @Override
253         public Boolean deserialize(String stringRepresentation) {
254             return Boolean.valueOf(stringRepresentation);
255         }
256     };
257
258     public static class Uint8CodecStringImpl extends TypeDefinitionAwareCodec<Short, UnsignedIntegerTypeDefinition>
259             implements Uint8Codec<String> {
260
261         protected Uint8CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
262             super(typeDef, Short.class);
263         }
264
265         @Override
266         public String serialize(Short data) {
267             return data == null ? "" : data.toString();
268         }
269
270         @Override
271         public Short deserialize(String stringRepresentation) {
272             int base = provideBase(stringRepresentation);
273             if (base == 16) {
274                 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
275             }
276             return Short.valueOf(stringRepresentation, base);
277         }
278     };
279
280     public static class Uint16CodecStringImpl extends TypeDefinitionAwareCodec<Integer, UnsignedIntegerTypeDefinition>
281             implements Uint16Codec<String> {
282         protected Uint16CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
283             super(typeDef, Integer.class);
284         }
285
286         @Override
287         public Integer deserialize(String stringRepresentation) {
288             int base = provideBase(stringRepresentation);
289             if (base == 16) {
290                 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
291             }
292             return Integer.valueOf(stringRepresentation, base);
293         }
294
295         @Override
296         public String serialize(Integer data) {
297             return data == null ? "" : data.toString();
298         }
299     };
300
301     public static class Uint32CodecStringImpl extends TypeDefinitionAwareCodec<Long, UnsignedIntegerTypeDefinition>
302             implements Uint32Codec<String> {
303
304         protected Uint32CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
305             super(typeDef, Long.class);
306         }
307
308         @Override
309         public Long deserialize(String stringRepresentation) {
310             int base = provideBase(stringRepresentation);
311             if (base == 16) {
312                 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
313             }
314             return Long.valueOf(stringRepresentation, base);
315         }
316
317         @Override
318         public String serialize(Long data) {
319             return data == null ? "" : data.toString();
320         }
321     };
322
323     public static class Uint64CodecStringImpl extends
324             TypeDefinitionAwareCodec<BigInteger, UnsignedIntegerTypeDefinition> implements Uint64Codec<String> {
325
326         protected Uint64CodecStringImpl(Optional<UnsignedIntegerTypeDefinition> typeDef) {
327             super(typeDef, BigInteger.class);
328         }
329
330         @Override
331         public BigInteger deserialize(String stringRepresentation) {
332             int base = provideBase(stringRepresentation);
333             if (base == 16) {
334                 return new BigInteger(normalizeHexadecimal(stringRepresentation), base);
335             }
336             return new BigInteger(stringRepresentation, base);
337         }
338
339         @Override
340         public String serialize(BigInteger data) {
341             return data == null ? "" : data.toString();
342         }
343     };
344
345     public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
346             StringCodec<String> {
347
348         protected StringCodecStringImpl(Optional<StringTypeDefinition> typeDef) {
349             super(typeDef, String.class);
350         }
351
352         @Override
353         public String deserialize(String stringRepresentation) {
354             return stringRepresentation;
355         }
356
357         @Override
358         public String serialize(String data) {
359             return data == null ? "" : data.toString();
360         }
361     };
362
363     public static class Int16CodecStringImpl extends TypeDefinitionAwareCodec<Short, IntegerTypeDefinition> implements
364             Int16Codec<String> {
365
366         protected Int16CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
367             super(typeDef, Short.class);
368         }
369
370         @Override
371         public Short deserialize(String stringRepresentation) {
372             int base = provideBase(stringRepresentation);
373             if (base == 16) {
374                 return Short.valueOf(normalizeHexadecimal(stringRepresentation), base);
375             }
376             return Short.valueOf(stringRepresentation, base);
377         }
378
379         @Override
380         public String serialize(Short data) {
381             return data == null ? "" : data.toString();
382         }
383     };
384
385     public static class Int32CodecStringImpl extends TypeDefinitionAwareCodec<Integer, IntegerTypeDefinition> implements
386             Int32Codec<String> {
387
388         protected Int32CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
389             super(typeDef, Integer.class);
390         }
391
392         @Override
393         public Integer deserialize(String stringRepresentation) {
394             int base = provideBase(stringRepresentation);
395             if (base == 16) {
396                 return Integer.valueOf(normalizeHexadecimal(stringRepresentation), base);
397             }
398             return Integer.valueOf(stringRepresentation, base);
399         }
400
401         @Override
402         public String serialize(Integer data) {
403             return data == null ? "" : data.toString();
404         }
405     };
406
407     public static class Int64CodecStringImpl extends TypeDefinitionAwareCodec<Long, IntegerTypeDefinition> implements
408             Int64Codec<String> {
409
410         protected Int64CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
411             super(typeDef, Long.class);
412         }
413
414         @Override
415         public Long deserialize(String stringRepresentation) {
416             int base = provideBase(stringRepresentation);
417             if (base == 16) {
418                 return Long.valueOf(normalizeHexadecimal(stringRepresentation), base);
419             }
420             return Long.valueOf(stringRepresentation, base);
421         }
422
423         @Override
424         public String serialize(Long data) {
425             return data == null ? "" : data.toString();
426         }
427     };
428
429     public static class Int8CodecStringImpl extends TypeDefinitionAwareCodec<Byte, IntegerTypeDefinition> implements
430             Int8Codec<String> {
431
432         protected Int8CodecStringImpl(Optional<IntegerTypeDefinition> typeDef) {
433             super(typeDef, Byte.class);
434         }
435
436         @Override
437         public Byte deserialize(String stringRepresentation) {
438             int base = provideBase(stringRepresentation);
439             if (base == 16) {
440                 return Byte.valueOf(normalizeHexadecimal(stringRepresentation), base);
441             }
442             return Byte.valueOf(stringRepresentation, base);
443         }
444
445         @Override
446         public String serialize(Byte data) {
447             return data == null ? "" : data.toString();
448         }
449     };
450
451     public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
452             EmptyCodec<String> {
453
454         protected EmptyCodecStringImpl(Optional<EmptyTypeDefinition> typeDef) {
455             super(typeDef, Void.class);
456         }
457
458         @Override
459         public String serialize(Void data) {
460             return "";
461         }
462
463         @Override
464         public Void deserialize(String stringRepresentation) {
465             return null;
466         }
467     };
468
469     public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
470             implements BinaryCodec<String> {
471
472         protected BinaryCodecStringImpl(Optional<BinaryTypeDefinition> typeDef) {
473             super(typeDef, byte[].class);
474         }
475
476         @Override
477         public String serialize(byte[] data) {
478             return data == null ? "" : BaseEncoding.base64().encode(data);
479         }
480
481         @Override
482         public byte[] deserialize(String stringRepresentation) {
483             return BaseEncoding.base64().decode(stringRepresentation);
484         }
485     };
486
487     public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
488             implements BitsCodec<String> {
489
490         public static final Joiner JOINER = Joiner.on(" ").skipNulls();
491         public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
492
493         @SuppressWarnings("unchecked")
494         protected BitsCodecStringImpl(Optional<BitsTypeDefinition> typeDef) {
495             super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
496         }
497
498         @Override
499         public String serialize(Set<String> data) {
500             return data == null ? "" : JOINER.join(data);
501         }
502
503         @Override
504         public Set<String> deserialize(String stringRepresentation) {
505             if (stringRepresentation == null)
506                 return ImmutableSet.of();
507             Iterable<String> strings = SPLITTER.split(stringRepresentation);
508             return ImmutableSet.copyOf(strings);
509         }
510     };
511
512     public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
513             EnumCodec<String> {
514
515         protected EnumCodecStringImpl(Optional<EnumTypeDefinition> typeDef) {
516             super(typeDef, String.class);
517         }
518
519         @Override
520         public String deserialize(String stringRepresentation) {
521             return stringRepresentation;
522         }
523
524         @Override
525         public String serialize(String data) {
526             return data == null ? "" : data.toString();
527         }
528     };
529
530     public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
531             implements DecimalCodec<String> {
532
533         protected DecimalCodecStringImpl(Optional<DecimalTypeDefinition> typeDef) {
534             super(typeDef, BigDecimal.class);
535         }
536
537         @Override
538         public String serialize(BigDecimal data) {
539             return data == null ? "" : data.toString();
540         }
541
542         @Override
543         public BigDecimal deserialize(String stringRepresentation) {
544             return new BigDecimal(stringRepresentation);
545         }
546     };
547
548     public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<String, UnionTypeDefinition> implements
549             UnionCodec<String> {
550
551         protected UnionCodecStringImpl(Optional<UnionTypeDefinition> typeDef) {
552             super(typeDef, String.class);
553         }
554
555         @Override
556         public String serialize(String data) {
557             return data == null ? "" : data;
558         }
559
560         @Override
561         public String deserialize(String stringRepresentation) {
562             return stringRepresentation;
563         }
564     };
565 }