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;
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;
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;
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;
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
* 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";
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);
+ };
}
/**
*/
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);
}
/**
* 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);
}
/**
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)}
* 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);
}
* 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);
// 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);
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;
}
}
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;
}
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());
// 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();
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) {
}
// 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);
}
}
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;
}
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
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);
}
}