BUG-2962: add DataTreeTip interface and implement it
[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 com.google.common.base.Joiner;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Sets;
17 import com.google.common.io.BaseEncoding;
18 import java.math.BigDecimal;
19 import java.util.Set;
20 import javax.xml.bind.DatatypeConverter;
21 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
22 import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
23 import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
24 import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
25 import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
26 import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
27 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
28 import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
29 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
37 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
38 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
40 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
41 import org.opendaylight.yangtools.yang.model.util.DerivedType;
42
43 public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> implements DataStringCodec<J> {
44
45
46
47     private final Optional<T> typeDefinition;
48     private final Class<J> inputClass;
49
50
51
52     private static final BinaryCodecStringImpl BINARY_DEFAULT_CODEC = new BinaryCodecStringImpl(
53             Optional.<BinaryTypeDefinition> absent());
54
55     private static final BooleanCodecStringImpl BOOLEAN_DEFAULT_CODEC = new BooleanCodecStringImpl(
56             Optional.<BooleanTypeDefinition> absent());
57
58     private static final DecimalCodecStringImpl DECIMAL64_DEFAULT_CODEC = new DecimalCodecStringImpl(
59             Optional.<DecimalTypeDefinition> absent());
60
61     private static final EmptyCodecStringImpl EMPTY_DEFAULT_CODEC = new EmptyCodecStringImpl(
62             Optional.<EmptyTypeDefinition> absent());
63
64     private static final StringCodecStringImpl STRING_DEFAULT_CODEC = new StringCodecStringImpl(
65             Optional.<StringTypeDefinition> absent());
66
67     @Override
68     public Class<J> getInputClass() {
69         return inputClass;
70     }
71
72     protected TypeDefinitionAwareCodec(final Optional<T> typeDefinition, final Class<J> outputClass) {
73         Preconditions.checkArgument(outputClass != null, "Output class must be specified.");
74         this.typeDefinition = typeDefinition;
75         this.inputClass = outputClass;
76     }
77
78     public Optional<T> getTypeDefinition() {
79         return typeDefinition;
80     }
81
82     @SuppressWarnings({ "rawtypes", "unchecked" })
83     public static final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> from(final TypeDefinition typeDefinition) {
84         return fromType(typeDefinition);
85     }
86
87     @SuppressWarnings("unchecked")
88     public static final <T extends TypeDefinition<T>> TypeDefinitionAwareCodec<?, T> fromType(final T typeDefinition) {
89         final T normalizedType = (T) DerivedType.from(typeDefinition);
90         @SuppressWarnings("rawtypes")
91         TypeDefinitionAwareCodec codec = null;
92
93         if (normalizedType instanceof BinaryTypeDefinition) {
94             codec = BINARY_DEFAULT_CODEC;
95         } else if (normalizedType instanceof BitsTypeDefinition) {
96             codec = new BitsCodecStringImpl( Optional.of( (BitsTypeDefinition)normalizedType ) );
97         } else if (normalizedType instanceof BooleanTypeDefinition) {
98             codec = BOOLEAN_DEFAULT_CODEC;
99         } else if (normalizedType instanceof DecimalTypeDefinition) {
100             codec = DECIMAL64_DEFAULT_CODEC;
101         } else if (normalizedType instanceof EmptyTypeDefinition) {
102             codec = EMPTY_DEFAULT_CODEC;
103         } else if (normalizedType instanceof EnumTypeDefinition) {
104             codec = new EnumCodecStringImpl( Optional.of( (EnumTypeDefinition)normalizedType ) );
105         } else if (normalizedType instanceof IntegerTypeDefinition) {
106             codec = AbstractIntegerStringCodec.from((IntegerTypeDefinition) normalizedType);
107         } else if (normalizedType instanceof StringTypeDefinition) {
108             codec = STRING_DEFAULT_CODEC;
109         } else if (normalizedType instanceof UnionTypeDefinition) {
110             codec = new UnionCodecStringImpl( Optional.of( (UnionTypeDefinition)normalizedType ) );
111         } else if (normalizedType instanceof UnsignedIntegerTypeDefinition) {
112             codec = AbstractIntegerStringCodec.from((UnsignedIntegerTypeDefinition) normalizedType);
113         }
114         return codec;
115     }
116
117     public static class BooleanCodecStringImpl extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
118             implements BooleanCodec<String> {
119
120         protected BooleanCodecStringImpl(final Optional<BooleanTypeDefinition> typeDef) {
121             super(typeDef, Boolean.class);
122         }
123
124         @Override
125         public String serialize(final Boolean data) {
126             return data == null ? "" : data.toString();
127         }
128
129         @Override
130         public Boolean deserialize(final String stringRepresentation) {
131             return Boolean.valueOf(stringRepresentation);
132         }
133     }
134
135     @Deprecated
136     public static class Uint8CodecStringImpl extends Uint8StringCodec {
137
138         protected Uint8CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
139             super(typeDef);
140         }
141     }
142
143     @Deprecated
144     public static class Uint16CodecStringImpl extends Uint16StringCodec {
145         protected Uint16CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
146             super(typeDef);
147         }
148     }
149
150     @Deprecated
151     public static class Uint32CodecStringImpl extends Uint32StringCodec {
152
153         protected Uint32CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
154             super(typeDef);
155         }
156
157     }
158
159     @Deprecated
160     public static class Uint64CodecStringImpl extends Uint64StringCodec {
161
162         protected Uint64CodecStringImpl(final Optional<UnsignedIntegerTypeDefinition> typeDef) {
163             super(typeDef);
164         }
165
166     }
167
168     public static class StringCodecStringImpl extends TypeDefinitionAwareCodec<String, StringTypeDefinition> implements
169             StringCodec<String> {
170
171         protected StringCodecStringImpl(final Optional<StringTypeDefinition> typeDef) {
172             super(typeDef, String.class);
173         }
174
175         @Override
176         public String deserialize(final String stringRepresentation) {
177             return stringRepresentation == null ? "" : stringRepresentation;
178         }
179
180         @Override
181         public String serialize(final String data) {
182             return data == null ? "" : data;
183         }
184     }
185
186     @Deprecated
187     public static class Int16CodecStringImpl extends Int16StringCodec {
188
189         protected Int16CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
190             super(typeDef);
191         }
192
193     }
194
195     @Deprecated
196     public static class Int32CodecStringImpl extends Int32StringCodec {
197
198         protected Int32CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
199             super(typeDef);
200         }
201
202     }
203
204     @Deprecated
205     public static class Int64CodecStringImpl extends Int64StringCodec {
206
207         protected Int64CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
208             super(typeDef);
209         }
210
211     }
212
213     @Deprecated
214     public static class Int8CodecStringImpl extends Int8StringCodec {
215
216         protected Int8CodecStringImpl(final Optional<IntegerTypeDefinition> typeDef) {
217             super(typeDef);
218         }
219
220     }
221
222     public static class EmptyCodecStringImpl extends TypeDefinitionAwareCodec<Void, EmptyTypeDefinition> implements
223             EmptyCodec<String> {
224
225         protected EmptyCodecStringImpl(final Optional<EmptyTypeDefinition> typeDef) {
226             super(typeDef, Void.class);
227         }
228
229         @Override
230         public String serialize(final Void data) {
231             return "";
232         }
233
234         @Override
235         public Void deserialize(final String stringRepresentation) {
236             Preconditions.checkArgument( Strings.isNullOrEmpty( stringRepresentation ),
237                                          "The value must be empty" );
238             return null;
239         }
240     }
241
242     public static final class BinaryCodecStringImpl extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
243             implements BinaryCodec<String> {
244
245         protected BinaryCodecStringImpl(final Optional<BinaryTypeDefinition> typeDef) {
246             super(typeDef, byte[].class);
247         }
248
249         @Override
250         public String serialize(final byte[] data) {
251             return data == null ? "" : BaseEncoding.base64().encode(data);
252         }
253
254         @Override
255         public byte[] deserialize(final String stringRepresentation) {
256             return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation);
257         }
258     }
259
260     public static final class BitsCodecStringImpl extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
261             implements BitsCodec<String> {
262
263         public static final Joiner JOINER = Joiner.on(" ").skipNulls();
264         public static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
265
266         @SuppressWarnings("unchecked")
267         protected BitsCodecStringImpl(final Optional<BitsTypeDefinition> typeDef) {
268             super(typeDef, (Class<Set<String>>) ((Class<?>) Set.class));
269         }
270
271         @Override
272         public String serialize(final Set<String> data) {
273             return data == null ? "" : JOINER.join(data);
274         }
275
276         @Override
277         public Set<String> deserialize(final String stringRepresentation) {
278             if (stringRepresentation == null) {
279                 return ImmutableSet.of();
280             }
281
282             final Iterable<String> strings = SPLITTER.split(stringRepresentation);
283
284             if( getTypeDefinition().isPresent() ) {
285                 final Set<String> allowedNames = Sets.newHashSet();
286                 for( final BitsTypeDefinition.Bit bit: getTypeDefinition().get().getBits() ) {
287                     allowedNames.add( bit.getName() );
288                 }
289
290                 for( final String bit: strings ) {
291                     if( !allowedNames.contains( bit ) ) {
292                         throw new IllegalArgumentException(
293                             "Invalid value \"" + bit + "\" for bits type. Allowed values are: " +
294                             allowedNames );
295                     }
296                 }
297             }
298
299             return ImmutableSet.copyOf(strings);
300         }
301     }
302
303     public static class EnumCodecStringImpl extends TypeDefinitionAwareCodec<String, EnumTypeDefinition> implements
304             EnumCodec<String> {
305
306         protected EnumCodecStringImpl(final Optional<EnumTypeDefinition> typeDef) {
307             super(typeDef, String.class);
308         }
309
310         @Override
311         public String deserialize(final String stringRepresentation) {
312             if( getTypeDefinition().isPresent() ) {
313                 final Set<String> allowedNames = Sets.newHashSet();
314                 for( final EnumPair pair: getTypeDefinition().get().getValues() ) {
315                     allowedNames.add( pair.getName() );
316                 }
317
318                 if( !allowedNames.contains( stringRepresentation ) ) {
319                     throw new IllegalArgumentException(
320                         "Invalid value \"" + stringRepresentation + "\" for enum type. Allowed values are: " +
321                         allowedNames );
322                 }
323             }
324
325             return stringRepresentation;
326         }
327
328         @Override
329         public String serialize(final String data) {
330             return data == null ? "" : data;
331         }
332     }
333
334     public static class DecimalCodecStringImpl extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
335             implements DecimalCodec<String> {
336
337         protected DecimalCodecStringImpl(final Optional<DecimalTypeDefinition> typeDef) {
338             super(typeDef, BigDecimal.class);
339         }
340
341         @Override
342         public String serialize(final BigDecimal data) {
343             return data == null ? "" : data.toString();
344         }
345
346         @Override
347         public BigDecimal deserialize(final String stringRepresentation) {
348             Preconditions.checkArgument( stringRepresentation != null , "Input cannot be null" );
349             return new BigDecimal(stringRepresentation);
350         }
351     }
352
353     public static class UnionCodecStringImpl extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition> implements
354             UnionCodec<String> {
355
356         protected UnionCodecStringImpl(final Optional<UnionTypeDefinition> typeDef) {
357             super(typeDef, Object.class);
358         }
359
360         @Override
361         public String serialize(final Object data) {
362             return data == null ? "" : data.toString();
363         }
364
365         @Override
366         public Object deserialize(final String stringRepresentation) {
367             if( getTypeDefinition().isPresent() ) {
368                 boolean valid = false;
369                 for( final TypeDefinition<?> type: getTypeDefinition().get().getTypes() ) {
370                     final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwareCodec = from( type );
371                     if( typeAwareCodec == null ) {
372                         // This is a type for which we have no codec (eg identity ref) so we'll say it's valid
373                         // but we'll continue in case there's another type for which we do have a codec.
374                         valid = true;
375                         continue;
376                     }
377
378                     try {
379                         typeAwareCodec.deserialize( stringRepresentation );
380                         valid = true;
381                         break;
382                     }
383                     catch( final Exception e ) {
384                         // invalid - try the next union type.
385                     }
386                 }
387
388                 if( !valid ) {
389                     throw new IllegalArgumentException(
390                                         "Invalid value \"" + stringRepresentation + "\" for union type." );
391                 }
392             }
393
394             return stringRepresentation;
395         }
396     }
397 }