23af4462ebaef3d3bbe0ed7b1ae625a40c565d73
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / UnionJSONCodec.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.codec.gson;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Verify;
12 import com.google.common.collect.ImmutableList;
13 import com.google.gson.stream.JsonWriter;
14 import java.io.IOException;
15 import java.util.Iterator;
16 import java.util.List;
17 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 /**
22  * Union codec based with a pre-calculated set of sub-types.
23  *
24  * @author Robert Varga
25  *
26  * @param <T> Data representation type
27  */
28 abstract class UnionJSONCodec<T> implements JSONCodec<T> {
29     private static final class Diverse extends UnionJSONCodec<Object> {
30         Diverse(final List<JSONCodec<?>> codecs) {
31             super(codecs);
32         }
33
34         @Override
35         public Class<Object> getDataType() {
36             return Object.class;
37         }
38     }
39
40     private static final class SingleType<T> extends UnionJSONCodec<T> {
41         private final Class<T> dataClass;
42
43         SingleType(final Class<T> dataClass, final List<JSONCodec<?>> codecs) {
44             super(codecs);
45             this.dataClass = Preconditions.checkNotNull(dataClass);
46         }
47
48         @Override
49         public Class<T> getDataType() {
50             return dataClass;
51         }
52     }
53
54     private static final Logger LOG = LoggerFactory.getLogger(UnionJSONCodec.class);
55
56     private final List<JSONCodec<?>> codecs;
57
58     UnionJSONCodec(final List<JSONCodec<?>> codecs) {
59         this.codecs = ImmutableList.copyOf(codecs);
60     }
61
62     static UnionJSONCodec<?> create(final UnionTypeDefinition type, final List<JSONCodec<?>> codecs) {
63         final Iterator<JSONCodec<?>> it = codecs.iterator();
64         Verify.verify(it.hasNext(), "Union %s has no subtypes", type);
65
66         Class<?> dataClass = it.next().getDataType();
67         while (it.hasNext()) {
68             final Class<?> next = it.next().getDataType();
69             if (!dataClass.equals(next)) {
70                 LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next);
71                 return new Diverse(codecs);
72             }
73         }
74
75         LOG.debug("Type {} has single data class {}", type, dataClass);
76         return new SingleType<>(dataClass, codecs);
77     }
78
79     @Override
80     @SuppressWarnings("checkstyle:illegalCatch")
81     public final T parseValue(final Object ctx, final String str) {
82         for (JSONCodec<?> codec : codecs) {
83             final Object ret;
84             try {
85                 ret = codec.parseValue(ctx, str);
86             } catch (RuntimeException e) {
87                 LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
88                 continue;
89             }
90
91             return getDataType().cast(ret);
92         }
93
94         throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
95     }
96
97     @Override
98     @SuppressWarnings("checkstyle:illegalCatch")
99     public final void writeValue(final JsonWriter ctx, final T value) throws IOException {
100         for (JSONCodec<?> codec : codecs) {
101             if (!codec.getDataType().isInstance(value)) {
102                 LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
103                 continue;
104             }
105
106             @SuppressWarnings("unchecked")
107             final JSONCodec<Object> objCodec = (JSONCodec<Object>) codec;
108             try {
109                 objCodec.writeValue(ctx, value);
110                 return;
111             } catch (RuntimeException e) {
112                 LOG.debug("Codec {} failed to serialize {}", codec, value, e);
113             }
114         }
115
116         throw new IllegalArgumentException("No codecs could serialize" + value);
117     }
118 }