Add JSONValue and JSONCodec.unparseValue()
[yangtools.git] / codec / 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 static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.ImmutableList;
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 sealed 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 = requireNonNull(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 ImmutableList<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(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 String str) {
82         for (var codec : codecs) {
83             final Object ret;
84             try {
85                 ret = codec.parseValue(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 JSONValueWriter ctx, final T value) throws IOException {
100         for (var codec : codecs) {
101             if (codec.getDataType().isInstance(value)) {
102                 @SuppressWarnings("unchecked")
103                 final var objCodec = (JSONCodec<Object>) codec;
104                 try {
105                     objCodec.writeValue(ctx, value);
106                     return;
107                 } catch (RuntimeException e) {
108                     LOG.debug("Codec {} failed to serialize {}", codec, value, e);
109                 }
110             } else {
111                 LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
112             }
113         }
114
115         throw new IllegalArgumentException("No codecs could serialize" + value);
116     }
117
118
119     @Override
120     @SuppressWarnings("checkstyle:illegalCatch")
121     public JSONValue unparseValue(final Object value) {
122         for (var codec : codecs) {
123             if (codec.getDataType().isInstance(value)) {
124                 @SuppressWarnings("unchecked")
125                 final var objCodec = (JSONCodec<Object>) codec;
126                 try {
127                     return objCodec.unparseValue(value);
128                 } catch (RuntimeException e) {
129                     LOG.debug("Codec {} failed to unparse {}", codec, value, e);
130                 }
131             } else {
132                 LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
133             }
134         }
135
136         throw new IllegalArgumentException("No codecs could unparse" + value);
137     }
138 }