• 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 DEFAULT_BUFFER_LEN = 32*1024;
53 
54     private static String sSpace = "                                                              ";
55 
56     private final int mBufferLen;
57     private final char[] mText;
58     private int mPos;
59 
60     private Writer mWriter;
61 
62     private OutputStream mOutputStream;
63     private CharsetEncoder mCharset;
64     private ByteBuffer mBytes;
65 
66     private boolean mIndent = false;
67     private boolean mInTag;
68 
69     private int mNesting = 0;
70     private boolean mLineStart = true;
71 
FastXmlSerializer()72     public FastXmlSerializer() {
73         this(DEFAULT_BUFFER_LEN);
74     }
75 
76     /**
77      * Allocate a FastXmlSerializer with the given internal output buffer size.  If the
78      * size is zero or negative, then the default buffer size will be used.
79      *
80      * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
81      */
FastXmlSerializer(int bufferSize)82     public FastXmlSerializer(int bufferSize) {
83         mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
84         mText = new char[mBufferLen];
85         mBytes = ByteBuffer.allocate(mBufferLen);
86     }
87 
append(char c)88     private void append(char c) throws IOException {
89         int pos = mPos;
90         if (pos >= (mBufferLen-1)) {
91             flush();
92             pos = mPos;
93         }
94         mText[pos] = c;
95         mPos = pos+1;
96     }
97 
append(String str, int i, final int length)98     private void append(String str, int i, final int length) throws IOException {
99         if (length > mBufferLen) {
100             final int end = i + length;
101             while (i < end) {
102                 int next = i + mBufferLen;
103                 append(str, i, next<end ? mBufferLen : (end-i));
104                 i = next;
105             }
106             return;
107         }
108         int pos = mPos;
109         if ((pos+length) > mBufferLen) {
110             flush();
111             pos = mPos;
112         }
113         str.getChars(i, i+length, mText, pos);
114         mPos = pos + length;
115     }
116 
append(char[] buf, int i, final int length)117     private void append(char[] buf, int i, final int length) throws IOException {
118         if (length > mBufferLen) {
119             final int end = i + length;
120             while (i < end) {
121                 int next = i + mBufferLen;
122                 append(buf, i, next<end ? mBufferLen : (end-i));
123                 i = next;
124             }
125             return;
126         }
127         int pos = mPos;
128         if ((pos+length) > mBufferLen) {
129             flush();
130             pos = mPos;
131         }
132         System.arraycopy(buf, i, mText, pos, length);
133         mPos = pos + length;
134     }
135 
append(String str)136     private void append(String str) throws IOException {
137         append(str, 0, str.length());
138     }
139 
appendIndent(int indent)140     private void appendIndent(int indent) throws IOException {
141         indent *= 4;
142         if (indent > sSpace.length()) {
143             indent = sSpace.length();
144         }
145         append(sSpace, 0, indent);
146     }
147 
escapeAndAppendString(final String string)148     private void escapeAndAppendString(final String string) throws IOException {
149         final int N = string.length();
150         final char NE = (char)ESCAPE_TABLE.length;
151         final String[] escapes = ESCAPE_TABLE;
152         int lastPos = 0;
153         int pos;
154         for (pos=0; pos<N; pos++) {
155             char c = string.charAt(pos);
156             if (c >= NE) continue;
157             String escape = escapes[c];
158             if (escape == null) continue;
159             if (lastPos < pos) append(string, lastPos, pos-lastPos);
160             lastPos = pos + 1;
161             append(escape);
162         }
163         if (lastPos < pos) append(string, lastPos, pos-lastPos);
164     }
165 
escapeAndAppendString(char[] buf, int start, int len)166     private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
167         final char NE = (char)ESCAPE_TABLE.length;
168         final String[] escapes = ESCAPE_TABLE;
169         int end = start+len;
170         int lastPos = start;
171         int pos;
172         for (pos=start; pos<end; pos++) {
173             char c = buf[pos];
174             if (c >= NE) continue;
175             String escape = escapes[c];
176             if (escape == null) continue;
177             if (lastPos < pos) append(buf, lastPos, pos-lastPos);
178             lastPos = pos + 1;
179             append(escape);
180         }
181         if (lastPos < pos) append(buf, lastPos, pos-lastPos);
182     }
183 
attribute(String namespace, String name, String value)184     public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
185             IllegalArgumentException, IllegalStateException {
186         append(' ');
187         if (namespace != null) {
188             append(namespace);
189             append(':');
190         }
191         append(name);
192         append("=\"");
193 
194         escapeAndAppendString(value);
195         append('"');
196         mLineStart = false;
197         return this;
198     }
199 
cdsect(String text)200     public void cdsect(String text) throws IOException, IllegalArgumentException,
201             IllegalStateException {
202         throw new UnsupportedOperationException();
203     }
204 
comment(String text)205     public void comment(String text) throws IOException, IllegalArgumentException,
206             IllegalStateException {
207         throw new UnsupportedOperationException();
208     }
209 
docdecl(String text)210     public void docdecl(String text) throws IOException, IllegalArgumentException,
211             IllegalStateException {
212         throw new UnsupportedOperationException();
213     }
214 
endDocument()215     public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
216         flush();
217     }
218 
endTag(String namespace, String name)219     public XmlSerializer endTag(String namespace, String name) throws IOException,
220             IllegalArgumentException, IllegalStateException {
221         mNesting--;
222         if (mInTag) {
223             append(" />\n");
224         } else {
225             if (mIndent && mLineStart) {
226                 appendIndent(mNesting);
227             }
228             append("</");
229             if (namespace != null) {
230                 append(namespace);
231                 append(':');
232             }
233             append(name);
234             append(">\n");
235         }
236         mLineStart = true;
237         mInTag = false;
238         return this;
239     }
240 
entityRef(String text)241     public void entityRef(String text) throws IOException, IllegalArgumentException,
242             IllegalStateException {
243         throw new UnsupportedOperationException();
244     }
245 
flushBytes()246     private void flushBytes() throws IOException {
247         int position;
248         if ((position = mBytes.position()) > 0) {
249             mBytes.flip();
250             mOutputStream.write(mBytes.array(), 0, position);
251             mBytes.clear();
252         }
253     }
254 
flush()255     public void flush() throws IOException {
256         //Log.i("PackageManager", "flush mPos=" + mPos);
257         if (mPos > 0) {
258             if (mOutputStream != null) {
259                 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
260                 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
261                 while (true) {
262                     if (result.isError()) {
263                         throw new IOException(result.toString());
264                     } else if (result.isOverflow()) {
265                         flushBytes();
266                         result = mCharset.encode(charBuffer, mBytes, true);
267                         continue;
268                     }
269                     break;
270                 }
271                 flushBytes();
272                 mOutputStream.flush();
273             } else {
274                 mWriter.write(mText, 0, mPos);
275                 mWriter.flush();
276             }
277             mPos = 0;
278         }
279     }
280 
getDepth()281     public int getDepth() {
282         throw new UnsupportedOperationException();
283     }
284 
getFeature(String name)285     public boolean getFeature(String name) {
286         throw new UnsupportedOperationException();
287     }
288 
getName()289     public String getName() {
290         throw new UnsupportedOperationException();
291     }
292 
getNamespace()293     public String getNamespace() {
294         throw new UnsupportedOperationException();
295     }
296 
getPrefix(String namespace, boolean generatePrefix)297     public String getPrefix(String namespace, boolean generatePrefix)
298             throws IllegalArgumentException {
299         throw new UnsupportedOperationException();
300     }
301 
getProperty(String name)302     public Object getProperty(String name) {
303         throw new UnsupportedOperationException();
304     }
305 
ignorableWhitespace(String text)306     public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
307             IllegalStateException {
308         throw new UnsupportedOperationException();
309     }
310 
processingInstruction(String text)311     public void processingInstruction(String text) throws IOException, IllegalArgumentException,
312             IllegalStateException {
313         throw new UnsupportedOperationException();
314     }
315 
setFeature(String name, boolean state)316     public void setFeature(String name, boolean state) throws IllegalArgumentException,
317             IllegalStateException {
318         if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
319             mIndent = true;
320             return;
321         }
322         throw new UnsupportedOperationException();
323     }
324 
setOutput(OutputStream os, String encoding)325     public void setOutput(OutputStream os, String encoding) throws IOException,
326             IllegalArgumentException, IllegalStateException {
327         if (os == null)
328             throw new IllegalArgumentException();
329         if (true) {
330             try {
331                 mCharset = Charset.forName(encoding).newEncoder()
332                         .onMalformedInput(CodingErrorAction.REPLACE)
333                         .onUnmappableCharacter(CodingErrorAction.REPLACE);
334             } catch (IllegalCharsetNameException e) {
335                 throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
336                         encoding).initCause(e));
337             } catch (UnsupportedCharsetException e) {
338                 throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
339                         encoding).initCause(e));
340             }
341             mOutputStream = os;
342         } else {
343             setOutput(
344                 encoding == null
345                     ? new OutputStreamWriter(os)
346                     : new OutputStreamWriter(os, encoding));
347         }
348     }
349 
setOutput(Writer writer)350     public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
351             IllegalStateException {
352         mWriter = writer;
353     }
354 
setPrefix(String prefix, String namespace)355     public void setPrefix(String prefix, String namespace) throws IOException,
356             IllegalArgumentException, IllegalStateException {
357         throw new UnsupportedOperationException();
358     }
359 
setProperty(String name, Object value)360     public void setProperty(String name, Object value) throws IllegalArgumentException,
361             IllegalStateException {
362         throw new UnsupportedOperationException();
363     }
364 
startDocument(String encoding, Boolean standalone)365     public void startDocument(String encoding, Boolean standalone) throws IOException,
366             IllegalArgumentException, IllegalStateException {
367         append("<?xml version='1.0' encoding='utf-8' standalone='"
368                 + (standalone ? "yes" : "no") + "' ?>\n");
369         mLineStart = true;
370     }
371 
startTag(String namespace, String name)372     public XmlSerializer startTag(String namespace, String name) throws IOException,
373             IllegalArgumentException, IllegalStateException {
374         if (mInTag) {
375             append(">\n");
376         }
377         if (mIndent) {
378             appendIndent(mNesting);
379         }
380         mNesting++;
381         append('<');
382         if (namespace != null) {
383             append(namespace);
384             append(':');
385         }
386         append(name);
387         mInTag = true;
388         mLineStart = false;
389         return this;
390     }
391 
text(char[] buf, int start, int len)392     public XmlSerializer text(char[] buf, int start, int len) throws IOException,
393             IllegalArgumentException, IllegalStateException {
394         if (mInTag) {
395             append(">");
396             mInTag = false;
397         }
398         escapeAndAppendString(buf, start, len);
399         if (mIndent) {
400             mLineStart = buf[start+len-1] == '\n';
401         }
402         return this;
403     }
404 
text(String text)405     public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
406             IllegalStateException {
407         if (mInTag) {
408             append(">");
409             mInTag = false;
410         }
411         escapeAndAppendString(text);
412         if (mIndent) {
413             mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
414         }
415         return this;
416     }
417 
418 }
419