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