Home | History | Annotate | Download | only in util
      1 package com.android.internal.util;
      2 
      3 import android.util.Printer;
      4 
      5 import java.io.IOException;
      6 import java.io.OutputStream;
      7 import java.io.PrintWriter;
      8 import java.io.UnsupportedEncodingException;
      9 import java.io.Writer;
     10 import java.nio.ByteBuffer;
     11 import java.nio.CharBuffer;
     12 import java.nio.charset.Charset;
     13 import java.nio.charset.CharsetEncoder;
     14 import java.nio.charset.CoderResult;
     15 import java.nio.charset.CodingErrorAction;
     16 
     17 public class FastPrintWriter extends PrintWriter {
     18     private static class DummyWriter extends Writer {
     19         @Override
     20         public void close() throws IOException {
     21             UnsupportedOperationException ex
     22                     = new UnsupportedOperationException("Shouldn't be here");
     23             throw ex;
     24         }
     25 
     26         @Override
     27         public void flush() throws IOException {
     28             close();
     29         }
     30 
     31         @Override
     32         public void write(char[] buf, int offset, int count) throws IOException {
     33             close();
     34         }
     35     };
     36 
     37     private final int mBufferLen;
     38     private final char[] mText;
     39     private int mPos;
     40 
     41     final private OutputStream mOutputStream;
     42     final private boolean mAutoFlush;
     43     final private String mSeparator;
     44 
     45     final private Writer mWriter;
     46     final private Printer mPrinter;
     47 
     48     private CharsetEncoder mCharset;
     49     final private ByteBuffer mBytes;
     50 
     51     private boolean mIoError;
     52 
     53     /**
     54      * Constructs a new {@code PrintWriter} with {@code out} as its target
     55      * stream. By default, the new print writer does not automatically flush its
     56      * contents to the target stream when a newline is encountered.
     57      *
     58      * @param out
     59      *            the target output stream.
     60      * @throws NullPointerException
     61      *             if {@code out} is {@code null}.
     62      */
     63     public FastPrintWriter(OutputStream out) {
     64         this(out, false, 8192);
     65     }
     66 
     67     /**
     68      * Constructs a new {@code PrintWriter} with {@code out} as its target
     69      * stream. The parameter {@code autoFlush} determines if the print writer
     70      * automatically flushes its contents to the target stream when a newline is
     71      * encountered.
     72      *
     73      * @param out
     74      *            the target output stream.
     75      * @param autoFlush
     76      *            indicates whether contents are flushed upon encountering a
     77      *            newline sequence.
     78      * @throws NullPointerException
     79      *             if {@code out} is {@code null}.
     80      */
     81     public FastPrintWriter(OutputStream out, boolean autoFlush) {
     82         this(out, autoFlush, 8192);
     83     }
     84 
     85     /**
     86      * Constructs a new {@code PrintWriter} with {@code out} as its target
     87      * stream and a custom buffer size. The parameter {@code autoFlush} determines
     88      * if the print writer automatically flushes its contents to the target stream
     89      * when a newline is encountered.
     90      *
     91      * @param out
     92      *            the target output stream.
     93      * @param autoFlush
     94      *            indicates whether contents are flushed upon encountering a
     95      *            newline sequence.
     96      * @param bufferLen
     97      *            specifies the size of the FastPrintWriter's internal buffer; the
     98      *            default is 8192.
     99      * @throws NullPointerException
    100      *             if {@code out} is {@code null}.
    101      */
    102     public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
    103         super(new DummyWriter(), autoFlush);
    104         if (out == null) {
    105             throw new NullPointerException("out is null");
    106         }
    107         mBufferLen = bufferLen;
    108         mText = new char[bufferLen];
    109         mBytes = ByteBuffer.allocate(mBufferLen);
    110         mOutputStream = out;
    111         mWriter = null;
    112         mPrinter = null;
    113         mAutoFlush = autoFlush;
    114         mSeparator = System.lineSeparator();
    115         initDefaultEncoder();
    116     }
    117 
    118     /**
    119      * Constructs a new {@code PrintWriter} with {@code wr} as its target
    120      * writer. By default, the new print writer does not automatically flush its
    121      * contents to the target writer when a newline is encountered.
    122      *
    123      * <p>NOTE: Unlike PrintWriter, this version will still do buffering inside of
    124      * FastPrintWriter before sending data to the Writer.  This means you must call
    125      * flush() before retrieving any data from the Writer.</p>
    126      *
    127      * @param wr
    128      *            the target writer.
    129      * @throws NullPointerException
    130      *             if {@code wr} is {@code null}.
    131      */
    132     public FastPrintWriter(Writer wr) {
    133         this(wr, false, 8192);
    134     }
    135 
    136     /**
    137      * Constructs a new {@code PrintWriter} with {@code wr} as its target
    138      * writer. The parameter {@code autoFlush} determines if the print writer
    139      * automatically flushes its contents to the target writer when a newline is
    140      * encountered.
    141      *
    142      * @param wr
    143      *            the target writer.
    144      * @param autoFlush
    145      *            indicates whether to flush contents upon encountering a
    146      *            newline sequence.
    147      * @throws NullPointerException
    148      *             if {@code out} is {@code null}.
    149      */
    150     public FastPrintWriter(Writer wr, boolean autoFlush) {
    151         this(wr, autoFlush, 8192);
    152     }
    153 
    154     /**
    155      * Constructs a new {@code PrintWriter} with {@code wr} as its target
    156      * writer and a custom buffer size. The parameter {@code autoFlush} determines
    157      * if the print writer automatically flushes its contents to the target writer
    158      * when a newline is encountered.
    159      *
    160      * @param wr
    161      *            the target writer.
    162      * @param autoFlush
    163      *            indicates whether to flush contents upon encountering a
    164      *            newline sequence.
    165      * @param bufferLen
    166      *            specifies the size of the FastPrintWriter's internal buffer; the
    167      *            default is 8192.
    168      * @throws NullPointerException
    169      *             if {@code wr} is {@code null}.
    170      */
    171     public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) {
    172         super(new DummyWriter(), autoFlush);
    173         if (wr == null) {
    174             throw new NullPointerException("wr is null");
    175         }
    176         mBufferLen = bufferLen;
    177         mText = new char[bufferLen];
    178         mBytes = null;
    179         mOutputStream = null;
    180         mWriter = wr;
    181         mPrinter = null;
    182         mAutoFlush = autoFlush;
    183         mSeparator = System.lineSeparator();
    184         initDefaultEncoder();
    185     }
    186 
    187     /**
    188      * Constructs a new {@code PrintWriter} with {@code pr} as its target
    189      * printer and the default buffer size.  Because a {@link Printer} is line-base,
    190      * autoflush is always enabled.
    191      *
    192      * @param pr
    193      *            the target writer.
    194      * @throws NullPointerException
    195      *             if {@code pr} is {@code null}.
    196      */
    197     public FastPrintWriter(Printer pr) {
    198         this(pr, 512);
    199     }
    200 
    201     /**
    202      * Constructs a new {@code PrintWriter} with {@code pr} as its target
    203      * printer and a custom buffer size.  Because a {@link Printer} is line-base,
    204      * autoflush is always enabled.
    205      *
    206      * @param pr
    207      *            the target writer.
    208      * @param bufferLen
    209      *            specifies the size of the FastPrintWriter's internal buffer; the
    210      *            default is 512.
    211      * @throws NullPointerException
    212      *             if {@code pr} is {@code null}.
    213      */
    214     public FastPrintWriter(Printer pr, int bufferLen) {
    215         super(new DummyWriter(), true);
    216         if (pr == null) {
    217             throw new NullPointerException("pr is null");
    218         }
    219         mBufferLen = bufferLen;
    220         mText = new char[bufferLen];
    221         mBytes = null;
    222         mOutputStream = null;
    223         mWriter = null;
    224         mPrinter = pr;
    225         mAutoFlush = true;
    226         mSeparator = System.lineSeparator();
    227         initDefaultEncoder();
    228     }
    229 
    230     private final void initEncoder(String csn) throws UnsupportedEncodingException {
    231         try {
    232             mCharset = Charset.forName(csn).newEncoder();
    233         } catch (Exception e) {
    234             throw new UnsupportedEncodingException(csn);
    235         }
    236         mCharset.onMalformedInput(CodingErrorAction.REPLACE);
    237         mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
    238     }
    239 
    240     /**
    241      * Flushes this writer and returns the value of the error flag.
    242      *
    243      * @return {@code true} if either an {@code IOException} has been thrown
    244      *         previously or if {@code setError()} has been called;
    245      *         {@code false} otherwise.
    246      * @see #setError()
    247      */
    248     public boolean checkError() {
    249         flush();
    250         synchronized (lock) {
    251             return mIoError;
    252         }
    253     }
    254 
    255     /**
    256      * Sets the error state of the stream to false.
    257      * @since 1.6
    258      */
    259     protected void clearError() {
    260         synchronized (lock) {
    261             mIoError = false;
    262         }
    263     }
    264 
    265     /**
    266      * Sets the error flag of this writer to true.
    267      */
    268     protected void setError() {
    269         synchronized (lock) {
    270             mIoError = true;
    271         }
    272     }
    273 
    274     private final void initDefaultEncoder() {
    275         mCharset = Charset.defaultCharset().newEncoder();
    276         mCharset.onMalformedInput(CodingErrorAction.REPLACE);
    277         mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
    278     }
    279 
    280     private void appendLocked(char c) throws IOException {
    281         int pos = mPos;
    282         if (pos >= (mBufferLen-1)) {
    283             flushLocked();
    284             pos = mPos;
    285         }
    286         mText[pos] = c;
    287         mPos = pos+1;
    288     }
    289 
    290     private void appendLocked(String str, int i, final int length) throws IOException {
    291         final int BUFFER_LEN = mBufferLen;
    292         if (length > BUFFER_LEN) {
    293             final int end = i + length;
    294             while (i < end) {
    295                 int next = i + BUFFER_LEN;
    296                 appendLocked(str, i, next < end ? BUFFER_LEN : (end - i));
    297                 i = next;
    298             }
    299             return;
    300         }
    301         int pos = mPos;
    302         if ((pos+length) > BUFFER_LEN) {
    303             flushLocked();
    304             pos = mPos;
    305         }
    306         str.getChars(i, i + length, mText, pos);
    307         mPos = pos + length;
    308     }
    309 
    310     private void appendLocked(char[] buf, int i, final int length) throws IOException {
    311         final int BUFFER_LEN = mBufferLen;
    312         if (length > BUFFER_LEN) {
    313             final int end = i + length;
    314             while (i < end) {
    315                 int next = i + BUFFER_LEN;
    316                 appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i));
    317                 i = next;
    318             }
    319             return;
    320         }
    321         int pos = mPos;
    322         if ((pos+length) > BUFFER_LEN) {
    323             flushLocked();
    324             pos = mPos;
    325         }
    326         System.arraycopy(buf, i, mText, pos, length);
    327         mPos = pos + length;
    328     }
    329 
    330     private void flushBytesLocked() throws IOException {
    331         int position;
    332         if ((position = mBytes.position()) > 0) {
    333             mBytes.flip();
    334             mOutputStream.write(mBytes.array(), 0, position);
    335             mBytes.clear();
    336         }
    337     }
    338 
    339     private void flushLocked() throws IOException {
    340         //Log.i("PackageManager", "flush mPos=" + mPos);
    341         if (mPos > 0) {
    342             if (mOutputStream != null) {
    343                 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
    344                 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
    345                 while (true) {
    346                     if (result.isError()) {
    347                         throw new IOException(result.toString());
    348                     } else if (result.isOverflow()) {
    349                         flushBytesLocked();
    350                         result = mCharset.encode(charBuffer, mBytes, true);
    351                         continue;
    352                     }
    353                     break;
    354                 }
    355                 flushBytesLocked();
    356                 mOutputStream.flush();
    357             } else if (mWriter != null) {
    358                 mWriter.write(mText, 0, mPos);
    359                 mWriter.flush();
    360             } else {
    361                 int nonEolOff = 0;
    362                 final int sepLen = mSeparator.length();
    363                 final int len = sepLen < mPos ? sepLen : mPos;
    364                 while (nonEolOff < len && mText[mPos-1-nonEolOff]
    365                         == mSeparator.charAt(mSeparator.length()-1-nonEolOff)) {
    366                     nonEolOff++;
    367                 }
    368                 if (nonEolOff >= mPos) {
    369                     mPrinter.println("");
    370                 } else {
    371                     mPrinter.println(new String(mText, 0, mPos-nonEolOff));
    372                 }
    373             }
    374             mPos = 0;
    375         }
    376     }
    377 
    378     /**
    379      * Ensures that all pending data is sent out to the target. It also
    380      * flushes the target. If an I/O error occurs, this writer's error
    381      * state is set to {@code true}.
    382      */
    383     @Override
    384     public void flush() {
    385         synchronized (lock) {
    386             try {
    387                 flushLocked();
    388                 if (mOutputStream != null) {
    389                     mOutputStream.flush();
    390                 } else if (mWriter != null) {
    391                     mWriter.flush();
    392                 }
    393             } catch (IOException e) {
    394                 setError();
    395             }
    396         }
    397     }
    398 
    399     @Override
    400     public void close() {
    401         synchronized (lock) {
    402             try {
    403                 flushLocked();
    404                 if (mOutputStream != null) {
    405                     mOutputStream.close();
    406                 } else if (mWriter != null) {
    407                     mWriter.close();
    408                 }
    409             } catch (IOException e) {
    410                 setError();
    411             }
    412         }
    413     }
    414 
    415     /**
    416      * Prints the string representation of the specified character array
    417      * to the target.
    418      *
    419      * @param charArray
    420      *            the character array to print to the target.
    421      * @see #print(String)
    422      */
    423     public void print(char[] charArray) {
    424         synchronized (lock) {
    425             try {
    426                 appendLocked(charArray, 0, charArray.length);
    427             } catch (IOException e) {
    428             }
    429         }
    430     }
    431 
    432     /**
    433      * Prints the string representation of the specified character to the
    434      * target.
    435      *
    436      * @param ch
    437      *            the character to print to the target.
    438      * @see #print(String)
    439      */
    440     public void print(char ch) {
    441         synchronized (lock) {
    442             try {
    443                 appendLocked(ch);
    444             } catch (IOException e) {
    445             }
    446         }
    447     }
    448 
    449     /**
    450      * Prints a string to the target. The string is converted to an array of
    451      * bytes using the encoding chosen during the construction of this writer.
    452      * The bytes are then written to the target with {@code write(int)}.
    453      * <p>
    454      * If an I/O error occurs, this writer's error flag is set to {@code true}.
    455      *
    456      * @param str
    457      *            the string to print to the target.
    458      * @see #write(int)
    459      */
    460     public void print(String str) {
    461         if (str == null) {
    462             str = String.valueOf((Object) null);
    463         }
    464         synchronized (lock) {
    465             try {
    466                 appendLocked(str, 0, str.length());
    467             } catch (IOException e) {
    468                 setError();
    469             }
    470         }
    471     }
    472 
    473 
    474     @Override
    475     public void print(int inum) {
    476         if (inum == 0) {
    477             print("0");
    478         } else {
    479             super.print(inum);
    480         }
    481     }
    482 
    483     @Override
    484     public void print(long lnum) {
    485         if (lnum == 0) {
    486             print("0");
    487         } else {
    488             super.print(lnum);
    489         }
    490     }
    491 
    492     /**
    493      * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}.
    494      */
    495     public void println() {
    496         synchronized (lock) {
    497             try {
    498                 appendLocked(mSeparator, 0, mSeparator.length());
    499                 if (mAutoFlush) {
    500                     flushLocked();
    501                 }
    502             } catch (IOException e) {
    503                 setError();
    504             }
    505         }
    506     }
    507 
    508     @Override
    509     public void println(int inum) {
    510         if (inum == 0) {
    511             println("0");
    512         } else {
    513             super.println(inum);
    514         }
    515     }
    516 
    517     @Override
    518     public void println(long lnum) {
    519         if (lnum == 0) {
    520             println("0");
    521         } else {
    522             super.println(lnum);
    523         }
    524     }
    525 
    526     /**
    527      * Prints the string representation of the character array {@code chars} followed by a newline.
    528      * Flushes this writer if the autoFlush flag is set to {@code true}.
    529      */
    530     public void println(char[] chars) {
    531         print(chars);
    532         println();
    533     }
    534 
    535     /**
    536      * Prints the string representation of the char {@code c} followed by a newline.
    537      * Flushes this writer if the autoFlush flag is set to {@code true}.
    538      */
    539     public void println(char c) {
    540         print(c);
    541         println();
    542     }
    543 
    544     /**
    545      * Writes {@code count} characters from {@code buffer} starting at {@code
    546      * offset} to the target.
    547      * <p>
    548      * This writer's error flag is set to {@code true} if this writer is closed
    549      * or an I/O error occurs.
    550      *
    551      * @param buf
    552      *            the buffer to write to the target.
    553      * @param offset
    554      *            the index of the first character in {@code buffer} to write.
    555      * @param count
    556      *            the number of characters in {@code buffer} to write.
    557      * @throws IndexOutOfBoundsException
    558      *             if {@code offset < 0} or {@code count < 0}, or if {@code
    559      *             offset + count} is greater than the length of {@code buf}.
    560      */
    561     @Override
    562     public void write(char[] buf, int offset, int count) {
    563         synchronized (lock) {
    564             try {
    565                 appendLocked(buf, offset, count);
    566             } catch (IOException e) {
    567             }
    568         }
    569     }
    570 
    571     /**
    572      * Writes one character to the target. Only the two least significant bytes
    573      * of the integer {@code oneChar} are written.
    574      * <p>
    575      * This writer's error flag is set to {@code true} if this writer is closed
    576      * or an I/O error occurs.
    577      *
    578      * @param oneChar
    579      *            the character to write to the target.
    580      */
    581     @Override
    582     public void write(int oneChar) {
    583         synchronized (lock) {
    584             try {
    585                 appendLocked((char) oneChar);
    586             } catch (IOException e) {
    587             }
    588         }
    589     }
    590 
    591     /**
    592      * Writes the characters from the specified string to the target.
    593      *
    594      * @param str
    595      *            the non-null string containing the characters to write.
    596      */
    597     @Override
    598     public void write(String str) {
    599         synchronized (lock) {
    600             try {
    601                 appendLocked(str, 0, str.length());
    602             } catch (IOException e) {
    603             }
    604         }
    605     }
    606 
    607     /**
    608      * Writes {@code count} characters from {@code str} starting at {@code
    609      * offset} to the target.
    610      *
    611      * @param str
    612      *            the non-null string containing the characters to write.
    613      * @param offset
    614      *            the index of the first character in {@code str} to write.
    615      * @param count
    616      *            the number of characters from {@code str} to write.
    617      * @throws IndexOutOfBoundsException
    618      *             if {@code offset < 0} or {@code count < 0}, or if {@code
    619      *             offset + count} is greater than the length of {@code str}.
    620      */
    621     @Override
    622     public void write(String str, int offset, int count) {
    623         synchronized (lock) {
    624             try {
    625                 appendLocked(str, offset, count);
    626             } catch (IOException e) {
    627             }
    628         }
    629     }
    630 
    631     /**
    632      * Appends a subsequence of the character sequence {@code csq} to the
    633      * target. This method works the same way as {@code
    634      * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
    635      * csq} is {@code null}, then the specified subsequence of the string "null"
    636      * will be written to the target.
    637      *
    638      * @param csq
    639      *            the character sequence appended to the target.
    640      * @param start
    641      *            the index of the first char in the character sequence appended
    642      *            to the target.
    643      * @param end
    644      *            the index of the character following the last character of the
    645      *            subsequence appended to the target.
    646      * @return this writer.
    647      * @throws StringIndexOutOfBoundsException
    648      *             if {@code start > end}, {@code start < 0}, {@code end < 0} or
    649      *             either {@code start} or {@code end} are greater or equal than
    650      *             the length of {@code csq}.
    651      */
    652     @Override
    653     public PrintWriter append(CharSequence csq, int start, int end) {
    654         if (csq == null) {
    655             csq = "null";
    656         }
    657         String output = csq.subSequence(start, end).toString();
    658         write(output, 0, output.length());
    659         return this;
    660     }
    661 }
    662