Fix license issues in dmaap dr
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / json / XML.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package org.json;\r
24 \r
25 \r
26 \r
27 import java.util.Iterator;\r
28 \r
29 \r
30 public class XML {\r
31 \r
32     /** The Character '&'. */\r
33     public static final Character AMP   = new Character('&');\r
34 \r
35     /** The Character '''. */\r
36     public static final Character APOS  = new Character('\'');\r
37 \r
38     /** The Character '!'. */\r
39     public static final Character BANG  = new Character('!');\r
40 \r
41     /** The Character '='. */\r
42     public static final Character EQ    = new Character('=');\r
43 \r
44     /** The Character '>'. */\r
45     public static final Character GT    = new Character('>');\r
46 \r
47     /** The Character '<'. */\r
48     public static final Character LT    = new Character('<');\r
49 \r
50     /** The Character '?'. */\r
51     public static final Character QUEST = new Character('?');\r
52 \r
53     /** The Character '"'. */\r
54     public static final Character QUOT  = new Character('"');\r
55 \r
56     /** The Character '/'. */\r
57     public static final Character SLASH = new Character('/');\r
58 \r
59     /**\r
60      * Replace special characters with XML escapes:\r
61      * <pre>\r
62      * &amp; <small>(ampersand)</small> is replaced by &amp;amp;\r
63      * &lt; <small>(less than)</small> is replaced by &amp;lt;\r
64      * &gt; <small>(greater than)</small> is replaced by &amp;gt;\r
65      * &quot; <small>(double quote)</small> is replaced by &amp;quot;\r
66      * </pre>\r
67      * @param string The string to be escaped.\r
68      * @return The escaped string.\r
69      */\r
70     public static String escape(String string) {\r
71         StringBuffer sb = new StringBuffer();\r
72         for (int i = 0, length = string.length(); i < length; i++) {\r
73             char c = string.charAt(i);\r
74             switch (c) {\r
75             case '&':\r
76                 sb.append("&amp;");\r
77                 break;\r
78             case '<':\r
79                 sb.append("&lt;");\r
80                 break;\r
81             case '>':\r
82                 sb.append("&gt;");\r
83                 break;\r
84             case '"':\r
85                 sb.append("&quot;");\r
86                 break;\r
87             case '\'':\r
88                 sb.append("&apos;");\r
89                 break;\r
90             default:\r
91                 sb.append(c);\r
92             }\r
93         }\r
94         return sb.toString();\r
95     }\r
96 \r
97     /**\r
98      * Throw an exception if the string contains whitespace.\r
99      * Whitespace is not allowed in tagNames and attributes.\r
100      * @param string\r
101      * @throws JSONException\r
102      */\r
103     public static void noSpace(String string) throws JSONException {\r
104         int i, length = string.length();\r
105         if (length == 0) {\r
106             throw new JSONException("Empty string.");\r
107         }\r
108         for (i = 0; i < length; i += 1) {\r
109             if (Character.isWhitespace(string.charAt(i))) {\r
110                 throw new JSONException("'" + string +\r
111                         "' contains a space character.");\r
112             }\r
113         }\r
114     }\r
115 \r
116     /**\r
117      * Scan the content following the named tag, attaching it to the context.\r
118      * @param x       The XMLTokener containing the source string.\r
119      * @param context The JSONObject that will include the new material.\r
120      * @param name    The tag name.\r
121      * @return true if the close tag is processed.\r
122      * @throws JSONException\r
123      */\r
124     private static boolean parse(XMLTokener x, JSONObject context,\r
125                                  String name) throws JSONException {\r
126         char       c;\r
127         int        i;\r
128         JSONObject jsonobject = null;\r
129         String     string;\r
130         String     tagName;\r
131         Object     token;\r
132 \r
133 // Test for and skip past these forms:\r
134 //      <!-- ... -->\r
135 //      <!   ...   >\r
136 //      <![  ... ]]>\r
137 //      <?   ...  ?>\r
138 // Report errors for these forms:\r
139 //      <>\r
140 //      <=\r
141 //      <<\r
142 \r
143         token = x.nextToken();\r
144 \r
145 // <!\r
146 \r
147         if (token == BANG) {\r
148             c = x.next();\r
149             if (c == '-') {\r
150                 if (x.next() == '-') {\r
151                     x.skipPast("-->");\r
152                     return false;\r
153                 }\r
154                 x.back();\r
155             } else if (c == '[') {\r
156                 token = x.nextToken();\r
157                 if ("CDATA".equals(token)) {\r
158                     if (x.next() == '[') {\r
159                         string = x.nextCDATA();\r
160                         if (string.length() > 0) {\r
161                             context.accumulate("content", string);\r
162                         }\r
163                         return false;\r
164                     }\r
165                 }\r
166                 throw x.syntaxError("Expected 'CDATA['");\r
167             }\r
168             i = 1;\r
169             do {\r
170                 token = x.nextMeta();\r
171                 if (token == null) {\r
172                     throw x.syntaxError("Missing '>' after '<!'.");\r
173                 } else if (token == LT) {\r
174                     i += 1;\r
175                 } else if (token == GT) {\r
176                     i -= 1;\r
177                 }\r
178             } while (i > 0);\r
179             return false;\r
180         } else if (token == QUEST) {\r
181 \r
182 // <?\r
183 \r
184             x.skipPast("?>");\r
185             return false;\r
186         } else if (token == SLASH) {\r
187 \r
188 // Close tag </\r
189 \r
190             token = x.nextToken();\r
191             if (name == null) {\r
192                 throw x.syntaxError("Mismatched close tag " + token);\r
193             }\r
194             if (!token.equals(name)) {\r
195                 throw x.syntaxError("Mismatched " + name + " and " + token);\r
196             }\r
197             if (x.nextToken() != GT) {\r
198                 throw x.syntaxError("Misshaped close tag");\r
199             }\r
200             return true;\r
201 \r
202         } else if (token instanceof Character) {\r
203             throw x.syntaxError("Misshaped tag");\r
204 \r
205 // Open tag <\r
206 \r
207         } else {\r
208             tagName = (String)token;\r
209             token = null;\r
210             jsonobject = new JSONObject();\r
211             for (;;) {\r
212                 if (token == null) {\r
213                     token = x.nextToken();\r
214                 }\r
215 \r
216 // attribute = value\r
217 \r
218                 if (token instanceof String) {\r
219                     string = (String)token;\r
220                     token = x.nextToken();\r
221                     if (token == EQ) {\r
222                         token = x.nextToken();\r
223                         if (!(token instanceof String)) {\r
224                             throw x.syntaxError("Missing value");\r
225                         }\r
226                         jsonobject.accumulate(string,\r
227                                 XML.stringToValue((String)token));\r
228                         token = null;\r
229                     } else {\r
230                         jsonobject.accumulate(string, "");\r
231                     }\r
232 \r
233 // Empty tag <.../>\r
234 \r
235                 } else if (token == SLASH) {\r
236                     if (x.nextToken() != GT) {\r
237                         throw x.syntaxError("Misshaped tag");\r
238                     }\r
239                     if (jsonobject.length() > 0) {\r
240                         context.accumulate(tagName, jsonobject);\r
241                     } else {\r
242                         context.accumulate(tagName, "");\r
243                     }\r
244                     return false;\r
245 \r
246 // Content, between <...> and </...>\r
247 \r
248                 } else if (token == GT) {\r
249                     for (;;) {\r
250                         token = x.nextContent();\r
251                         if (token == null) {\r
252                             if (tagName != null) {\r
253                                 throw x.syntaxError("Unclosed tag " + tagName);\r
254                             }\r
255                             return false;\r
256                         } else if (token instanceof String) {\r
257                             string = (String)token;\r
258                             if (string.length() > 0) {\r
259                                 jsonobject.accumulate("content",\r
260                                         XML.stringToValue(string));\r
261                             }\r
262 \r
263 // Nested element\r
264 \r
265                         } else if (token == LT) {\r
266                             if (parse(x, jsonobject, tagName)) {\r
267                                 if (jsonobject.length() == 0) {\r
268                                     context.accumulate(tagName, "");\r
269                                 } else if (jsonobject.length() == 1 &&\r
270                                        jsonobject.opt("content") != null) {\r
271                                     context.accumulate(tagName,\r
272                                             jsonobject.opt("content"));\r
273                                 } else {\r
274                                     context.accumulate(tagName, jsonobject);\r
275                                 }\r
276                                 return false;\r
277                             }\r
278                         }\r
279                     }\r
280                 } else {\r
281                     throw x.syntaxError("Misshaped tag");\r
282                 }\r
283             }\r
284         }\r
285     }\r
286 \r
287 \r
288     /**\r
289      * Try to convert a string into a number, boolean, or null. If the string\r
290      * can't be converted, return the string. This is much less ambitious than\r
291      * JSONObject.stringToValue, especially because it does not attempt to\r
292      * convert plus forms, octal forms, hex forms, or E forms lacking decimal\r
293      * points.\r
294      * @param string A String.\r
295      * @return A simple JSON value.\r
296      */\r
297     public static Object stringToValue(String string) {\r
298         if ("".equals(string)) {\r
299             return string;\r
300         }\r
301         if ("true".equalsIgnoreCase(string)) {\r
302             return Boolean.TRUE;\r
303         }\r
304         if ("false".equalsIgnoreCase(string)) {\r
305             return Boolean.FALSE;\r
306         }\r
307         if ("null".equalsIgnoreCase(string)) {\r
308             return JSONObject.NULL;\r
309         }\r
310         if ("0".equals(string)) {\r
311             return new Integer(0);\r
312         }\r
313 \r
314 // If it might be a number, try converting it. If that doesn't work,\r
315 // return the string.\r
316 \r
317         try {\r
318             char initial = string.charAt(0);\r
319             boolean negative = false;\r
320             if (initial == '-') {\r
321                 initial = string.charAt(1);\r
322                 negative = true;\r
323             }\r
324             if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {\r
325                 return string;\r
326             }\r
327             if ((initial >= '0' && initial <= '9')) {\r
328                 if (string.indexOf('.') >= 0) {\r
329                     return Double.valueOf(string);\r
330                 } else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {\r
331                     Long myLong = new Long(string);\r
332                     if (myLong.longValue() == myLong.intValue()) {\r
333                         return new Integer(myLong.intValue());\r
334                     } else {\r
335                         return myLong;\r
336                     }\r
337                 }\r
338             }\r
339         }  catch (Exception ignore) {\r
340         }\r
341         return string;\r
342     }\r
343 \r
344 \r
345     /**\r
346      * Convert a well-formed (but not necessarily valid) XML string into a\r
347      * JSONObject. Some information may be lost in this transformation\r
348      * because JSON is a data format and XML is a document format. XML uses\r
349      * elements, attributes, and content text, while JSON uses unordered\r
350      * collections of name/value pairs and arrays of values. JSON does not\r
351      * does not like to distinguish between elements and attributes.\r
352      * Sequences of similar elements are represented as JSONArrays. Content\r
353      * text may be placed in a "content" member. Comments, prologs, DTDs, and\r
354      * <code>&lt;[ [ ]]></code> are ignored.\r
355      * @param string The source string.\r
356      * @return A JSONObject containing the structured data from the XML string.\r
357      * @throws JSONException\r
358      */\r
359     public static JSONObject toJSONObject(String string) throws JSONException {\r
360         JSONObject jo = new JSONObject();\r
361         XMLTokener x = new XMLTokener(string);\r
362         while (x.more() && x.skipPast("<")) {\r
363             parse(x, jo, null);\r
364         }\r
365         return jo;\r
366     }\r
367 \r
368 \r
369     /**\r
370      * Convert a JSONObject into a well-formed, element-normal XML string.\r
371      * @param object A JSONObject.\r
372      * @return  A string.\r
373      * @throws  JSONException\r
374      */\r
375     public static String toString(Object object) throws JSONException {\r
376         return toString(object, null);\r
377     }\r
378 \r
379 \r
380     /**\r
381      * Convert a JSONObject into a well-formed, element-normal XML string.\r
382      * @param object A JSONObject.\r
383      * @param tagName The optional name of the enclosing tag.\r
384      * @return A string.\r
385      * @throws JSONException\r
386      */\r
387     public static String toString(Object object, String tagName)\r
388             throws JSONException {\r
389         StringBuffer sb = new StringBuffer();\r
390         int          i;\r
391         JSONArray    ja;\r
392         JSONObject   jo;\r
393         String       key;\r
394         Iterator<String> keys;\r
395         int          length;\r
396         String       string;\r
397         Object       value;\r
398         if (object instanceof JSONObject) {\r
399 \r
400 // Emit <tagName>\r
401 \r
402             if (tagName != null) {\r
403                 sb.append('<');\r
404                 sb.append(tagName);\r
405                 sb.append('>');\r
406             }\r
407 \r
408 // Loop thru the keys.\r
409 \r
410             jo = (JSONObject)object;\r
411             keys = jo.keys();\r
412             while (keys.hasNext()) {\r
413                 key = keys.next().toString();\r
414                 value = jo.opt(key);\r
415                 if (value == null) {\r
416                     value = "";\r
417                 }\r
418                 if (value instanceof String) {\r
419                     string = (String)value;\r
420                 } else {\r
421                     string = null;\r
422                 }\r
423 \r
424 // Emit content in body\r
425 \r
426                 if ("content".equals(key)) {\r
427                     if (value instanceof JSONArray) {\r
428                         ja = (JSONArray)value;\r
429                         length = ja.length();\r
430                         for (i = 0; i < length; i += 1) {\r
431                             if (i > 0) {\r
432                                 sb.append('\n');\r
433                             }\r
434                             sb.append(escape(ja.get(i).toString()));\r
435                         }\r
436                     } else {\r
437                         sb.append(escape(value.toString()));\r
438                     }\r
439 \r
440 // Emit an array of similar keys\r
441 \r
442                 } else if (value instanceof JSONArray) {\r
443                     ja = (JSONArray)value;\r
444                     length = ja.length();\r
445                     for (i = 0; i < length; i += 1) {\r
446                         value = ja.get(i);\r
447                         if (value instanceof JSONArray) {\r
448                             sb.append('<');\r
449                             sb.append(key);\r
450                             sb.append('>');\r
451                             sb.append(toString(value));\r
452                             sb.append("</");\r
453                             sb.append(key);\r
454                             sb.append('>');\r
455                         } else {\r
456                             sb.append(toString(value, key));\r
457                         }\r
458                     }\r
459                 } else if ("".equals(value)) {\r
460                     sb.append('<');\r
461                     sb.append(key);\r
462                     sb.append("/>");\r
463 \r
464 // Emit a new tag <k>\r
465 \r
466                 } else {\r
467                     sb.append(toString(value, key));\r
468                 }\r
469             }\r
470             if (tagName != null) {\r
471 \r
472 // Emit the </tagname> close tag\r
473 \r
474                 sb.append("</");\r
475                 sb.append(tagName);\r
476                 sb.append('>');\r
477             }\r
478             return sb.toString();\r
479 \r
480 // XML does not have good support for arrays. If an array appears in a place\r
481 // where XML is lacking, synthesize an <array> element.\r
482 \r
483         } else {\r
484             if (object.getClass().isArray()) {\r
485                 object = new JSONArray(object);\r
486             }\r
487             if (object instanceof JSONArray) {\r
488                 ja = (JSONArray)object;\r
489                 length = ja.length();\r
490                 for (i = 0; i < length; i += 1) {\r
491                     sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName));\r
492                 }\r
493                 return sb.toString();\r
494             } else {\r
495                 string = (object == null) ? "null" : escape(object.toString());\r
496                 return (tagName == null) ? "\"" + string + "\"" :\r
497                     (string.length() == 0) ? "<" + tagName + "/>" :\r
498                     "<" + tagName + ">" + string + "</" + tagName + ">";\r
499             }\r
500         }\r
501     }\r
502 }\r