Use switch with patterns in yang-data-codec-xml
[yangtools.git] / codec / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / XmlParserStream.java
index 47fdfa2b1c74838984f45067ebfcfb25dc2075cc..e3ccd1ea58c388f11996184eacd38afe31689f70 100644 (file)
@@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableMap;
 import java.io.Closeable;
 import java.io.Flushable;
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.Deque;
 import java.util.HashMap;
@@ -81,12 +80,12 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
 
 /**
  * This class provides functionality for parsing an XML source containing YANG-modeled data. It disallows multiple
@@ -110,7 +109,7 @@ public final class XmlParserStream implements Closeable, Flushable {
      *             annotations.
      */
     @Deprecated
-    public static final QNameModule LEGACY_ATTRIBUTE_NAMESPACE = QNameModule.create(XMLNamespace.of("")).intern();
+    public static final QNameModule LEGACY_ATTRIBUTE_NAMESPACE = QNameModule.of("").intern();
 
     private static final Logger LOG = LoggerFactory.getLogger(XmlParserStream.class);
     private static final String XML_STANDARD_VERSION = "1.0";
@@ -149,23 +148,17 @@ public final class XmlParserStream implements Closeable, Flushable {
         this.codecs = requireNonNull(codecs);
         this.stack = requireNonNull(stack);
         this.strictParsing = strictParsing;
+        parentNode = stack.isEmpty() ? stack.modelContext() : coerceAsParent(stack.currentStatement());
+    }
 
-        if (!stack.isEmpty()) {
-            final var stmt = stack.currentStatement();
-            if (stmt instanceof DataSchemaNode data) {
-                parentNode = data;
-            } else if (stmt instanceof OperationDefinition oper) {
-                parentNode = oper.toContainerLike();
-            } else if (stmt instanceof NotificationDefinition notif) {
-                parentNode = notif.toContainerLike();
-            } else if (stmt instanceof YangDataSchemaNode yangData) {
-                parentNode = yangData.toContainerLike();
-            } else {
-                throw new IllegalArgumentException("Illegal parent node " + stmt);
-            }
-        } else {
-            parentNode = stack.getEffectiveModelContext();
-        }
+    private static DataSchemaNode coerceAsParent(final EffectiveStatement<?, ?> stmt) {
+        return switch (stmt) {
+            case DataSchemaNode data -> data;
+            case OperationDefinition oper -> oper.toContainerLike();
+            case NotificationDefinition notif -> notif.toContainerLike();
+            case YangDataSchemaNode yangData -> yangData.toContainerLike();
+            default -> throw new IllegalArgumentException("Illegal parent node " + stmt);
+        };
     }
 
     /**
@@ -235,7 +228,7 @@ public final class XmlParserStream implements Closeable, Flushable {
      */
     public static XmlParserStream create(final NormalizedNodeStreamWriter writer,
             final EffectiveStatementInference parentNode, final boolean strictParsing) {
-        return create(writer, XmlCodecFactory.create(parentNode.getEffectiveModelContext()), parentNode, strictParsing);
+        return create(writer, XmlCodecFactory.create(parentNode.modelContext()), parentNode, strictParsing);
     }
 
     /**
@@ -244,8 +237,7 @@ public final class XmlParserStream implements Closeable, Flushable {
      * instead and maintain a {@link XmlCodecFactory} to match the current {@link MountPointContext}.
      */
     public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx) {
-        return create(writer, mountCtx, SchemaInferenceStack.of(mountCtx.getEffectiveModelContext()).toInference(),
-            true);
+        return create(writer, mountCtx, SchemaInferenceStack.of(mountCtx.modelContext()).toInference(), true);
     }
 
     /**
@@ -280,7 +272,7 @@ public final class XmlParserStream implements Closeable, Flushable {
     public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
             final Absolute parentNode, final boolean strictParsing) {
         return new XmlParserStream(writer, codecs,
-            SchemaInferenceStack.of(codecs.getEffectiveModelContext(), parentNode), strictParsing);
+            SchemaInferenceStack.of(codecs.modelContext(), parentNode), strictParsing);
     }
 
     @Beta
@@ -292,7 +284,7 @@ public final class XmlParserStream implements Closeable, Flushable {
     @Beta
     public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
             final YangInstanceIdentifier parentNode, final boolean strictParsing) {
-        final var init = DataSchemaContextTree.from(mountCtx.getEffectiveModelContext())
+        final var init = DataSchemaContextTree.from(mountCtx.modelContext())
             .enterPath(parentNode)
             .orElseThrow();
         return new XmlParserStream(writer, XmlCodecFactory.create(mountCtx), init.stack(), strictParsing);
@@ -342,16 +334,11 @@ public final class XmlParserStream implements Closeable, Flushable {
      *              instance of XmlParserStream
      * @throws XMLStreamException
      *              if a well-formedness error or an unexpected processing condition occurs while parsing the XML
-     * @throws URISyntaxException
-     *              if the namespace URI of an XML element contains a syntax error
      * @throws IOException
      *              if an error occurs while parsing the value of an anyxml node
-     * @throws SAXException
-     *              if an error occurs while parsing the value of an anyxml node
      */
     @Beta
-    public XmlParserStream traverse(final DOMSource src) throws XMLStreamException, URISyntaxException, IOException,
-            SAXException {
+    public XmlParserStream traverse(final DOMSource src) throws XMLStreamException, IOException {
         return parse(new DOMSourceXMLStreamReader(src));
     }
 
@@ -379,7 +366,7 @@ public final class XmlParserStream implements Closeable, Flushable {
             final Optional<QNameModule> optModule = resolveXmlNamespace(attributeNS);
             if (optModule.isPresent()) {
                 final QName qname = QName.create(optModule.orElseThrow(), localName);
-                final var optAnnotation = AnnotationSchemaNode.find(codecs.getEffectiveModelContext(),
+                final var optAnnotation = AnnotationSchemaNode.find(codecs.modelContext(),
                     new AnnotationName(qname));
                 if (optAnnotation.isPresent()) {
                     final AnnotationSchemaNode schema = optAnnotation.orElseThrow();
@@ -521,14 +508,14 @@ public final class XmlParserStream implements Closeable, Flushable {
 
                     final XMLNamespace nsUri;
                     try {
-                        nsUri = rawXmlNamespace(elementNS).getNamespace();
+                        nsUri = rawXmlNamespace(elementNS).namespace();
                     } catch (IllegalArgumentException e) {
                         throw new XMLStreamException("Failed to convert namespace " + xmlElementName, in.getLocation(),
                             e);
                     }
 
-                    final Deque<DataSchemaNode> childDataSchemaNodes =
-                            ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, xmlElementName, nsUri);
+                    final var childDataSchemaNodes = ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema,
+                        xmlElementName, nsUri);
                     if (!childDataSchemaNodes.isEmpty()) {
                         final boolean elementList = isElementList(childDataSchemaNodes);
                         if (!added && !elementList) {
@@ -538,9 +525,9 @@ public final class XmlParserStream implements Closeable, Flushable {
                         }
 
                         // We have a match, proceed with it
-                        final QName qname = childDataSchemaNodes.peekLast().getQName();
-                        final AbstractNodeDataWithSchema<?> child = ((CompositeNodeDataWithSchema<?>) parent).addChild(
-                            childDataSchemaNodes, elementList ? ChildReusePolicy.REUSE : ChildReusePolicy.NOOP);
+                        final var qname = childDataSchemaNodes.peekLast().getQName();
+                        final var child = ((CompositeNodeDataWithSchema<?>) parent).addChild(childDataSchemaNodes,
+                            elementList ? ChildReusePolicy.REUSE : ChildReusePolicy.NOOP);
                         stack.enterDataTree(qname);
                         read(in, child, rootElement);
                         stack.exit();
@@ -551,17 +538,14 @@ public final class XmlParserStream implements Closeable, Flushable {
                         // Parent can potentially hold a mount point, let's see if there is a label present. We
                         // explicitly unmask Optional to null so as to not to lead us on to functional programming,
                         // because ...
-                        final MountPointSchemaNode mount;
-                        if (parentSchema instanceof ContainerSchemaNode container) {
-                            mount = MountPointSchemaNode.streamAll(container).findFirst().orElse(null);
-                        } else if (parentSchema instanceof ListSchemaNode list) {
-                            mount = MountPointSchemaNode.streamAll(list).findFirst().orElse(null);
-                        } else if (parentSchema instanceof ContainerLike) {
-                            mount = null;
-                        } else {
-                            throw new XMLStreamException("Unhandled mount-aware schema " + parentSchema,
+                        final var mount = switch (parentSchema) {
+                            case ContainerSchemaNode container ->
+                                MountPointSchemaNode.streamAll(container).findFirst().orElse(null);
+                            case ListSchemaNode list -> MountPointSchemaNode.streamAll(list).findFirst().orElse(null);
+                            case ContainerLike containerLike -> null;
+                            default -> throw new XMLStreamException("Unhandled mount-aware schema " + parentSchema,
                                 in.getLocation());
-                        }
+                        };
 
                         if (mount != null) {
                             final var label = mount.asEffectiveStatement().argument();
@@ -625,7 +609,7 @@ public final class XmlParserStream implements Closeable, Flushable {
             }
 
             LOG.warn("Encountered unknown element {} from YANG Library namespace", localName);
-        } else if (SchemaMountConstants.RFC8528_MODULE.getNamespace().equals(namespace)) {
+        } else if (SchemaMountConstants.MODULE_NAMESPACE.equals(namespace)) {
             mount.setSchemaMounts(child);
             return;
         }
@@ -680,21 +664,27 @@ public final class XmlParserStream implements Closeable, Flushable {
 
     private Object translateValueByType(final Object value, final DataSchemaNode node,
             final NamespaceContext namespaceCtx) {
-        if (node instanceof AnyxmlSchemaNode) {
-            checkArgument(value instanceof Document);
-            /*
-             * FIXME: Figure out some YANG extension dispatch, which will reuse JSON parsing or XML parsing -
-             *        anyxml is not well-defined in JSON.
-             */
-            return new DOMSource(((Document) value).getDocumentElement());
-        } else if (node instanceof AnydataSchemaNode) {
-            checkArgument(value instanceof Document);
-            return new DOMSourceAnydata(new DOMSource(((Document) value).getDocumentElement()));
-        } else if (node instanceof TypedDataSchemaNode typedNode) {
-            checkArgument(value instanceof String);
-            return codecs.codecFor(typedNode, stack).parseValue(namespaceCtx, (String) value);
-        } else {
-            throw new IllegalStateException("Unhandled schema " + node);
+        return switch (node) {
+            // FIXME: Figure out some YANG extension dispatch, which will reuse JSON parsing or XML parsing -
+            //        anyxml is not well-defined in JSON.
+            case AnydataSchemaNode anydata ->
+                new DOMSourceAnydata(new DOMSource(checkDocument(value).getDocumentElement()));
+            case AnyxmlSchemaNode anyxml -> new DOMSource(checkDocument(value).getDocumentElement());
+            case TypedDataSchemaNode typedNode ->
+                codecs.codecFor(typedNode, stack).parseValue(namespaceCtx, checkValue(String.class, value));
+            default -> throw new IllegalStateException("Unhandled schema " + node);
+        };
+    }
+
+    private static Document checkDocument(final Object value) {
+        return checkValue(Document.class, value);
+    }
+
+    private static <T> T checkValue(final Class<T> type, final Object value) {
+        try {
+            return type.cast(requireNonNull(value));
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("Unexpected value while expecting a " + type.getName(), e);
         }
     }
 
@@ -718,12 +708,12 @@ public final class XmlParserStream implements Closeable, Flushable {
 
     private Optional<QNameModule> resolveXmlNamespace(final String xmlNamespace) {
         return resolvedNamespaces.computeIfAbsent(xmlNamespace, nsUri -> {
-            final var it = codecs.getEffectiveModelContext().findModuleStatements(XMLNamespace.of(nsUri)).iterator();
+            final var it = codecs.modelContext().findModuleStatements(XMLNamespace.of(nsUri)).iterator();
             return it.hasNext() ? Optional.of(it.next().localQNameModule()) : Optional.empty();
         });
     }
 
     private QNameModule rawXmlNamespace(final String xmlNamespace) {
-        return rawNamespaces.computeIfAbsent(xmlNamespace, nsUri -> QNameModule.create(XMLNamespace.of(nsUri)));
+        return rawNamespaces.computeIfAbsent(xmlNamespace, QNameModule::of);
     }
 }