BUG-7983: unify JSONCodec and XmlCodec methods
[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     public final T parseValue(final Object ctx, final String str) {
81         for (JSONCodec<?> codec : codecs) {
82             final Object ret;
83             try {
84                 ret = codec.parseValue(ctx, str);
85             } catch (RuntimeException e) {
86                 LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
87                 continue;
88             }
89
90             return getDataType().cast(ret);
91         }
92
93         throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
94     }
95
96     @Override
97     public final void writeValue(final JsonWriter ctx, final T value) throws IOException {
98         for (JSONCodec<?> codec : codecs) {
99             if (!codec.getDataType().isInstance(value)) {
100                 LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
101                 continue;
102             }
103
104             @SuppressWarnings("unchecked")
105             final JSONCodec<Object> objCodec = (JSONCodec<Object>) codec;
106             try {
107                 objCodec.writeValue(ctx, value);
108                 return;
109             } catch (RuntimeException e) {
110                 LOG.debug("Codec {} failed to serialize {}", codec, value, e);
111             }
112         }
113
114         throw new IllegalArgumentException("No codecs could serialize" + value);
115     }
116 }