• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import org.xmlpull.v1.XmlSerializer;
20 
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.UnsupportedEncodingException;
25 import java.io.Writer;
26 import java.nio.ByteBuffer;
27 import java.nio.CharBuffer;
28 import java.nio.charset.Charset;
29 import java.nio.charset.CharsetEncoder;
30 import java.nio.charset.CoderResult;
31 import java.nio.charset.CodingErrorAction;
32 import java.nio.charset.IllegalCharsetNameException;
33 import java.nio.charset.UnsupportedCharsetException;
34 
35 /**
36  * This is a quick and dirty implementation of XmlSerializer that isn't horribly
37  * painfully slow like the normal one.  It only does what is needed for the
38  * specific XML files being written with it.
39  */
40 public class FastXmlSerializer implements XmlSerializer {
41     private static final String ESCAPE_TABLE[] = new String[] {
42         "�",   "",   "",   "",  "",    "",   "",  "",  // 0-7
43         "",   "	",   "
",  "", "",   "
",  "", "", // 8-15
44         "",  "",  "",  "", "",   "",  "", "", // 16-23
45         "",  "",  "",  "", "",   "",  "", "", // 24-31
46         null,     null,     """, null,     null,     null,     "&",  null,   // 32-39
47         null,     null,     null,     null,     null,     null,     null,     null,   // 40-47
48         null,     null,     null,     null,     null,     null,     null,     null,   // 48-55
49         null,     null,     null,     null,     "<",   null,     ">",   null,   // 56-63
50     };
51 
52     private static final int BUFFER_LEN = 8192;
53 
54     private static String sSpace = "                                                              ";
55 
56     private final char[] mText = new char[BUFFER_LEN];
57     private int mPos;
58 
59     private Writer mWriter;
60 
61     private OutputStream mOutputStream;
62     private CharsetEncoder mCharset;
63     private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);
64 
65     private boolean mIndent = false;
66     private boolean mInTag;
67 
68     private int mNesting = 0;
69     private boolean mLineStart = true;
70 
append(char c)71     private void append(char c) throws IOException {
72         int pos = mPos;
73         if (pos >= (BUFFER_LEN-1)) {
74             flush();
75             pos = mPos;
76         }
77         mText[pos] = c;
78         mPos = pos+1;
79     }
80 
append(String str, int i, final int length)81     private void append(String str, int i, final int length) throws IOException {
82         if (length > BUFFER_LEN) {
83             final int end = i + length;
84             while (i < end) {
85                 int next = i + BUFFER_LEN;
86                 append(str, i, next<end ? BUFFER_LEN : (end-i));
87                 i = next;
88             }
89             return;
90         }
91         int pos = mPos;
92         if ((pos+length) > BUFFER_LEN) {
93             flush();
94             pos = mPos;
95         }
96         str.getChars(i, i+length, mText, pos);
97         mPos = pos + length;
98     }
99 
append(char[] buf, int i, final int length)100     private void append(char[] buf, int i, final int length) throws IOException {
101         if (length > BUFFER_LEN) {
102             final int end = i + length;
103             while (i < end) {
104                 int next = i + BUFFER_LEN;
105                 append(buf, i, next<end ? BUFFER_LEN : (end-i));
106                 i = next;
107             }
108             return;
109         }
110         int pos = mPos;
111         if ((pos+length) > BUFFER_LEN) {
112             flush();
113             pos = mPos;
114         }
115         System.arraycopy(buf, i, mText, pos, length);
116         mPos = pos + length;
117     }
118 
append(String str)119     private void append(String str) throws IOException {
120         append(str, 0, str.length());
121     }
122 
appendIndent(int indent)123     private void appendIndent(int indent) throws IOException {
124         indent *= 4;
125         if (indent > sSpace.length()) {
126             indent = sSpace.length();
127         }
128         append(sSpace, 0, indent);
129     }
130 
escapeAndAppendString(final String string)131     private void escapeAndAppendString(final String string) throws IOException {
132         final int N = string.length();
133         final char NE = (char)ESCAPE_TABLE.length;
134         final String[] escapes = ESCAPE_TABLE;
135         int lastPos = 0;
136         int pos;
137         for (pos=0; pos<N; pos++) {
138             char c = string.charAt(pos);
139             if (c >= NE) continue;
140             String escape = escapes[c];
141             if (escape == null) continue;
142             if (lastPos < pos) append(string, lastPos, pos-lastPos);
143             lastPos = pos + 1;
144             append(escape);
145         }
146         if (lastPos < pos) append(string, lastPos, pos-lastPos);
147     }
148 
escapeAndAppendString(char[] buf, int start, int len)149     private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
150         final char NE = (char)ESCAPE_TABLE.length;
151         final String[] escapes = ESCAPE_TABLE;
152         int end = start+len;
153         int lastPos = start;
154         int pos;
155         for (pos=start; pos<end; pos++) {
156             char c = buf[pos];
157             if (c >= NE) continue;
158             String escape = escapes[c];
159             if (escape == null) continue;
160             if (lastPos < pos) append(buf, lastPos, pos-lastPos);
161             lastPos = pos + 1;
162             append(escape);
163         }
164         if (lastPos < pos) append(buf, lastPos, pos-lastPos);
165     }
166 
attribute(String namespace, String name, String value)167     public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
168             IllegalArgumentException, IllegalStateException {
169         append(' ');
170         if (namespace != null) {
171             append(namespace);
172             append(':');
173         }
174         append(name);
175         append("=\"");
176 
177         escapeAndAppendString(value);
178         append('"');
179         mLineStart = false;
180         return this;
181     }
182 
cdsect(String text)183     public void cdsect(String text) throws IOException, IllegalArgumentException,
184             IllegalStateException {
185         throw new UnsupportedOperationException();
186     }
187 
comment(String text)188     public void comment(String text) throws IOException, IllegalArgumentException,
189             IllegalStateException {
190         throw new UnsupportedOperationException();
191     }
192 
docdecl(String text)193     public void docdecl(String text) throws IOException, IllegalArgumentException,
194             IllegalStateException {
195         throw new UnsupportedOperationException();
196     }
197 
endDocument()198     public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
199         flush();
200     }
201 
endTag(String namespace, String name)202     public XmlSerializer endTag(String namespace, String name) throws IOException,
203             IllegalArgumentException, IllegalStateException {
204         mNesting--;
205         if (mInTag) {
206             append(" />\n");
207         } else {
208             if (mIndent && mLineStart) {
209                 appendIndent(mNesting);
210             }
211             append("</");
212             if (namespace != null) {
213                 append(namespace);
214                 append(':');
215             }
216             append(name);
217             append(">\n");
218         }
219         mLineStart = true;
220         mInTag = false;
221         return this;
222     }
223 
entityRef(String text)224     public void entityRef(String text) throws IOException, IllegalArgumentException,
225             IllegalStateException {
226         throw new UnsupportedOperationException();
227     }
228 
flushBytes()229     private void flushBytes() throws IOException {
230         int position;
231         if ((position = mBytes.position()) > 0) {
232             mBytes.flip();
233             mOutputStream.write(mBytes.array(), 0, position);
234             mBytes.clear();
235         }
236     }
237 
flush()238     public void flush() throws IOException {
239         //Log.i("PackageManager", "flush mPos=" + mPos);
240         if (mPos > 0) {
241             if (mOutputStream != null) {
242                 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
243                 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
244                 while (true) {
245                     if (result.isError()) {
246                         throw new IOException(result.toString());
247                     } else if (result.isOverflow()) {
248                         flushBytes();
249                         result = mCharset.encode(charBuffer, mBytes, true);
250                         continue;
251                     }
252                     break;
253                 }
254                 flushBytes();
255                 mOutputStream.flush();
256             } else {
257                 mWriter.write(mText, 0, mPos);
258                 mWriter.flush();
259             }
260             mPos = 0;
261         }
262     }
263 
getDepth()264     public int getDepth() {
265         throw new UnsupportedOperationException();
266     }
267 
getFeature(String name)268     public boolean getFeature(String name) {
269         throw new UnsupportedOperationException();
270     }
271 
getName()272     public String getName() {
273         throw new UnsupportedOperationException();
274     }
275 
getNamespace()276     public String getNamespace() {
277         throw new UnsupportedOperationException();
278     }
279 
getPrefix(String namespace, boolean generatePrefix)280     public String getPrefix(String namespace, boolean generatePrefix)
281             throws IllegalArgumentException {
282         throw new UnsupportedOperationException();
283     }
284 
getProperty(String name)285     public Object getProperty(String name) {
286         throw new UnsupportedOperationException();
287     }
288 
ignorableWhitespace(String text)289     public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
290             IllegalStateException {
291         throw new UnsupportedOperationException();
292     }
293 
processingInstruction(String text)294     public void processingInstruction(String text) throws IOException, IllegalArgumentException,
295             IllegalStateException {
296         throw new UnsupportedOperationException();
297     }
298 
setFeature(String name, boolean state)299     public void setFeature(String name, boolean state) throws IllegalArgumentException,
300             IllegalStateException {
301         if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
302             mIndent = true;
303             return;
304         }
305         throw new UnsupportedOperationException();
306     }
307 
setOutput(OutputStream os, String encoding)308     public void setOutput(OutputStream os, String encoding) throws IOException,
309             IllegalArgumentException, IllegalStateException {
310         if (os == null)
311             throw new IllegalArgumentException();
312         if (true) {
313             try {
314                 mCharset = Charset.forName(encoding).newEncoder()
315                         .onMalformedInput(CodingErrorAction.REPLACE)
316                         .onUnmappableCharacter(CodingErrorAction.REPLACE);
317             } catch (IllegalCharsetNameException e) {
318                 throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
319                         encoding).initCause(e));
320             } catch (UnsupportedCharsetException e) {
321                 throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
322                         encoding).initCause(e));
323             }
324             mOutputStream = os;
325         } else {
326             setOutput(
327                 encoding == null
328                     ? new OutputStreamWriter(os)
329                     : new OutputStreamWriter(os, encoding));
330         }
331     }
332 
setOutput(Writer writer)333     public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
334             IllegalStateException {
335         mWriter = writer;
336     }
337 
setPrefix(String prefix, String namespace)338     public void setPrefix(String prefix, String namespace) throws IOException,
339             IllegalArgumentException, IllegalStateException {
340         throw new UnsupportedOperationException();
341     }
342 
setProperty(String name, Object value)343     public void setProperty(String name, Object value) throws IllegalArgumentException,
344             IllegalStateException {
345         throw new UnsupportedOperationException();
346     }
347 
startDocument(String encoding, Boolean standalone)348     public void startDocument(String encoding, Boolean standalone) throws IOException,
349             IllegalArgumentException, IllegalStateException {
350         append("<?xml version='1.0' encoding='utf-8' standalone='"
351                 + (standalone ? "yes" : "no") + "' ?>\n");
352         mLineStart = true;
353     }
354 
startTag(String namespace, String name)355     public XmlSerializer startTag(String namespace, String name) throws IOException,
356             IllegalArgumentException, IllegalStateException {
357         if (mInTag) {
358             append(">\n");
359         }
360         if (mIndent) {
361             appendIndent(mNesting);
362         }
363         mNesting++;
364         append('<');
365         if (namespace != null) {
366             append(namespace);
367             append(':');
368         }
369         append(name);
370         mInTag = true;
371         mLineStart = false;
372         return this;
373     }
374 
text(char[] buf, int start, int len)375     public XmlSerializer text(char[] buf, int start, int len) throws IOException,
376             IllegalArgumentException, IllegalStateException {
377         if (mInTag) {
378             append(">");
379             mInTag = false;
380         }
381         escapeAndAppendString(buf, start, len);
382         if (mIndent) {
383             mLineStart = buf[start+len-1] == '\n';
384         }
385         return this;
386     }
387 
text(String text)388     public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
389             IllegalStateException {
390         if (mInTag) {
391             append(">");
392             mInTag = false;
393         }
394         escapeAndAppendString(text);
395         if (mIndent) {
396             mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
397         }
398         return this;
399     }
400 
401 }
402