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