Populate codec/ directory
[yangtools.git] / codec / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / UnionXmlCodec.java
diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/UnionXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/UnionXmlCodec.java
new file mode 100644 (file)
index 0000000..76c7a5b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Intel Corporation and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class UnionXmlCodec<T> implements XmlCodec<T> {
+    private static final class Diverse extends UnionXmlCodec<Object> {
+        Diverse(final List<XmlCodec<?>> codecs) {
+            super(codecs);
+        }
+
+        @Override
+        public Class<Object> getDataType() {
+            return Object.class;
+        }
+    }
+
+    private static final class SingleType<T> extends UnionXmlCodec<T> {
+        private final Class<T> dataClass;
+
+        SingleType(final Class<T> dataClass, final List<XmlCodec<?>> codecs) {
+            super(codecs);
+            this.dataClass = requireNonNull(dataClass);
+        }
+
+        @Override
+        public Class<T> getDataType() {
+            return dataClass;
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(UnionXmlCodec.class);
+
+    private final ImmutableList<XmlCodec<?>> codecs;
+
+    UnionXmlCodec(final List<XmlCodec<?>> codecs) {
+        this.codecs = ImmutableList.copyOf(codecs);
+    }
+
+    static UnionXmlCodec<?> create(final UnionTypeDefinition type, final List<XmlCodec<?>> codecs) {
+        final Iterator<XmlCodec<?>> it = codecs.iterator();
+        verify(it.hasNext(), "Union %s has no subtypes", type);
+
+        Class<?> dataClass = it.next().getDataType();
+        while (it.hasNext()) {
+            final Class<?> next = it.next().getDataType();
+            if (!dataClass.equals(next)) {
+                LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next);
+                return new Diverse(codecs);
+            }
+        }
+
+        LOG.debug("Type {} has single data class {}", type, dataClass);
+        return new SingleType<>(dataClass, codecs);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public final T parseValue(final NamespaceContext ctx, final String str) {
+        for (XmlCodec<?> codec : codecs) {
+            final Object ret;
+            try {
+                ret = codec.parseValue(ctx, str);
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
+                continue;
+            }
+
+            return getDataType().cast(ret);
+        }
+
+        throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public void writeValue(final XMLStreamWriter ctx, final Object value) throws XMLStreamException {
+        for (XmlCodec<?> codec : codecs) {
+            if (!codec.getDataType().isInstance(value)) {
+                LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
+                continue;
+            }
+
+            @SuppressWarnings("unchecked")
+            final XmlCodec<Object> objCodec = (XmlCodec<Object>) codec;
+            try {
+                objCodec.writeValue(ctx, value);
+                return;
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} failed to serialize {}", codec, value, e);
+            }
+        }
+
+        throw new IllegalArgumentException("No codec would accept value \"" + value + "\"");
+    }
+}