• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.hotspot2.omadm;
2 
3 import org.xml.sax.Attributes;
4 import org.xml.sax.SAXException;
5 
6 import java.io.IOException;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collections;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 
16 public class XMLNode {
17     private final String mTag;
18     private final Map<String, NodeAttribute> mAttributes;
19     private final List<XMLNode> mChildren;
20     private final XMLNode mParent;
21     private MOTree mMO;
22     private StringBuilder mTextBuilder;
23     private String mText;
24 
25     private static final String XML_SPECIAL_CHARS = "\"'<>&";
26     private static final Set<Character> XML_SPECIAL = new HashSet<>();
27     private static final String CDATA_OPEN = "<![CDATA[";
28     private static final String CDATA_CLOSE = "]]>";
29 
30     static {
31         for (int n = 0; n < XML_SPECIAL_CHARS.length(); n++) {
XML_SPECIAL_CHARS.charAt(n)32             XML_SPECIAL.add(XML_SPECIAL_CHARS.charAt(n));
33         }
34     }
35 
XMLNode(XMLNode parent, String tag, Attributes attributes)36     public XMLNode(XMLNode parent, String tag, Attributes attributes) throws SAXException {
37         mTag = tag;
38 
39         mAttributes = new HashMap<>();
40 
41         if (attributes.getLength() > 0) {
42             for (int n = 0; n < attributes.getLength(); n++)
43                 mAttributes.put(attributes.getQName(n), new NodeAttribute(attributes.getQName(n),
44                         attributes.getType(n), attributes.getValue(n)));
45         }
46 
47         mParent = parent;
48         mChildren = new ArrayList<>();
49 
50         mTextBuilder = new StringBuilder();
51     }
52 
XMLNode(XMLNode parent, String tag, Map<String, String> attributes)53     public XMLNode(XMLNode parent, String tag, Map<String, String> attributes) {
54         mTag = tag;
55 
56         mAttributes = new HashMap<>(attributes == null ? 0 : attributes.size());
57 
58         if (attributes != null) {
59             for (Map.Entry<String, String> entry : attributes.entrySet()) {
60                 mAttributes.put(entry.getKey(),
61                         new NodeAttribute(entry.getKey(), "", entry.getValue()));
62             }
63         }
64 
65         mParent = parent;
66         mChildren = new ArrayList<>();
67 
68         mTextBuilder = new StringBuilder();
69     }
70 
setText(String text)71     public void setText(String text) {
72         mText = text;
73         mTextBuilder = null;
74     }
75 
addText(char[] chs, int start, int length)76     public void addText(char[] chs, int start, int length) {
77         String s = new String(chs, start, length);
78         String trimmed = s.trim();
79         if (trimmed.isEmpty())
80             return;
81 
82         if (s.charAt(0) != trimmed.charAt(0))
83             mTextBuilder.append(' ');
84         mTextBuilder.append(trimmed);
85         if (s.charAt(s.length() - 1) != trimmed.charAt(trimmed.length() - 1))
86             mTextBuilder.append(' ');
87     }
88 
addChild(XMLNode child)89     public void addChild(XMLNode child) {
90         mChildren.add(child);
91     }
92 
close()93     public void close() throws IOException, SAXException {
94         String text = mTextBuilder.toString().trim();
95         StringBuilder filtered = new StringBuilder(text.length());
96         for (int n = 0; n < text.length(); n++) {
97             char ch = text.charAt(n);
98             if (ch >= ' ')
99                 filtered.append(ch);
100         }
101 
102         mText = filtered.toString();
103         mTextBuilder = null;
104 
105         if (MOTree.hasMgmtTreeTag(mText)) {
106             try {
107                 NodeAttribute urn = mAttributes.get(OMAConstants.SppMOAttribute);
108                 OMAParser omaParser = new OMAParser();
109                 mMO = omaParser.parse(mText, urn != null ? urn.getValue() : null);
110             } catch (SAXException | IOException e) {
111                 mMO = null;
112             }
113         }
114     }
115 
getTag()116     public String getTag() {
117         return mTag;
118     }
119 
getNameSpace()120     public String getNameSpace() throws OMAException {
121         String[] nsn = mTag.split(":");
122         if (nsn.length != 2) {
123             throw new OMAException("Non-namespaced tag: '" + mTag + "'");
124         }
125         return nsn[0];
126     }
127 
getStrippedTag()128     public String getStrippedTag() throws OMAException {
129         String[] nsn = mTag.split(":");
130         if (nsn.length != 2) {
131             throw new OMAException("Non-namespaced tag: '" + mTag + "'");
132         }
133         return nsn[1].toLowerCase();
134     }
135 
getSoleChild()136     public XMLNode getSoleChild() throws OMAException {
137         if (mChildren.size() != 1) {
138             throw new OMAException("Expected exactly one child to " + mTag);
139         }
140         return mChildren.get(0);
141     }
142 
getParent()143     public XMLNode getParent() {
144         return mParent;
145     }
146 
getText()147     public String getText() {
148         return mText;
149     }
150 
getAttributes()151     public Map<String, NodeAttribute> getAttributes() {
152         return Collections.unmodifiableMap(mAttributes);
153     }
154 
getTextualAttributes()155     public Map<String, String> getTextualAttributes() {
156         Map<String, String> map = new HashMap<>(mAttributes.size());
157         for (Map.Entry<String, NodeAttribute> entry : mAttributes.entrySet()) {
158             map.put(entry.getKey(), entry.getValue().getValue());
159         }
160         return map;
161     }
162 
getAttributeValue(String name)163     public String getAttributeValue(String name) {
164         NodeAttribute nodeAttribute = mAttributes.get(name);
165         return nodeAttribute != null ? nodeAttribute.getValue() : null;
166     }
167 
getChildren()168     public List<XMLNode> getChildren() {
169         return mChildren;
170     }
171 
getMOTree()172     public MOTree getMOTree() {
173         return mMO;
174     }
175 
toString(char[] indent, StringBuilder sb)176     private void toString(char[] indent, StringBuilder sb) {
177         Arrays.fill(indent, ' ');
178 
179         sb.append(indent).append('<').append(mTag);
180         for (Map.Entry<String, NodeAttribute> entry : mAttributes.entrySet()) {
181             sb.append(' ').append(entry.getKey()).append("='")
182                     .append(entry.getValue().getValue()).append('\'');
183         }
184 
185         if (mText != null && !mText.isEmpty()) {
186             sb.append('>').append(escapeCdata(mText)).append("</").append(mTag).append(">\n");
187         } else if (mChildren.isEmpty()) {
188             sb.append("/>\n");
189         } else {
190             sb.append(">\n");
191             char[] subIndent = Arrays.copyOf(indent, indent.length + 2);
192             for (XMLNode child : mChildren) {
193                 child.toString(subIndent, sb);
194             }
195             sb.append(indent).append("</").append(mTag).append(">\n");
196         }
197     }
198 
escapeCdata(String text)199     private static String escapeCdata(String text) {
200         if (!escapable(text)) {
201             return text;
202         }
203 
204         // Any appearance of ]]> in the text must be split into "]]" | "]]>" | <![CDATA[ | ">"
205         // i.e. "split the sequence by putting a close CDATA and a new open CDATA before the '>'
206         StringBuilder sb = new StringBuilder();
207         sb.append(CDATA_OPEN);
208         int start = 0;
209         for (; ; ) {
210             int etoken = text.indexOf(CDATA_CLOSE);
211             if (etoken >= 0) {
212                 sb.append(text.substring(start, etoken + 2)).append(CDATA_CLOSE).append(CDATA_OPEN);
213                 start = etoken + 2;
214             } else {
215                 if (start < text.length() - 1) {
216                     sb.append(text.substring(start));
217                 }
218                 break;
219             }
220         }
221         sb.append(CDATA_CLOSE);
222         return sb.toString();
223     }
224 
escapable(String s)225     private static boolean escapable(String s) {
226         for (int n = 0; n < s.length(); n++) {
227             if (XML_SPECIAL.contains(s.charAt(n))) {
228                 return true;
229             }
230         }
231         return false;
232     }
233 
234     @Override
toString()235     public String toString() {
236         StringBuilder sb = new StringBuilder();
237         toString(new char[0], sb);
238         return sb.toString();
239     }
240 }
241