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