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