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