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