| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.util; |
| |
| import org.xmlpull.v1.XmlSerializer; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.io.Writer; |
| import java.nio.ByteBuffer; |
| import java.nio.CharBuffer; |
| import java.nio.charset.Charset; |
| import java.nio.charset.CharsetEncoder; |
| import java.nio.charset.CoderResult; |
| import java.nio.charset.CodingErrorAction; |
| import java.nio.charset.IllegalCharsetNameException; |
| import java.nio.charset.UnsupportedCharsetException; |
| |
| /** |
| * This is a quick and dirty implementation of XmlSerializer that isn't horribly |
| * painfully slow like the normal one. It only does what is needed for the |
| * specific XML files being written with it. |
| */ |
| public class FastXmlSerializer implements XmlSerializer { |
| private static final String ESCAPE_TABLE[] = new String[] { |
| "�", "", "", "", "", "", "", "", // 0-7 |
| "", "	", " ", "", "", " ", "", "", // 8-15 |
| "", "", "", "", "", "", "", "", // 16-23 |
| "", "", "", "", "", "", "", "", // 24-31 |
| null, null, """, null, null, null, "&", null, // 32-39 |
| null, null, null, null, null, null, null, null, // 40-47 |
| null, null, null, null, null, null, null, null, // 48-55 |
| null, null, null, null, "<", null, ">", null, // 56-63 |
| }; |
| |
| private static final int BUFFER_LEN = 8192; |
| |
| private static String sSpace = " "; |
| |
| private final char[] mText = new char[BUFFER_LEN]; |
| private int mPos; |
| |
| private Writer mWriter; |
| |
| private OutputStream mOutputStream; |
| private CharsetEncoder mCharset; |
| private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); |
| |
| private boolean mIndent = false; |
| private boolean mInTag; |
| |
| private int mNesting = 0; |
| private boolean mLineStart = true; |
| |
| private void append(char c) throws IOException { |
| int pos = mPos; |
| if (pos >= (BUFFER_LEN-1)) { |
| flush(); |
| pos = mPos; |
| } |
| mText[pos] = c; |
| mPos = pos+1; |
| } |
| |
| private void append(String str, int i, final int length) throws IOException { |
| if (length > BUFFER_LEN) { |
| final int end = i + length; |
| while (i < end) { |
| int next = i + BUFFER_LEN; |
| append(str, i, next<end ? BUFFER_LEN : (end-i)); |
| i = next; |
| } |
| return; |
| } |
| int pos = mPos; |
| if ((pos+length) > BUFFER_LEN) { |
| flush(); |
| pos = mPos; |
| } |
| str.getChars(i, i+length, mText, pos); |
| mPos = pos + length; |
| } |
| |
| private void append(char[] buf, int i, final int length) throws IOException { |
| if (length > BUFFER_LEN) { |
| final int end = i + length; |
| while (i < end) { |
| int next = i + BUFFER_LEN; |
| append(buf, i, next<end ? BUFFER_LEN : (end-i)); |
| i = next; |
| } |
| return; |
| } |
| int pos = mPos; |
| if ((pos+length) > BUFFER_LEN) { |
| flush(); |
| pos = mPos; |
| } |
| System.arraycopy(buf, i, mText, pos, length); |
| mPos = pos + length; |
| } |
| |
| private void append(String str) throws IOException { |
| append(str, 0, str.length()); |
| } |
| |
| private void appendIndent(int indent) throws IOException { |
| indent *= 4; |
| if (indent > sSpace.length()) { |
| indent = sSpace.length(); |
| } |
| append(sSpace, 0, indent); |
| } |
| |
| private void escapeAndAppendString(final String string) throws IOException { |
| final int N = string.length(); |
| final char NE = (char)ESCAPE_TABLE.length; |
| final String[] escapes = ESCAPE_TABLE; |
| int lastPos = 0; |
| int pos; |
| for (pos=0; pos<N; pos++) { |
| char c = string.charAt(pos); |
| if (c >= NE) continue; |
| String escape = escapes[c]; |
| if (escape == null) continue; |
| if (lastPos < pos) append(string, lastPos, pos-lastPos); |
| lastPos = pos + 1; |
| append(escape); |
| } |
| if (lastPos < pos) append(string, lastPos, pos-lastPos); |
| } |
| |
| private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { |
| final char NE = (char)ESCAPE_TABLE.length; |
| final String[] escapes = ESCAPE_TABLE; |
| int end = start+len; |
| int lastPos = start; |
| int pos; |
| for (pos=start; pos<end; pos++) { |
| char c = buf[pos]; |
| if (c >= NE) continue; |
| String escape = escapes[c]; |
| if (escape == null) continue; |
| if (lastPos < pos) append(buf, lastPos, pos-lastPos); |
| lastPos = pos + 1; |
| append(escape); |
| } |
| if (lastPos < pos) append(buf, lastPos, pos-lastPos); |
| } |
| |
| public XmlSerializer attribute(String namespace, String name, String value) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| append(' '); |
| if (namespace != null) { |
| append(namespace); |
| append(':'); |
| } |
| append(name); |
| append("=\""); |
| |
| escapeAndAppendString(value); |
| append('"'); |
| mLineStart = false; |
| return this; |
| } |
| |
| public void cdsect(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void comment(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void docdecl(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { |
| flush(); |
| } |
| |
| public XmlSerializer endTag(String namespace, String name) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| mNesting--; |
| if (mInTag) { |
| append(" />\n"); |
| } else { |
| if (mIndent && mLineStart) { |
| appendIndent(mNesting); |
| } |
| append("</"); |
| if (namespace != null) { |
| append(namespace); |
| append(':'); |
| } |
| append(name); |
| append(">\n"); |
| } |
| mLineStart = true; |
| mInTag = false; |
| return this; |
| } |
| |
| public void entityRef(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private void flushBytes() throws IOException { |
| int position; |
| if ((position = mBytes.position()) > 0) { |
| mBytes.flip(); |
| mOutputStream.write(mBytes.array(), 0, position); |
| mBytes.clear(); |
| } |
| } |
| |
| public void flush() throws IOException { |
| //Log.i("PackageManager", "flush mPos=" + mPos); |
| if (mPos > 0) { |
| if (mOutputStream != null) { |
| CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); |
| CoderResult result = mCharset.encode(charBuffer, mBytes, true); |
| while (true) { |
| if (result.isError()) { |
| throw new IOException(result.toString()); |
| } else if (result.isOverflow()) { |
| flushBytes(); |
| result = mCharset.encode(charBuffer, mBytes, true); |
| continue; |
| } |
| break; |
| } |
| flushBytes(); |
| mOutputStream.flush(); |
| } else { |
| mWriter.write(mText, 0, mPos); |
| mWriter.flush(); |
| } |
| mPos = 0; |
| } |
| } |
| |
| public int getDepth() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean getFeature(String name) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getName() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getNamespace() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getPrefix(String namespace, boolean generatePrefix) |
| throws IllegalArgumentException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Object getProperty(String name) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void processingInstruction(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void setFeature(String name, boolean state) throws IllegalArgumentException, |
| IllegalStateException { |
| if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { |
| mIndent = true; |
| return; |
| } |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void setOutput(OutputStream os, String encoding) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| if (os == null) |
| throw new IllegalArgumentException(); |
| if (true) { |
| try { |
| mCharset = Charset.forName(encoding).newEncoder() |
| .onMalformedInput(CodingErrorAction.REPLACE) |
| .onUnmappableCharacter(CodingErrorAction.REPLACE); |
| } catch (IllegalCharsetNameException e) { |
| throw (UnsupportedEncodingException) (new UnsupportedEncodingException( |
| encoding).initCause(e)); |
| } catch (UnsupportedCharsetException e) { |
| throw (UnsupportedEncodingException) (new UnsupportedEncodingException( |
| encoding).initCause(e)); |
| } |
| mOutputStream = os; |
| } else { |
| setOutput( |
| encoding == null |
| ? new OutputStreamWriter(os) |
| : new OutputStreamWriter(os, encoding)); |
| } |
| } |
| |
| public void setOutput(Writer writer) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| mWriter = writer; |
| } |
| |
| public void setPrefix(String prefix, String namespace) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void setProperty(String name, Object value) throws IllegalArgumentException, |
| IllegalStateException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void startDocument(String encoding, Boolean standalone) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| append("<?xml version='1.0' encoding='utf-8' standalone='" |
| + (standalone ? "yes" : "no") + "' ?>\n"); |
| mLineStart = true; |
| } |
| |
| public XmlSerializer startTag(String namespace, String name) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| if (mInTag) { |
| append(">\n"); |
| } |
| if (mIndent) { |
| appendIndent(mNesting); |
| } |
| mNesting++; |
| append('<'); |
| if (namespace != null) { |
| append(namespace); |
| append(':'); |
| } |
| append(name); |
| mInTag = true; |
| mLineStart = false; |
| return this; |
| } |
| |
| public XmlSerializer text(char[] buf, int start, int len) throws IOException, |
| IllegalArgumentException, IllegalStateException { |
| if (mInTag) { |
| append(">"); |
| mInTag = false; |
| } |
| escapeAndAppendString(buf, start, len); |
| if (mIndent) { |
| mLineStart = buf[start+len-1] == '\n'; |
| } |
| return this; |
| } |
| |
| public XmlSerializer text(String text) throws IOException, IllegalArgumentException, |
| IllegalStateException { |
| if (mInTag) { |
| append(">"); |
| mInTag = false; |
| } |
| escapeAndAppendString(text); |
| if (mIndent) { |
| mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); |
| } |
| return this; |
| } |
| |
| } |