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