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