Merge "Yang parser refactoring."
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / XmlDocumentUtils.java
1 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
2
3 import java.util.Set;
4
5 import javax.activation.UnsupportedDataTypeException;
6 import javax.xml.parsers.DocumentBuilder;
7 import javax.xml.parsers.DocumentBuilderFactory;
8 import javax.xml.parsers.ParserConfigurationException;
9
10 import org.opendaylight.yangtools.yang.common.QName;
11 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
12 import org.opendaylight.yangtools.yang.data.api.Node;
13 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
14 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
15 import org.opendaylight.yangtools.yang.model.api.*;
16 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Element;
21
22 import com.google.common.base.Preconditions;
23
24 public class XmlDocumentUtils {
25
26     private static final XmlCodecProvider DEFAULT_XML_VALUE_CODEC_PROVIDER = new XmlCodecProvider() {
27
28         @Override
29         public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(TypeDefinition<?> baseType) {
30             return TypeDefinitionAwareCodec.from(baseType);
31         }
32     };
33
34     private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
35
36     public static Document toDocument(CompositeNode data, DataNodeContainer schema, XmlCodecProvider codecProvider)
37             throws UnsupportedDataTypeException {
38         Preconditions.checkNotNull(data);
39         Preconditions.checkNotNull(schema);
40
41         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
42         Document doc = null;
43         try {
44             DocumentBuilder bob = dbf.newDocumentBuilder();
45             doc = bob.newDocument();
46         } catch (ParserConfigurationException e) {
47             return null;
48         }
49
50         if (schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode) {
51             doc.appendChild(createXmlRootElement(doc, data, (SchemaNode) schema, codecProvider));
52             return doc;
53         } else {
54             throw new UnsupportedDataTypeException(
55                     "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
56         }
57     }
58
59     private static Element createXmlRootElement(Document doc, Node<?> data, SchemaNode schema,
60             XmlCodecProvider codecProvider) throws UnsupportedDataTypeException {
61         QName dataType = data.getNodeType();
62         Element itemEl = doc.createElementNS(dataType.getNamespace().toString(), dataType.getLocalName());
63         if (data instanceof SimpleNode<?>) {
64             if (schema instanceof LeafListSchemaNode) {
65                 writeValueByType(itemEl, (SimpleNode<?>) data, ((LeafListSchemaNode) schema).getType(),
66                         (DataSchemaNode) schema, codecProvider);
67             } else if (schema instanceof LeafSchemaNode) {
68                 writeValueByType(itemEl, (SimpleNode<?>) data, ((LeafSchemaNode) schema).getType(),
69                         (DataSchemaNode) schema, codecProvider);
70             } else {
71                 Object value = data.getValue();
72                 if (value != null) {
73                     itemEl.setTextContent(String.valueOf(value));
74                 }
75             }
76         } else { // CompositeNode
77             for (Node<?> child : ((CompositeNode) data).getChildren()) {
78                 DataSchemaNode childSchema = null;
79                 if (schema != null) {
80                     childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes());
81                     if (logger.isDebugEnabled()) {
82                         if (childSchema == null) {
83                             logger.debug("Probably the data node \""
84                                     + ((child == null) ? "" : child.getNodeType().getLocalName())
85                                     + "\" is not conform to schema");
86                         }
87                     }
88                 }
89                 itemEl.appendChild(createXmlRootElement(doc, child, childSchema, codecProvider));
90             }
91         }
92         return itemEl;
93     }
94
95     public static void writeValueByType(Element element, SimpleNode<?> node, TypeDefinition<?> type,
96             DataSchemaNode schema, XmlCodecProvider codecProvider) {
97
98         TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
99
100         if (baseType instanceof IdentityrefTypeDefinition) {
101             if (node.getValue() instanceof QName) {
102                 QName value = (QName) node.getValue();
103                 String prefix = "x";
104                 if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
105                     prefix = value.getPrefix();
106                 }
107                 element.setAttribute("xmlns:" + prefix, value.getNamespace().toString());
108                 element.setTextContent(prefix + ":" + value.getLocalName());
109             } else {
110                 logger.debug("Value of {}:{} is not instance of QName but is {}",
111                         baseType.getQName().getNamespace(), //
112                         baseType.getQName().getLocalName(), //
113                         node.getValue().getClass());
114                 element.setTextContent(String.valueOf(node.getValue()));
115             }
116         } else {
117             if (node.getValue() != null) {
118                 try {
119                     String value = codecProvider.codecFor(baseType).serialize(node.getValue());
120                     element.setTextContent(value);
121                 } catch (ClassCastException e) {
122                     element.setTextContent(String.valueOf(node.getValue()));
123                     logger.error("Provided node did not have type required by mapping. Using stream instead. {}",e);
124                 }
125             }
126         }
127     }
128
129     public final static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
130         TypeDefinition<?> superType = type;
131         while (superType.getBaseType() != null) {
132             superType = superType.getBaseType();
133         }
134         return superType;
135     }
136
137     private static final DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
138         if (dataSchemaNode != null && node != null) {
139             for (DataSchemaNode dsn : dataSchemaNode) {
140                 if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
141                     return dsn;
142                 } else if (dsn instanceof ChoiceNode) {
143                     for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
144                         DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
145                         if (foundDsn != null) {
146                             return foundDsn;
147                         }
148                     }
149                 }
150             }
151         }
152         return null;
153     }
154
155     public static final XmlCodecProvider defaultValueCodecProvider() {
156         return DEFAULT_XML_VALUE_CODEC_PROVIDER;
157     }
158
159 }