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