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%2FXmlDocumentUtils.java;h=f0d82c33b9829ee97e85d063c2fcff0b40a27a94;hb=6804543abd6a520dc6c293340aa577748d17da74;hp=e011d3ec83bb13b85434c3c2261f061490fe7b57;hpb=9ec4bb50a1cf8bd773459eb26e25c87606c8f31c;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java index e011d3ec83..f0d82c33b9 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java @@ -18,7 +18,10 @@ import com.google.common.collect.ImmutableList; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -26,18 +29,20 @@ import javax.activation.UnsupportedDataTypeException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; 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.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; @@ -47,11 +52,11 @@ 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.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Attr; @@ -60,6 +65,9 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class XmlDocumentUtils { + private final static String RPC_REPLY_LOCAL_NAME = "rpc-reply"; + private final static String RPC_REPLY_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0"; + private final static QName RPC_REPLY_QNAME = QName.create(URI.create(RPC_REPLY_NAMESPACE), null, RPC_REPLY_LOCAL_NAME); private static class ElementWithSchemaContext { Element element; @@ -79,15 +87,9 @@ public class XmlDocumentUtils { } } - private static final XmlCodecProvider DEFAULT_XML_VALUE_CODEC_PROVIDER = new XmlCodecProvider() { - - @Override - public TypeDefinitionAwareCodec> codecFor(final TypeDefinition baseType) { - return TypeDefinitionAwareCodec.from(baseType); - } - }; - + public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation"); private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class); + private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory(); /** * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding @@ -106,14 +108,19 @@ public class XmlDocumentUtils { Preconditions.checkNotNull(data); Preconditions.checkNotNull(schema); - Document doc = getDocument(); + if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) { + throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); + } - if (schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode) { - doc.appendChild(createXmlRootElement(doc, data, (SchemaNode) schema, codecProvider)); - return doc; - } else { - throw new UnsupportedDataTypeException( - "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); + final DOMResult result = new DOMResult(getDocument()); + try { + final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result); + XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema); + writer.close(); + return (Document)result.getNode(); + } catch (XMLStreamException e) { + logger.error("Failed to serialize data {}", data, e); + return null; } } @@ -139,158 +146,66 @@ public class XmlDocumentUtils { * @return new instance of XML Document * @throws UnsupportedDataTypeException */ - public static Document toDocument(final CompositeNode data, final XmlCodecProvider codecProvider) - throws UnsupportedDataTypeException { - Preconditions.checkNotNull(data); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - Document doc = null; + public static Document toDocument(final CompositeNode data, final XmlCodecProvider codecProvider) { + final DOMResult result = new DOMResult(getDocument()); try { - DocumentBuilder bob = dbf.newDocumentBuilder(); - doc = bob.newDocument(); - } catch (ParserConfigurationException e) { + final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result); + XmlStreamUtils.create(codecProvider).writeDocument(writer, data); + writer.close(); + return (Document)result.getNode(); + } catch (XMLStreamException e) { + logger.error("Failed to serialize data {}", data, e); return null; } - - doc.appendChild(createXmlRootElement(doc, data, null, codecProvider)); - return doc; } - private static Element createXmlRootElement(final Document doc, final Node data, final SchemaNode schema, - final XmlCodecProvider codecProvider) throws UnsupportedDataTypeException { - Element itemEl = createElementFor(doc, data); - if (data instanceof SimpleNode) { - if (schema instanceof LeafListSchemaNode) { - writeValueByType(itemEl, (SimpleNode) data, ((LeafListSchemaNode) schema).getType(), - (DataSchemaNode) schema, codecProvider); - } else if (schema instanceof LeafSchemaNode) { - writeValueByType(itemEl, (SimpleNode) data, ((LeafSchemaNode) schema).getType(), - (DataSchemaNode) schema, codecProvider); - } else { - Object value = data.getValue(); - if (value != null) { - itemEl.setTextContent(String.valueOf(value)); - } - } - } else { // CompositeNode - for (Node child : ((CompositeNode) data).getValue()) { - DataSchemaNode childSchema = null; - if (schema instanceof DataNodeContainer) { - childSchema = findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); - if (logger.isDebugEnabled()) { - if (childSchema == null) { - logger.debug("Probably the data node \"" - + ((child == null) ? "" : child.getNodeType().getLocalName()) - + "\" is not conform to schema"); - } - } - } - itemEl.appendChild(createXmlRootElement(doc, child, childSchema, codecProvider)); - } - } - return itemEl; - } - - public static Element createElementFor(final Document doc, final Node data) { - QName dataType = data.getNodeType(); - Element ret; - if (dataType.getNamespace() != null) { - ret = doc.createElementNS(dataType.getNamespace().toString(), dataType.getLocalName()); + private static final Element createElementFor(final Document doc, final QName qname, final Object obj) { + final Element ret; + if (qname.getNamespace() != null) { + ret = doc.createElementNS(qname.getNamespace().toString(), qname.getLocalName()); } else { - ret = doc.createElementNS(null, dataType.getLocalName()); + ret = doc.createElementNS(null, qname.getLocalName()); } - if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { - for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { - ret.setAttributeNS(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), - attribute.getValue()); - } + if (obj instanceof AttributesContainer) { + final Map attrs = ((AttributesContainer)obj).getAttributes(); + + if (attrs != null) { + for (Entry attribute : attrs.entrySet()) { + ret.setAttributeNS(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), + attribute.getValue()); + } + } } + return ret; } - public static void writeValueByType(final Element element, final SimpleNode node, final TypeDefinition type, - final DataSchemaNode schema, final XmlCodecProvider codecProvider) { - - Object nodeValue = node.getValue(); - - writeValueByType(element, type, codecProvider, nodeValue); + public static Element createElementFor(final Document doc, final Node data) { + return createElementFor(doc, data.getNodeType(), data); } - public static void writeValueByType(final Element element, final TypeDefinition type, final XmlCodecProvider codecProvider, final Object nodeValue) { - TypeDefinition baseType = resolveBaseTypeFrom(type); - if (baseType instanceof IdentityrefTypeDefinition) { - if (nodeValue instanceof QName) { - QName value = (QName) nodeValue; - String prefix = "x"; - if (value.getPrefix() != null && !value.getPrefix().isEmpty()) { - prefix = value.getPrefix(); - } - element.setAttribute("xmlns:" + prefix, value.getNamespace().toString()); - element.setTextContent(prefix + ":" + value.getLocalName()); - } else { - Object value = nodeValue; - logger.debug("Value of {}:{} is not instance of QName but is {}", baseType.getQName().getNamespace(), - baseType.getQName().getLocalName(), value != null ? value.getClass() : "null"); - if (value != null) { - element.setTextContent(String.valueOf(value)); - } - } - } else if (baseType instanceof InstanceIdentifierTypeDefinition) { - if (nodeValue instanceof InstanceIdentifier) { - InstanceIdentifierForXmlCodec.serialize((InstanceIdentifier)nodeValue,element); - } else { - Object value = nodeValue; - logger.debug("Value of {}:{} is not instance of InstanceIdentifier but is {}", baseType.getQName() - .getNamespace(), // - baseType.getQName().getLocalName(), value != null ? value.getClass() : "null"); - if (value != null) { - element.setTextContent(String.valueOf(value)); - } - } - } else { - if (nodeValue != null) { - final TypeDefinitionAwareCodec codec = codecProvider.codecFor(baseType); - if (codec != null) { - try { - final String text = codec.serialize(nodeValue); - element.setTextContent(text); - } catch (ClassCastException e) { - logger.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", nodeValue, baseType, e); - element.setTextContent(String.valueOf(nodeValue)); - } - } else { - logger.error("Failed to find codec for {}, falling back to using stream", baseType); - element.setTextContent(String.valueOf(nodeValue)); - } - } - } + public static Element createElementFor(final Document doc, final NormalizedNode data) { + return createElementFor(doc, data.getNodeType(), data); } - public final static TypeDefinition resolveBaseTypeFrom(final TypeDefinition type) { - TypeDefinition superType = type; - while (superType.getBaseType() != null) { - superType = superType.getBaseType(); - } - return superType; + public static Node toDomNode(final Element xmlElement, final Optional schema, + final Optional codecProvider) { + return toDomNode(xmlElement, schema, codecProvider, Optional.absent()); } public static Node toDomNode(final Element xmlElement, final Optional schema, - final Optional codecProvider) { + final Optional codecProvider, final Optional schemaContext) { if (schema.isPresent()) { - return toNodeWithSchema(xmlElement, schema.get(), codecProvider.or(DEFAULT_XML_VALUE_CODEC_PROVIDER)); + if(schemaContext.isPresent()) { + return toNodeWithSchema(xmlElement, schema.get(), codecProvider.or(XmlUtils.DEFAULT_XML_CODEC_PROVIDER), schemaContext.get()); + } else { + return toNodeWithSchema(xmlElement, schema.get(), codecProvider.or(XmlUtils.DEFAULT_XML_CODEC_PROVIDER)); + } } return toDomNode(xmlElement); } - public static CompositeNode fromElement(final Element xmlElement) { - CompositeNodeBuilder node = ImmutableCompositeNode.builder(); - node.setQName(qNameFromElement(xmlElement)); - - return node.toInstance(); - } - public static QName qNameFromElement(final Element xmlElement) { String namespace = xmlElement.getNamespaceURI(); String localName = xmlElement.getLocalName(); @@ -321,10 +236,11 @@ public class XmlDocumentUtils { if (codec != null) { value = codec.deserialize(text); } + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(schema.getType()); - if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) { value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); - } else if(schema.getType() instanceof IdentityrefTypeDefinition){ + } else if(baseType instanceof IdentityrefTypeDefinition){ value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx); } @@ -344,7 +260,7 @@ public class XmlDocumentUtils { if (codec != null) { value = codec.deserialize(text); } - if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) { value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); } if (value == null) { @@ -362,13 +278,11 @@ public class XmlDocumentUtils { return ImmutableCompositeNode.create(qName, values, modifyAction.orNull()); } - public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation"); - public static Optional getModifyOperationFromAttributes(final Element xmlElement) { Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName()); if(attributeNodeNS == null) { - return Optional.absent(); - } + return Optional.absent(); + } ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue()); Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement); @@ -377,12 +291,12 @@ public class XmlDocumentUtils { } private static void checkQName(final Element xmlElement, final QName qName) { - checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString())); - checkState(qName.getLocalName().equals(xmlElement.getLocalName())); + checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString()), "Not equal: %s to: %s for: %s and: %s", qName.getNamespace(), xmlElement.getNamespaceURI(), qName, xmlElement); + checkState(qName.getLocalName().equals(xmlElement.getLocalName()), "Not equal: %s to: %s for: %s and: %s", qName.getLocalName(), xmlElement.getLocalName(), qName, xmlElement); } - public static final Optional findFirstSchema(final QName qname, final Set dataSchemaNode) { - if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) { + public static final Optional findFirstSchema(final QName qname, final Iterable dataSchemaNode) { + if (dataSchemaNode != null && qname != null) { for (DataSchemaNode dsn : dataSchemaNode) { if (qname.isEqualWithoutRevision(dsn.getQName())) { return Optional. of(dsn); @@ -429,7 +343,7 @@ public class XmlDocumentUtils { return ImmutableCompositeNode.create(qname, values.build()); } - public static List> toDomNodes(final Element element, final Optional> context,final SchemaContext schemaCtx) { + public static List> toDomNodes(final Element element, final Optional> context, final SchemaContext schemaCtx) { return forEachChild(element.getChildNodes(),schemaCtx, new Function>>() { @Override @@ -438,8 +352,8 @@ public class XmlDocumentUtils { QName partialQName = qNameFromElement(input.getElement()); Optional schemaNode = findFirstSchema(partialQName, context.get()); if (schemaNode.isPresent()) { - return Optional.> fromNullable(// - toNodeWithSchema(input.getElement(), schemaNode.get(), DEFAULT_XML_VALUE_CODEC_PROVIDER,input.getSchemaContext())); + return Optional.> fromNullable( + toNodeWithSchema(input.getElement(), schemaNode.get(), XmlUtils.DEFAULT_XML_CODEC_PROVIDER, input.getSchemaContext())); } } return Optional.> fromNullable(toDomNode(input.getElement())); @@ -449,8 +363,8 @@ public class XmlDocumentUtils { } - public static List> toDomNodes(final Element element, final Optional> context) { - return toDomNodes(element,context,null); + public static List> toDomNodes(final Element element, final Optional> context) { + return toDomNodes(element, context, null); } /** @@ -486,9 +400,9 @@ public class XmlDocumentUtils { final Optional notificationDef = findNotification(partialQName, notifications.get()); if (notificationDef.isPresent()) { - final Set dataNodes = notificationDef.get().getChildNodes(); + final Iterable dataNodes = notificationDef.get().getChildNodes(); final List> domNodes = toDomNodes(childElement, - Optional.> fromNullable(dataNodes),schemaCtx); + Optional.> fromNullable(dataNodes),schemaCtx); return ImmutableCompositeNode.create(notificationDef.get().getQName(), domNodes); } } @@ -497,6 +411,60 @@ public class XmlDocumentUtils { return null; } + /** + * Transforms XML Document representing Rpc output body into Composite Node structure based on Rpc definition + * within given Schema Context. The transformation is based on Rpc Definition which is defined in provided Schema Context. + * If Rpc Definition is missing from given Schema Context the method will return null + * + * @param document XML Document containing Output RPC message + * @param rpcName Rpc QName + * @param context Schema Context + * @return Rpc message in Composite Node data structures if Rpc definition is present within provided Schema Context, otherwise + * returns null + */ + public static CompositeNode rpcReplyToDomNodes(final Document document, final QName rpcName, + final SchemaContext context) { + Preconditions.checkNotNull(document); + Preconditions.checkNotNull(rpcName); + Preconditions.checkNotNull(context); + + Optional rpcDefinition = findRpc(rpcName, context); + if (rpcDefinition.isPresent()) { + RpcDefinition rpc = rpcDefinition.get(); + + final Collection outputNode = rpc.getOutput().getChildNodes(); + final Element rpcReplyElement = document.getDocumentElement(); + final QName partialQName = qNameFromElement(rpcReplyElement); + + if (RPC_REPLY_QNAME.equals(partialQName)) { + final List> domNodes = toDomNodes(rpcReplyElement, Optional.fromNullable(outputNode), context); + List> rpcOutNodes = Collections.>singletonList(ImmutableCompositeNode.create( + rpc.getOutput().getQName(), domNodes)); + return ImmutableCompositeNode.create(rpcName, rpcOutNodes); + } + } + return null; + } + + /** + * Method searches given schema context for Rpc Definition with given QName. + * Returns Rpc Definition if is present within given Schema Context, otherwise returns Optional.absent(). + * + * @param rpc Rpc QName + * @param context Schema Context + * @return Rpc Definition if is present within given Schema Context, otherwise returns Optional.absent(). + */ + private static Optional findRpc(final QName rpc, final SchemaContext context) { + Preconditions.checkNotNull(rpc); + Preconditions.checkNotNull(context); + for (final RpcDefinition rpcDefinition : context.getOperations()) { + if ((rpcDefinition != null) && rpc.equals(rpcDefinition.getQName())) { + return Optional.of(rpcDefinition); + } + } + return Optional.absent(); + } + public static CompositeNode notificationToDomNodes(final Document document, final Optional> notifications) { return notificationToDomNodes(document, notifications,null); @@ -534,6 +502,6 @@ public class XmlDocumentUtils { } public static final XmlCodecProvider defaultValueCodecProvider() { - return DEFAULT_XML_VALUE_CODEC_PROVIDER; + return XmlUtils.DEFAULT_XML_CODEC_PROVIDER; } }