X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fimpl%2Fcodec%2Fxml%2FXmlStreamUtils.java;h=1a235d92618bee4b4abc65a081b35ebdcaa0957d;hb=774cfc9c95b20bad92b33a6398793c940f1339a2;hp=1d319a5acec49db92d0fb909e266ebc85b313508;hpb=616f6ffb09eab12c34f7c36491a87f6f549c1599;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java index 1d319a5ace..1a235d9261 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java @@ -1,32 +1,38 @@ package org.opendaylight.yangtools.yang.data.impl.codec.xml; import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; - import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; import java.util.Map.Entry; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; - import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.AttributesContainer; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; 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.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,9 +44,15 @@ import org.slf4j.LoggerFactory; public class XmlStreamUtils { private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class); private final XmlCodecProvider codecProvider; + private final Optional schemaContext; protected XmlStreamUtils(final XmlCodecProvider codecProvider) { + this(codecProvider, null); + } + + private XmlStreamUtils(XmlCodecProvider codecProvider, SchemaContext schemaContext) { this.codecProvider = Preconditions.checkNotNull(codecProvider); + this.schemaContext = Optional.fromNullable(schemaContext); } /** @@ -84,7 +96,7 @@ public class XmlStreamUtils { * @param id InstanceIdentifier * @throws XMLStreamException */ - public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifier id) throws XMLStreamException { + public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException { Preconditions.checkNotNull(writer, "Writer may not be null"); Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); @@ -92,7 +104,10 @@ public class XmlStreamUtils { final String str = XmlUtils.encodeIdentifier(prefixes, id); for (Entry e: prefixes.getPrefixes()) { - writer.writeNamespace(e.getValue(), e.getKey().toString()); + final String ns = e.getKey().toString(); + final String p = e.getValue(); + + writer.writeNamespace(p, ns); } writer.writeCharacters(str); } @@ -116,12 +131,11 @@ public class XmlStreamUtils { } /** - * Short-hand for {@link #writeDataDocument(XMLStreamWriter, CompositeNode, SchemaNode, XmlCodecProvider)} with + * Short-hand for {@link #writeDocument(XMLStreamWriter, CompositeNode, SchemaNode)})} with * null SchemaNode. * * @param writer XML Stream writer * @param data data node - * @param schema corresponding schema node, may be null * @throws XMLStreamException if an encoding problem occurs */ public void writeDocument(final XMLStreamWriter writer, final CompositeNode data) throws XMLStreamException { @@ -139,27 +153,45 @@ public class XmlStreamUtils { */ public void writeElement(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { final QName qname = data.getNodeType(); - final String pfx = qname.getPrefix() != null ? qname.getPrefix() : ""; final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : ""; + final String pfx = ""; if (isEmptyElement(data)) { - writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + if (hasAttributes(data)) { + writer.writeStartElement(pfx, qname.getLocalName(), ns); + final RandomPrefix randomPrefix = new RandomPrefix(); + writeAttributes(writer, (AttributesContainer) data, randomPrefix); + writer.writeEndElement(); + } else { + writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + } return; } writer.writeStartElement(pfx, qname.getLocalName(), ns); - if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { - for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { - writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue()); - } + writeValue(writer, data, schema); + writer.writeEndElement(); + } + + /** + * Write a value into a XML stream writer. This method assumes the start and end of element is + * emitted by the caller. + * + * @param writer XML Stream writer + * @param data data node + * @param schema Schema node + * @throws XMLStreamException if an encoding problem occurs + */ + public void writeValue(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { + if (hasAttributes(data)) { + RandomPrefix randomPrefix = new RandomPrefix(); + writeAttributes(writer, (AttributesContainer) data, randomPrefix); } if (data instanceof SimpleNode) { // Simple node - if (schema instanceof LeafListSchemaNode) { - writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue()); - } else if (schema instanceof LeafSchemaNode) { - writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue()); + if (schema instanceof LeafListSchemaNode || schema instanceof LeafSchemaNode) { + writeValue(writer, schema, data.getValue()); } else { Object value = data.getValue(); if (value != null) { @@ -168,22 +200,71 @@ public class XmlStreamUtils { } } else { // CompositeNode - for (Node child : ((CompositeNode) data).getValue()) { - DataSchemaNode childSchema = null; - if (schema instanceof DataNodeContainer) { - childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); - if (LOG.isDebugEnabled()) { - if (childSchema == null) { - LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName()); - } - } + final CompositeNode castedData = ((CompositeNode) data); + final DataNodeContainer castedSchema; + if (schema instanceof DataNodeContainer) { + castedSchema = (DataNodeContainer) schema; + } else { + castedSchema = null; + } + final Collection keyLeaves; + if (schema instanceof ListSchemaNode) { + keyLeaves = ((ListSchemaNode) schema).getKeyDefinition(); + + } else { + keyLeaves = Collections.emptyList(); + + } + for (QName key : keyLeaves) { + SimpleNode keyLeaf = castedData.getFirstSimpleByName(key); + if(keyLeaf != null) { + writeChildElement(writer,keyLeaf,castedSchema); } + } - writeElement(writer, child, childSchema); + for (Node child : castedData.getValue()) { + if(keyLeaves.contains(child.getNodeType())) { + // Skip key leaf which was written by previous for loop. + continue; + } + writeChildElement(writer,child,castedSchema); } } + } - writer.writeEndElement(); + private void writeChildElement(XMLStreamWriter writer, Node child, DataNodeContainer parentSchema) throws XMLStreamException { + DataSchemaNode childSchema = null; + if (parentSchema != null) { + childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), parentSchema.getChildNodes()).orNull(); + if ((childSchema == null) && LOG.isDebugEnabled()) { + LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName()); + } + } + writeElement(writer, child, childSchema); + } + + private static void writeAttributes(final XMLStreamWriter writer, final AttributesContainer data, final RandomPrefix randomPrefix) throws XMLStreamException { + for (Entry attribute : data.getAttributes().entrySet()) { + writeAttribute(writer, attribute, randomPrefix); + } + } + + private static boolean hasAttributes(final Node data) { + if (data instanceof AttributesContainer) { + final Map c = ((AttributesContainer) data).getAttributes(); + return c != null && !c.isEmpty(); + } else { + return false; + } + } + + @VisibleForTesting + static void writeAttribute(final XMLStreamWriter writer, final Entry attribute, final RandomPrefix randomPrefix) + throws XMLStreamException { + final QName key = attribute.getKey(); + final String prefix = randomPrefix.encodePrefix(key.getNamespace()); + writer.writeAttribute("xmlns:" + prefix, key.getNamespace().toString()); + writer.writeAttribute(prefix, key.getNamespace().toString(), key.getLocalName(), attribute.getValue()); } /** @@ -191,8 +272,40 @@ public class XmlStreamUtils { * emitted by the caller. * * @param writer XML Stream writer - * @param data data node - * @param schema Schema node + * @param schemaNode Schema node that describes the value + * @param value data value + * @throws XMLStreamException if an encoding problem occurs + */ + public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull SchemaNode schemaNode, final Object value) throws XMLStreamException { + if (value == null) { + LOG.debug("Value of {}:{} is null, not encoding it", schemaNode.getQName().getNamespace(), schemaNode.getQName().getLocalName()); + return; + } + + Preconditions.checkArgument(schemaNode instanceof LeafSchemaNode || schemaNode instanceof LeafListSchemaNode, + "Unable to write value for node %s, only nodes of type: leaf and leaf-list can be written at this point", schemaNode.getQName()); + + TypeDefinition type = schemaNode instanceof LeafSchemaNode ? + ((LeafSchemaNode) schemaNode).getType(): + ((LeafListSchemaNode) schemaNode).getType(); + + TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); + + if (schemaContext.isPresent() && baseType instanceof LeafrefTypeDefinition) { + LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) baseType; + baseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypeDefinition, schemaContext.get(), schemaNode); + } + + writeValue(writer, baseType, value); + } + + /** + * Write a value into a XML stream writer. This method assumes the start and end of element is + * emitted by the caller. + * + * @param writer XML Stream writer + * @param type data type. In case of leaf ref this should be the type of leaf being referenced + * @param value data value * @throws XMLStreamException if an encoding problem occurs */ public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition type, final Object value) throws XMLStreamException { @@ -200,8 +313,8 @@ public class XmlStreamUtils { LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName()); return; } + TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); - final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); if (baseType instanceof IdentityrefTypeDefinition) { write(writer, (IdentityrefTypeDefinition) baseType, value); } else if (baseType instanceof InstanceIdentifierTypeDefinition) { @@ -224,17 +337,14 @@ public class XmlStreamUtils { } } + @SuppressWarnings("deprecation") private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { if (value instanceof QName) { final QName qname = (QName) value; - final String prefix; - if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { - prefix = qname.getPrefix(); - } else { - prefix = "x"; - } + final String prefix = "x"; - writer.writeNamespace(prefix, qname.getNamespace().toString()); + final String ns = qname.getNamespace().toString(); + writer.writeNamespace(prefix, ns); writer.writeCharacters(prefix + ':' + qname.getLocalName()); } else { LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); @@ -243,11 +353,15 @@ public class XmlStreamUtils { } private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { - if (value instanceof InstanceIdentifier) { - write(writer, (InstanceIdentifier)value); + if (value instanceof YangInstanceIdentifier) { + write(writer, (YangInstanceIdentifier)value); } else { - LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); writer.writeCharacters(String.valueOf(value)); } } + + public static XmlStreamUtils create(XmlCodecProvider codecProvider, SchemaContext schemaContext) { + return new XmlStreamUtils(codecProvider, schemaContext); + } }