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=eeda8954c79231371567d674eda12e8b3fc6b482;hb=e704e6a6d1cc4db7ac1e1f53b54ec3bf51aaecc3;hp=8d3890980bfedc11d39b1dbb09efcd663d0c6885;hpb=173719b3ce9677f36932d41086f7ee987dcb4a50;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 8d3890980b..eeda8954c7 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,31 +1,36 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.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 com.google.common.base.Verify; import java.net.URI; -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.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.common.QNameModule; 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.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; @@ -37,9 +42,20 @@ import org.slf4j.LoggerFactory; public class XmlStreamUtils { private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class); private final XmlCodecProvider codecProvider; + private final Optional schemaContext; + /** + * @deprecated Use {@link #create(XmlCodecProvider)} instead. This method will be hidden and the class + * made final in a future release. + */ + @Deprecated protected XmlStreamUtils(final XmlCodecProvider codecProvider) { + this(codecProvider, null); + } + + private XmlStreamUtils(final XmlCodecProvider codecProvider, final SchemaContext schemaContext) { this.codecProvider = Preconditions.checkNotNull(codecProvider); + this.schemaContext = Optional.fromNullable(schemaContext); } /** @@ -49,180 +65,62 @@ public class XmlStreamUtils { * @return A new instance */ public static XmlStreamUtils create(final XmlCodecProvider codecProvider) { - return new XmlStreamUtils(codecProvider); - } - - /** - * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This - * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally. - * - * @param data Data node - * @return True if the data node will result in empty element body. - */ - public static boolean isEmptyElement(final Node data) { - if (data == null) { - return true; - } - - if (data instanceof CompositeNode) { - return ((CompositeNode) data).getValue().isEmpty(); - } - if (data instanceof SimpleNode) { - return data.getValue() == null; - } - - // Safe default - return false; - } - - /** - * Write an InstanceIdentifier into the output stream. Calling corresponding {@link XMLStreamWriter#writeStartElement(String)} - * and {@link XMLStreamWriter#writeEndElement()} is the responsibility of the caller. - * - * @param writer XML Stream writer - * @param id InstanceIdentifier - * @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"); - - final RandomPrefix prefixes = new RandomPrefix(); - final String str = XmlUtils.encodeIdentifier(prefixes, id); - - for (Entry e: prefixes.getPrefixes()) { - final String ns = e.getKey().toString(); - final String p = e.getValue(); - - writer.writeNamespace(p, ns); - } - writer.writeCharacters(str); + return new XmlStreamUtils(codecProvider, null); } - /** - * Write a full XML document corresponding to a CompositeNode into an XML stream writer. - * - * @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 @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException { - // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); - // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces"); - - writer.writeStartDocument(); - writeElement(writer, data, schema); - writer.writeEndDocument(); - writer.flush(); + public static XmlStreamUtils create(final XmlCodecProvider codecProvider, final SchemaContext schemaContext) { + return new XmlStreamUtils(codecProvider, schemaContext); } - /** - * Short-hand for {@link #writeDocument(XMLStreamWriter, CompositeNode, SchemaNode)})} with - * null SchemaNode. - * - * @param writer XML Stream writer - * @param data data node - * @throws XMLStreamException if an encoding problem occurs - */ - public void writeDocument(final XMLStreamWriter writer, final CompositeNode data) throws XMLStreamException { - writeDocument(writer, data, null); + @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()); } /** - * Write an element into a XML stream writer. This includes the element start/end tags and - * the value of the element. + * 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 + * @param schemaNode Schema node that describes the value + * @param value data value + * @param parent optional parameter of a module QName owning the leaf definition * @throws XMLStreamException if an encoding problem occurs */ - 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() : ""; - - if (isEmptyElement(data)) { - 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); - } + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode, final Object value, final Optional parent) throws XMLStreamException { + if (value == null) { + LOG.debug("Value of {}:{} is null, not encoding it", schemaNode.getQName().getNamespace(), schemaNode.getQName().getLocalName()); return; } - writer.writeStartElement(pfx, qname.getLocalName(), ns); - writeValue(writer, data, schema); - writer.writeEndElement(); - } + 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()); - /** - * 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); - } + TypeDefinition type = schemaNode instanceof LeafSchemaNode ? + ((LeafSchemaNode) schemaNode).getType(): + ((LeafListSchemaNode) schemaNode).getType(); - 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()); - } else { - Object value = data.getValue(); - if (value != null) { - writer.writeCharacters(String.valueOf(value)); - } - } - } else { - // CompositeNode - for (Node child : ((CompositeNode) data).getValue()) { - DataSchemaNode childSchema = null; - if (schema instanceof DataNodeContainer) { - childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); - } else 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); - } - } - } + TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); - 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); + if (schemaContext.isPresent() && baseType instanceof LeafrefTypeDefinition) { + LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) baseType; + baseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypeDefinition, schemaContext.get(), schemaNode); + Verify.verifyNotNull(baseType, "Unable to find base type for leafref node '%s'.", schemaNode.getPath()); } + + writeValue(writer, baseType, value, parent); } - 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; - } + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode, final Object value) throws XMLStreamException { + writeValue(writer, schemaNode, value, Optional.absent()); } - @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()); + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode, final Object value, final QNameModule parent) throws XMLStreamException { + writeValue(writer, schemaNode, value, Optional.of(parent)); } /** @@ -230,65 +128,99 @@ public class XmlStreamUtils { * emitted by the caller. * * @param writer XML Stream writer - * @param type data type + * @param type data type. In case of leaf ref this should be the type of leaf being referenced * @param value data value + * @param parent optional parameter of a module QName owning the leaf definition * @throws XMLStreamException if an encoding problem occurs */ - public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition type, final Object value) throws XMLStreamException { + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition type, final Object value, final Optional parent) throws XMLStreamException { if (value == null) { 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); + if (parent.isPresent()) { + write(writer, (IdentityrefTypeDefinition) baseType, value, parent); + } else { + write(writer, (IdentityrefTypeDefinition) baseType, value, Optional.absent()); + } } else if (baseType instanceof InstanceIdentifierTypeDefinition) { write(writer, (InstanceIdentifierTypeDefinition) baseType, value); } else { - final TypeDefinitionAwareCodec codec = codecProvider.codecFor(baseType); + final TypeDefinitionAwareCodec codec = codecProvider.codecFor(type); String text; if (codec != null) { try { text = codec.serialize(value); } catch (ClassCastException e) { - LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e); + LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, type, e); text = String.valueOf(value); } } else { - LOG.error("Failed to find codec for {}, falling back to using stream", baseType); + LOG.error("Failed to find codec for {}, falling back to using stream", type); text = String.valueOf(value); } writer.writeCharacters(text); } } - @SuppressWarnings("deprecation") - private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition type, final Object value, final QNameModule parent) throws XMLStreamException { + writeValue(writer, type, value, Optional.of(parent)); + } + + public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition type, final Object value) throws XMLStreamException { + writeValue(writer, type, value, Optional.absent()); + } + + @VisibleForTesting + static void write(@Nonnull final XMLStreamWriter writer, @Nonnull final IdentityrefTypeDefinition type, @Nonnull final Object value, final Optional parent) throws XMLStreamException { if (value instanceof QName) { final QName qname = (QName) value; - final String prefix; - if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { - prefix = qname.getPrefix(); + final String prefix = "x"; + + //in case parent is present and same as element namespace write value without namespace + if (parent.isPresent() && qname.getNamespace().equals(parent.get().getNamespace())){ + writer.writeCharacters(qname.getLocalName()); } else { - prefix = "x"; + final String ns = qname.getNamespace().toString(); + writer.writeNamespace(prefix, ns); + writer.writeCharacters(prefix + ':' + qname.getLocalName()); } - 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()); writer.writeCharacters(String.valueOf(value)); } } - private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + private void write(@Nonnull final XMLStreamWriter writer, @Nonnull final InstanceIdentifierTypeDefinition type, @Nonnull final Object value) throws XMLStreamException { if (value instanceof YangInstanceIdentifier) { - write(writer, (YangInstanceIdentifier)value); + writeInstanceIdentifier(writer, (YangInstanceIdentifier)value); } else { LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); writer.writeCharacters(String.valueOf(value)); } } + + public void writeInstanceIdentifier(final XMLStreamWriter writer, final YangInstanceIdentifier value) throws XMLStreamException { + if(schemaContext.isPresent()) { + RandomPrefixInstanceIdentifierSerializer iiCodec = new RandomPrefixInstanceIdentifierSerializer(schemaContext.get()); + String serializedValue = iiCodec.serialize(value); + writeNamespaceDeclarations(writer,iiCodec.getPrefixes()); + writer.writeCharacters(serializedValue); + } else { + LOG.warn("Schema context not present in {}, serializing {} without schema.",this,value); + writeInstanceIdentifier(writer, value); + } + } + + private static void writeNamespaceDeclarations(final XMLStreamWriter writer, final Iterable> prefixes) throws XMLStreamException { + for (Entry e: prefixes) { + final String ns = e.getKey().toString(); + final String p = e.getValue(); + writer.writeNamespace(p, ns); + } + } }