03663d6cb8fbf67b7489af8163621cce53e98787
[yangtools.git] / codec / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / UnionXmlCodec.java
1 /*
2  * Copyright (c) 2016 Intel Corporation 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.xml;
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.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17 import javax.xml.namespace.NamespaceContext;
18 import javax.xml.stream.XMLStreamException;
19 import javax.xml.stream.XMLStreamWriter;
20 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 abstract class UnionXmlCodec<T> implements XmlCodec<T> {
25     private static final class Diverse extends UnionXmlCodec<Object> {
26         Diverse(final List<XmlCodec<?>> codecs) {
27             super(codecs);
28         }
29
30         @Override
31         public Class<Object> getDataType() {
32             return Object.class;
33         }
34     }
35
36     private static final class SingleType<T> extends UnionXmlCodec<T> {
37         private final Class<T> dataClass;
38
39         SingleType(final Class<T> dataClass, final List<XmlCodec<?>> codecs) {
40             super(codecs);
41             this.dataClass = requireNonNull(dataClass);
42         }
43
44         @Override
45         public Class<T> getDataType() {
46             return dataClass;
47         }
48     }
49
50     private static final Logger LOG = LoggerFactory.getLogger(UnionXmlCodec.class);
51
52     private final ImmutableList<XmlCodec<?>> codecs;
53
54     UnionXmlCodec(final List<XmlCodec<?>> codecs) {
55         this.codecs = ImmutableList.copyOf(codecs);
56     }
57
58     static UnionXmlCodec<?> create(final UnionTypeDefinition type, final List<XmlCodec<?>> codecs) {
59         final Iterator<XmlCodec<?>> it = codecs.iterator();
60         verify(it.hasNext(), "Union %s has no subtypes", type);
61
62         Class<?> dataClass = it.next().getDataType();
63         while (it.hasNext()) {
64             final Class<?> next = it.next().getDataType();
65             if (!dataClass.equals(next)) {
66                 LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next);
67                 return new Diverse(codecs);
68             }
69         }
70
71         LOG.debug("Type {} has single data class {}", type, dataClass);
72         return new SingleType<>(dataClass, codecs);
73     }
74
75     @Override
76     @SuppressWarnings("checkstyle:illegalCatch")
77     public final T parseValue(final NamespaceContext ctx, final String str) {
78         final var suppressed = new ArrayList<RuntimeException>();
79
80         for (XmlCodec<?> codec : codecs) {
81             final Object ret;
82             try {
83                 ret = codec.parseValue(ctx, str);
84             } catch (RuntimeException e) {
85                 LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
86                 suppressed.add(e);
87                 continue;
88             }
89
90             return getDataType().cast(ret);
91         }
92
93         final var ex = new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
94         suppressed.forEach(ex::addSuppressed);
95         throw ex;
96     }
97
98     @Override
99     @SuppressWarnings("checkstyle:illegalCatch")
100     public void writeValue(final XMLStreamWriter ctx, final Object value) throws XMLStreamException {
101         for (XmlCodec<?> 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 XmlCodec<Object> objCodec = (XmlCodec<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 codec would accept value \"" + value + "\"");
118     }
119 }