summaryrefslogtreecommitdiffstats
path: root/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java
blob: 0134a5a47892181765dcfb5aae4397514556b707 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*-
 * ============LICENSE_START=======================================================
 * ONAP - CCSDK
 * ================================================================================
 * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved.
 * ================================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============LICENSE_END=========================================================
 */

package org.onap.ccsdk.sli.plugins.yangserializers.dfserializer;

import static javax.xml.transform.OutputKeys.INDENT;
import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.XmlNodeType.OBJECT_NODE;
import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.XmlNodeType.TEXT_NODE;
import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getRevision;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.dom4j.Element;
import org.onap.ccsdk.sli.core.sli.SvcLogicException;
import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace;
import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Utilities for data format serializer.
 */
public final class DfSerializerUtil {

    static final String JSON_WRITE_ERR = "Unable to write to JSON from " +
            "properties.";

    static final String NODE_TYPE_ERR = "The node type %s is not supported.";

    static final String JSON_LIS_ERR = "The JSON serializer doesn't have " +
            "JSON listener";

    static final String XML_LIS_ERR = "The XML serializer doesn't have XML " +
            "listener";

    static final String PROP_NODE_ERR = "The property node doesn't have " +
            "schema node bound to it.";

    static final String DF_ERR = "Type mismatch for the node %s. The schema " +
            "node does not match with the data format node type %s.";

    static final String XML_PREFIX = "yangid";

    private static final String YES = "yes";

    private static final String INDENT_XMLNS = "{http://xml.apache" +
            ".org/xslt}indent-amount";

    private static final String XML_PARSE_ERR = "Unable to parse the xml to " +
            "document : \n";

    private static final String URI_ERR = "Unable to parse the URI";

    /**
     * Data format error message for unsupported types.
     */
    public static final String FORMAT_ERR = "Only JSON and XML formats are " +
            "supported. %s is not supported";

    /**
     * UTF header message for XML data format message.
     */
    public static final String UTF_HEADER = "<?xml version=\"1.0\" " +
            "encoding=\"UTF-8\"?>";

    /**
     * Error message when a JSON tree creation fails.
     */
    public static final String JSON_TREE_ERR = "Unable to form JSON tree " +
            "object from the JSON body provided.";

    /**
     * Error message when a XML tree creation fails.
     */
    public static final String XML_TREE_ERR = "Unable to form XML tree object" +
            " from the XML body provided.";

    //No instantiation.
    private DfSerializerUtil() {
    }

    /**
     * Returns the writer which contains the pretty formatted XML string.
     *
     * @param input  input XML
     * @param indent indentation level
     * @return writer with XML
     * @throws SvcLogicException when transformation of source fails
     */
    public static Writer getXmlWriter(String input, String indent)
            throws SvcLogicException {
        try {
            TransformerFactory factory = javax.xml.transform.TransformerFactory.newInstance();
            // Remediate XML external entity vulnerabilty - prohibit the use of all protocols by external entities:
            factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
            factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
            Transformer transformer = factory.newTransformer();
            transformer.setOutputProperty(INDENT, YES);
            transformer.setOutputProperty(INDENT_XMLNS, indent);
            StreamResult result = new StreamResult(new StringWriter());
            DOMSource source = new DOMSource(parseXml(input));
            transformer.transform(source, result);
            return result.getWriter();
        } catch (TransformerException e) {
            throw new SvcLogicException(XML_PARSE_ERR + input, e);
        }
    }

    /**
     * Parses the XML and converts it into dom document which can be used for
     * formatting the XML.
     *
     * @param in input XML
     * @return dom document of XML
     * @throws SvcLogicException when document building fails
     */
    private static Document parseXml(String in) throws SvcLogicException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        try {
            // To remediate XML external entity vulnerability, completely disable external entities declarations:
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
             dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputSource is = new InputSource(new StringReader(in));
            return db.parse(is);
        } catch (SAXException | IOException | ParserConfigurationException e) {
            throw new SvcLogicException(XML_PARSE_ERR + in, e);
        }
    }

    /**
     * Returns the resolved namespace object from the input received from the
     * abstract data format.
     *
     * @param mName  module name
     * @param mUri   module URI
     * @param ctx    schema context
     * @param parent parent properties node
     * @return namespace
     * @throws SvcLogicException when resolving namespace fails
     */
    static Namespace getResolvedNamespace(String mName, String mUri,
                                          SchemaContext ctx,
                                          PropertiesNode parent)
            throws SvcLogicException {
        if (mName == null && mUri == null) {
            Namespace parentNs = parent.namespace();
            return new Namespace(parentNs.moduleName(), parentNs.moduleNs(),
                                 parentNs.revision());
        }

        Iterator<? extends Module> it;
        Module mod;
        if (mName != null) {
            it = ctx.findModules(mName).iterator();
        } else {
            URI modUri = null;
            try {
               modUri = new URI(mUri);
            } catch (URISyntaxException e) {
                throw new SvcLogicException(URI_ERR, e);
            }
            it = ctx.findModules(modUri).iterator();
        }

        if (!it.hasNext()) {
            return null;
        }
        mod = it.next();

        return new Namespace(mod.getName(), mod.getQNameModule().getNamespace(),
                             getRevision(mod.getRevision()));
    }

    /**
     * Returns the node type of a XML element.
     *
     * @param element XML element
     * @return node type of the XML element
     */
    static XmlNodeType getXmlNodeType(Element element) {
        Element newElement = element.createCopy();
        newElement.remove(element.getNamespace());
        return newElement.hasContent() && newElement.isTextOnly() ?
                TEXT_NODE : OBJECT_NODE;
    }

    /**
     * Resolves the super type to the base type from type definition.
     *
     * @param type super type
     * @return base type definition
     */
    static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
        TypeDefinition superType = type;
        while (superType.getBaseType() != null) {
            superType = superType.getBaseType();
        }
        return superType;
    }
}