Home | History | Annotate | Download | only in util
      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 
     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 
     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 
     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 
    112     private void append(String str) throws IOException {
    113         append(str, 0, str.length());
    114     }
    115 
    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 
    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 
    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 
    167     public void cdsect(String text) throws IOException, IllegalArgumentException,
    168             IllegalStateException {
    169         throw new UnsupportedOperationException();
    170     }
    171 
    172     public void comment(String text) throws IOException, IllegalArgumentException,
    173             IllegalStateException {
    174         throw new UnsupportedOperationException();
    175     }
    176 
    177     public void docdecl(String text) throws IOException, IllegalArgumentException,
    178             IllegalStateException {
    179         throw new UnsupportedOperationException();
    180     }
    181 
    182     public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
    183         flush();
    184     }
    185 
    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 
    203     public void entityRef(String text) throws IOException, IllegalArgumentException,
    204             IllegalStateException {
    205         throw new UnsupportedOperationException();
    206     }
    207 
    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 
    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 
    243     public int getDepth() {
    244         throw new UnsupportedOperationException();
    245     }
    246 
    247     public boolean getFeature(String name) {
    248         throw new UnsupportedOperationException();
    249     }
    250 
    251     public String getName() {
    252         throw new UnsupportedOperationException();
    253     }
    254 
    255     public String getNamespace() {
    256         throw new UnsupportedOperationException();
    257     }
    258 
    259     public String getPrefix(String namespace, boolean generatePrefix)
    260             throws IllegalArgumentException {
    261         throw new UnsupportedOperationException();
    262     }
    263 
    264     public Object getProperty(String name) {
    265         throw new UnsupportedOperationException();
    266     }
    267 
    268     public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
    269             IllegalStateException {
    270         throw new UnsupportedOperationException();
    271     }
    272 
    273     public void processingInstruction(String text) throws IOException, IllegalArgumentException,
    274             IllegalStateException {
    275         throw new UnsupportedOperationException();
    276     }
    277 
    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 
    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 
    309     public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
    310             IllegalStateException {
    311         mWriter = writer;
    312     }
    313 
    314     public void setPrefix(String prefix, String namespace) throws IOException,
    315             IllegalArgumentException, IllegalStateException {
    316         throw new UnsupportedOperationException();
    317     }
    318 
    319     public void setProperty(String name, Object value) throws IllegalArgumentException,
    320             IllegalStateException {
    321         throw new UnsupportedOperationException();
    322     }
    323 
    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 
    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 
    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 
    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