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