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