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 397dc44323dbd48376f2b8902550da617b9226ff..e3ccd1ea58c388f11996184eacd38afe31689f70 100644 (file)
@@ -9,26 +9,23 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Strings;
+import com.google.common.base.VerifyException;
 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;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
-import java.util.Set;
 import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 import javax.xml.stream.XMLStreamConstants;
@@ -42,16 +39,17 @@ import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stax.StAXSource;
 import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
-import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
-import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
-import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
-import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants;
-import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode;
 import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaNode;
 import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
+import org.opendaylight.yangtools.yang.common.AnnotationName;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
+import org.opendaylight.yangtools.yang.data.api.schema.MountPointContextFactory.ContainerName;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.util.AbstractMountPointDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
@@ -60,6 +58,7 @@ import org.opendaylight.yangtools.yang.data.util.AnydataNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema.ChildReusePolicy;
 import org.opendaylight.yangtools.yang.data.util.ContainerNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.data.util.LeafListEntryNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema;
@@ -67,7 +66,6 @@ import org.opendaylight.yangtools.yang.data.util.ListEntryNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.data.util.MountPointData;
 import org.opendaylight.yangtools.yang.data.util.MultipleEntryDataWithSchema;
-import org.opendaylight.yangtools.yang.data.util.OperationAsContainer;
 import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils;
 import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema;
 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
@@ -78,17 +76,16 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
+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
@@ -112,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";
@@ -151,19 +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 EffectiveStatement<?, ?> stmt = stack.currentStatement();
-            if (stmt instanceof DataSchemaNode) {
-                parentNode = (DataSchemaNode) stmt;
-            } else if (stmt instanceof OperationDefinition) {
-                parentNode = OperationAsContainer.of((OperationDefinition) stmt);
-            } 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);
+        };
     }
 
     /**
@@ -233,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);
     }
 
     /**
@@ -242,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);
     }
 
     /**
@@ -256,6 +250,46 @@ public final class XmlParserStream implements Closeable, Flushable {
         return create(writer, mountCtx, parentNode, true);
     }
 
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final Absolute parentNode) {
+        return create(writer, mountCtx, parentNode, true);
+    }
+
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final Absolute parentNode, final boolean strictParsing) {
+        return create(writer, XmlCodecFactory.create(mountCtx), parentNode, strictParsing);
+    }
+
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
+            final Absolute parentNode) {
+        return create(writer, codecs, parentNode, true);
+    }
+
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
+            final Absolute parentNode, final boolean strictParsing) {
+        return new XmlParserStream(writer, codecs,
+            SchemaInferenceStack.of(codecs.modelContext(), parentNode), strictParsing);
+    }
+
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final YangInstanceIdentifier parentNode) {
+        return create(writer, mountCtx, parentNode, true);
+    }
+
+    @Beta
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final YangInstanceIdentifier parentNode, final boolean strictParsing) {
+        final var init = DataSchemaContextTree.from(mountCtx.modelContext())
+            .enterPath(parentNode)
+            .orElseThrow();
+        return new XmlParserStream(writer, XmlCodecFactory.create(mountCtx), init.stack(), strictParsing);
+    }
+
     /**
      * Utility method for use when caching {@link XmlCodecFactory} is not feasible. Users with high performance
      * requirements should use {@link #create(NormalizedNodeStreamWriter, XmlCodecFactory, EffectiveStatementInference)}
@@ -276,34 +310,13 @@ 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
      */
-    public XmlParserStream parse(final XMLStreamReader reader) throws XMLStreamException, URISyntaxException,
-            IOException, SAXException {
+    public XmlParserStream parse(final XMLStreamReader reader) throws XMLStreamException, IOException {
         if (reader.hasNext()) {
             reader.nextTag();
-            final AbstractNodeDataWithSchema<?> nodeDataWithSchema;
-            if (parentNode instanceof ContainerLike) {
-                nodeDataWithSchema = new ContainerNodeDataWithSchema((ContainerLike) parentNode);
-            } else if (parentNode instanceof ListSchemaNode) {
-                nodeDataWithSchema = new ListNodeDataWithSchema((ListSchemaNode) parentNode);
-            } else if (parentNode instanceof AnyxmlSchemaNode) {
-                nodeDataWithSchema = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) parentNode);
-            } else if (parentNode instanceof LeafSchemaNode) {
-                nodeDataWithSchema = new LeafNodeDataWithSchema((LeafSchemaNode) parentNode);
-            } else if (parentNode instanceof LeafListSchemaNode) {
-                nodeDataWithSchema = new LeafListNodeDataWithSchema((LeafListSchemaNode) parentNode);
-            } else if (parentNode instanceof AnydataSchemaNode) {
-                nodeDataWithSchema = new AnydataNodeDataWithSchema((AnydataSchemaNode) parentNode);
-            } else {
-                throw new IllegalStateException("Unsupported schema node type " + parentNode.getClass() + ".");
-            }
-
+            final var nodeDataWithSchema = AbstractNodeDataWithSchema.of(parentNode);
             read(reader, nodeDataWithSchema, reader.getLocalName());
             nodeDataWithSchema.write(writer);
         }
@@ -321,22 +334,17 @@ 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));
     }
 
     private ImmutableMap<QName, Object> getElementAttributes(final XMLStreamReader in) {
         checkState(in.isStartElement(), "Attributes can be extracted only from START_ELEMENT.");
-        final Map<QName, Object> attributes = new LinkedHashMap<>();
+        final var attributes = new LinkedHashMap<QName, Object>();
 
         for (int attrIndex = 0; attrIndex < in.getAttributeCount(); attrIndex++) {
             final String attributeNS = in.getAttributeNamespace(attrIndex);
@@ -357,11 +365,11 @@ public final class XmlParserStream implements Closeable, Flushable {
             // Cross-relate attribute namespace to the module
             final Optional<QNameModule> optModule = resolveXmlNamespace(attributeNS);
             if (optModule.isPresent()) {
-                final QName qname = QName.create(optModule.get(), localName);
-                final Optional<AnnotationSchemaNode> optAnnotation = AnnotationSchemaNode.find(
-                    codecs.getEffectiveModelContext(), qname);
+                final QName qname = QName.create(optModule.orElseThrow(), localName);
+                final var optAnnotation = AnnotationSchemaNode.find(codecs.modelContext(),
+                    new AnnotationName(qname));
                 if (optAnnotation.isPresent()) {
-                    final AnnotationSchemaNode schema = optAnnotation.get();
+                    final AnnotationSchemaNode schema = optAnnotation.orElseThrow();
                     final Object value = codecs.codecFor(schema, stack)
                         .parseValue(in.getNamespaceContext(), attrValue);
                     attributes.put(schema.getQName(), value);
@@ -410,7 +418,7 @@ public final class XmlParserStream implements Closeable, Flushable {
 
         if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) {
             parent.setAttributes(getElementAttributes(in));
-            setValue((SimpleNodeDataWithSchema<?>) parent, in.getElementText().trim(), in.getNamespaceContext());
+            setValue((SimpleNodeDataWithSchema<?>) parent, in.getElementText(), in.getNamespaceContext());
             if (isNextEndDocument(in)) {
                 return;
             }
@@ -426,21 +434,22 @@ public final class XmlParserStream implements Closeable, Flushable {
         }
 
         if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
-            String xmlElementName = in.getLocalName();
-            while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
+            final String localName = in.getLocalName();
+            final String namespaceURI = in.getNamespaceURI();
+            // aggregate current and subsequent nodes having same localName and namespace
+            // into set of entries belonging to current parent node
+            while (localName.equals(in.getLocalName()) && namespaceURI.equals(in.getNamespaceURI())) {
                 read(in, newEntryNode(parent), rootElement);
                 if (in.getEventType() == XMLStreamConstants.END_DOCUMENT
                         || in.getEventType() == XMLStreamConstants.END_ELEMENT) {
                     break;
                 }
-                xmlElementName = in.getLocalName();
             }
-
             return;
         }
 
-        if (parent instanceof AnyXmlNodeDataWithSchema) {
-            setValue((AnyXmlNodeDataWithSchema) parent, readAnyXmlValue(in), in.getNamespaceContext());
+        if (parent instanceof AnyXmlNodeDataWithSchema anyxml) {
+            setValue(anyxml, readAnyXmlValue(in), in.getNamespaceContext());
             if (isNextEndDocument(in)) {
                 return;
             }
@@ -452,8 +461,7 @@ public final class XmlParserStream implements Closeable, Flushable {
             return;
         }
 
-        if (parent instanceof AnydataNodeDataWithSchema) {
-            final AnydataNodeDataWithSchema anydata = (AnydataNodeDataWithSchema) parent;
+        if (parent instanceof AnydataNodeDataWithSchema anydata) {
             anydata.setObjectModel(DOMSourceAnydata.class);
             anydata.setAttributes(getElementAttributes(in));
             setValue(anydata, readAnyXmlValue(in), in.getNamespaceContext());
@@ -473,7 +481,7 @@ public final class XmlParserStream implements Closeable, Flushable {
                 // FIXME: 7.0.0: why do we even need this tracker? either document it or remove it.
                 //               it looks like it is a crude duplicate finder, which should really be handled via
                 //               ChildReusePolicy.REJECT
-                final Set<Entry<String, String>> namesakes = new HashSet<>();
+                final var namesakes = new HashSet<Entry<String, String>>();
                 while (in.hasNext()) {
                     final String xmlElementName = in.getLocalName();
                     final DataSchemaNode parentSchema = parent.getSchema();
@@ -500,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) {
@@ -517,43 +525,43 @@ 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();
                         continue;
                     }
 
-                    if (parent instanceof AbstractMountPointDataWithSchema) {
-                        // Parent can potentially hold a mount point, let's see if there is a label present
-                        final Optional<MountPointSchemaNode> optMount;
-                        if (parentSchema instanceof ContainerSchemaNode) {
-                            optMount = MountPointSchemaNode.streamAll((ContainerSchemaNode) parentSchema).findFirst();
-                        } else if (parentSchema instanceof ListSchemaNode) {
-                            optMount = MountPointSchemaNode.streamAll((ListSchemaNode) parentSchema).findFirst();
-                        } else {
-                            throw new XMLStreamException("Unhandled mount-aware schema " + parentSchema,
+                    if (parent instanceof AbstractMountPointDataWithSchema<?> mountParent) {
+                        // 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 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 (optMount.isPresent()) {
-                            final MountPointIdentifier mountId = MountPointIdentifier.of(optMount.get().getQName());
+                        if (mount != null) {
+                            final var label = mount.asEffectiveStatement().argument();
                             LOG.debug("Assuming node {} and namespace {} belongs to mount point {}", xmlElementName,
-                                nsUri, mountId);
+                                nsUri, label);
 
-                            final Optional<MountPointContextFactory> optFactory = codecs.mountPointContext()
-                                    .findMountPoint(mountId);
+                            final var optFactory = codecs.mountPointContext().findMountPoint(label);
                             if (optFactory.isPresent()) {
-                                final MountPointData mountData = ((AbstractMountPointDataWithSchema<?>) parent)
-                                        .getMountPointData(mountId, optFactory.get());
+                                final var mountData = mountParent.getMountPointData(label, optFactory.orElseThrow());
                                 addMountPointChild(mountData, nsUri, xmlElementName,
                                     new DOMSource(readAnyXmlValue(in).getDocumentElement()));
+                                // ... this call does not work with functional programming
                                 continue;
                             }
 
-                            LOG.debug("Mount point {} not attached", mountId);
+                            LOG.debug("Mount point {} not attached", label);
                         }
                     }
 
@@ -593,15 +601,15 @@ public final class XmlParserStream implements Closeable, Flushable {
     private static void addMountPointChild(final MountPointData mount, final XMLNamespace namespace,
             final String localName, final DOMSource source) {
         final DOMSourceMountPointChild child = new DOMSourceMountPointChild(source);
-        if (YangLibraryConstants.MODULE_NAMESPACE.equals(namespace)) {
-            final Optional<ContainerName> optName = ContainerName.forLocalName(localName);
-            if (optName.isPresent()) {
-                mount.setContainer(optName.get(), child);
+        if (YangConstants.YANG_LIBRARY_NAMESPACE.equals(namespace)) {
+            final var optName = ContainerName.forLocalName(localName);
+            if (optName != null) {
+                mount.setContainer(optName, child);
                 return;
             }
 
             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;
         }
@@ -656,27 +664,35 @@ 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());
-        }
-        if (node instanceof AnydataSchemaNode) {
-            checkArgument(value instanceof Document);
-            return new DOMSourceAnydata(new DOMSource(((Document) value).getDocumentElement()));
-        }
+        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);
+        };
+    }
 
-        checkArgument(node instanceof TypedDataSchemaNode);
-        checkArgument(value instanceof String);
-        return codecs.codecFor((TypedDataSchemaNode) node, stack).parseValue(namespaceCtx, (String) value);
+    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);
+        }
     }
 
     private static AbstractNodeDataWithSchema<?> newEntryNode(final AbstractNodeDataWithSchema<?> parent) {
-        verify(parent instanceof MultipleEntryDataWithSchema, "Unexpected parent %s", parent);
-        return ((MultipleEntryDataWithSchema<?>) parent).newChildEntry();
+        if (parent instanceof MultipleEntryDataWithSchema<?> multiEntry) {
+            return multiEntry.newChildEntry();
+        }
+        throw new VerifyException("Unexpected parent " + parent);
     }
 
     @Override
@@ -692,13 +708,12 @@ public final class XmlParserStream implements Closeable, Flushable {
 
     private Optional<QNameModule> resolveXmlNamespace(final String xmlNamespace) {
         return resolvedNamespaces.computeIfAbsent(xmlNamespace, nsUri -> {
-            final Iterator<? extends Module> it = codecs.getEffectiveModelContext().findModules(XMLNamespace.of(nsUri))
-                    .iterator();
-            return it.hasNext() ? Optional.of(it.next().getQNameModule()) : Optional.empty();
+            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);
     }
 }