All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.internal.util.FastPrintWriter Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.util;

import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import android.util.Printer;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;

public class FastPrintWriter extends PrintWriter {
    private static class DummyWriter extends Writer {
        @Override
        public void close() throws IOException {
            UnsupportedOperationException ex
                    = new UnsupportedOperationException("Shouldn't be here");
            throw ex;
        }

        @Override
        public void flush() throws IOException {
            close();
        }

        @Override
        public void write(char[] buf, int offset, int count) throws IOException {
            close();
        }
    };

    private final int mBufferLen;
    private final char[] mText;
    private int mPos;

    final private OutputStream mOutputStream;
    final private boolean mAutoFlush;
    final private String mSeparator;

    final private Writer mWriter;
    final private Printer mPrinter;

    private CharsetEncoder mCharset;
    final private ByteBuffer mBytes;

    private boolean mIoError;

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * stream. By default, the new print writer does not automatically flush its
     * contents to the target stream when a newline is encountered.
     *
     * @param out
     *            the target output stream.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    @UnsupportedAppUsage
    public FastPrintWriter(OutputStream out) {
        this(out, false, 8192);
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * stream. The parameter {@code autoFlush} determines if the print writer
     * automatically flushes its contents to the target stream when a newline is
     * encountered.
     *
     * @param out
     *            the target output stream.
     * @param autoFlush
     *            indicates whether contents are flushed upon encountering a
     *            newline sequence.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    public FastPrintWriter(OutputStream out, boolean autoFlush) {
        this(out, autoFlush, 8192);
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * stream and a custom buffer size. The parameter {@code autoFlush} determines
     * if the print writer automatically flushes its contents to the target stream
     * when a newline is encountered.
     *
     * @param out
     *            the target output stream.
     * @param autoFlush
     *            indicates whether contents are flushed upon encountering a
     *            newline sequence.
     * @param bufferLen
     *            specifies the size of the FastPrintWriter's internal buffer; the
     *            default is 8192.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
        super(new DummyWriter(), autoFlush);
        if (out == null) {
            throw new NullPointerException("out is null");
        }
        mBufferLen = bufferLen;
        mText = new char[bufferLen];
        mBytes = ByteBuffer.allocate(mBufferLen);
        mOutputStream = out;
        mWriter = null;
        mPrinter = null;
        mAutoFlush = autoFlush;
        mSeparator = System.lineSeparator();
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code wr} as its target
     * writer. By default, the new print writer does not automatically flush its
     * contents to the target writer when a newline is encountered.
     *
     * 

NOTE: Unlike PrintWriter, this version will still do buffering inside of * FastPrintWriter before sending data to the Writer. This means you must call * flush() before retrieving any data from the Writer.

* * @param wr * the target writer. * @throws NullPointerException * if {@code wr} is {@code null}. */ public FastPrintWriter(Writer wr) { this(wr, false, 8192); } /** * Constructs a new {@code PrintWriter} with {@code wr} as its target * writer. The parameter {@code autoFlush} determines if the print writer * automatically flushes its contents to the target writer when a newline is * encountered. * * @param wr * the target writer. * @param autoFlush * indicates whether to flush contents upon encountering a * newline sequence. * @throws NullPointerException * if {@code out} is {@code null}. */ public FastPrintWriter(Writer wr, boolean autoFlush) { this(wr, autoFlush, 8192); } /** * Constructs a new {@code PrintWriter} with {@code wr} as its target * writer and a custom buffer size. The parameter {@code autoFlush} determines * if the print writer automatically flushes its contents to the target writer * when a newline is encountered. * * @param wr * the target writer. * @param autoFlush * indicates whether to flush contents upon encountering a * newline sequence. * @param bufferLen * specifies the size of the FastPrintWriter's internal buffer; the * default is 8192. * @throws NullPointerException * if {@code wr} is {@code null}. */ public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) { super(new DummyWriter(), autoFlush); if (wr == null) { throw new NullPointerException("wr is null"); } mBufferLen = bufferLen; mText = new char[bufferLen]; mBytes = null; mOutputStream = null; mWriter = wr; mPrinter = null; mAutoFlush = autoFlush; mSeparator = System.lineSeparator(); initDefaultEncoder(); } /** * Constructs a new {@code PrintWriter} with {@code pr} as its target * printer and the default buffer size. Because a {@link Printer} is line-base, * autoflush is always enabled. * * @param pr * the target writer. * @throws NullPointerException * if {@code pr} is {@code null}. */ public FastPrintWriter(Printer pr) { this(pr, 512); } /** * Constructs a new {@code PrintWriter} with {@code pr} as its target * printer and a custom buffer size. Because a {@link Printer} is line-base, * autoflush is always enabled. * * @param pr * the target writer. * @param bufferLen * specifies the size of the FastPrintWriter's internal buffer; the * default is 512. * @throws NullPointerException * if {@code pr} is {@code null}. */ public FastPrintWriter(Printer pr, int bufferLen) { super(new DummyWriter(), true); if (pr == null) { throw new NullPointerException("pr is null"); } mBufferLen = bufferLen; mText = new char[bufferLen]; mBytes = null; mOutputStream = null; mWriter = null; mPrinter = pr; mAutoFlush = true; mSeparator = System.lineSeparator(); initDefaultEncoder(); } private final void initEncoder(String csn) throws UnsupportedEncodingException { try { mCharset = Charset.forName(csn).newEncoder(); } catch (Exception e) { throw new UnsupportedEncodingException(csn); } mCharset.onMalformedInput(CodingErrorAction.REPLACE); mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); } /** * Flushes this writer and returns the value of the error flag. * * @return {@code true} if either an {@code IOException} has been thrown * previously or if {@code setError()} has been called; * {@code false} otherwise. * @see #setError() */ public boolean checkError() { flush(); synchronized (lock) { return mIoError; } } /** * Sets the error state of the stream to false. * @since 1.6 */ protected void clearError() { synchronized (lock) { mIoError = false; } } /** * Sets the error flag of this writer to true. */ protected void setError() { synchronized (lock) { mIoError = true; } } private final void initDefaultEncoder() { mCharset = Charset.defaultCharset().newEncoder(); mCharset.onMalformedInput(CodingErrorAction.REPLACE); mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); } private void appendLocked(char c) throws IOException { int pos = mPos; if (pos >= (mBufferLen-1)) { flushLocked(); pos = mPos; } mText[pos] = c; mPos = pos+1; } private void appendLocked(String str, int i, final int length) throws IOException { final int BUFFER_LEN = mBufferLen; if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; appendLocked(str, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = mPos; if ((pos+length) > BUFFER_LEN) { flushLocked(); pos = mPos; } str.getChars(i, i + length, mText, pos); mPos = pos + length; } private void appendLocked(char[] buf, int i, final int length) throws IOException { final int BUFFER_LEN = mBufferLen; if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = mPos; if ((pos+length) > BUFFER_LEN) { flushLocked(); pos = mPos; } System.arraycopy(buf, i, mText, pos, length); mPos = pos + length; } private void flushBytesLocked() throws IOException { if (!mIoError) { int position; if ((position = mBytes.position()) > 0) { mBytes.flip(); mOutputStream.write(mBytes.array(), 0, position); mBytes.clear(); } } } private void flushLocked() throws IOException { //Log.i("PackageManager", "flush mPos=" + mPos); if (mPos > 0) { if (mOutputStream != null) { CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); CoderResult result = mCharset.encode(charBuffer, mBytes, true); while (!mIoError) { if (result.isError()) { throw new IOException(result.toString()); } else if (result.isOverflow()) { flushBytesLocked(); result = mCharset.encode(charBuffer, mBytes, true); continue; } break; } if (!mIoError) { flushBytesLocked(); mOutputStream.flush(); } } else if (mWriter != null) { if (!mIoError) { mWriter.write(mText, 0, mPos); mWriter.flush(); } } else { int nonEolOff = 0; final int sepLen = mSeparator.length(); final int len = sepLen < mPos ? sepLen : mPos; while (nonEolOff < len && mText[mPos-1-nonEolOff] == mSeparator.charAt(mSeparator.length()-1-nonEolOff)) { nonEolOff++; } if (nonEolOff >= mPos) { mPrinter.println(""); } else { mPrinter.println(new String(mText, 0, mPos-nonEolOff)); } } mPos = 0; } } /** * Ensures that all pending data is sent out to the target. It also * flushes the target. If an I/O error occurs, this writer's error * state is set to {@code true}. */ @Override public void flush() { synchronized (lock) { try { flushLocked(); if (!mIoError) { if (mOutputStream != null) { mOutputStream.flush(); } else if (mWriter != null) { mWriter.flush(); } } } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } @Override public void close() { synchronized (lock) { try { flushLocked(); if (mOutputStream != null) { mOutputStream.close(); } else if (mWriter != null) { mWriter.close(); } } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Prints the string representation of the specified character array * to the target. * * @param charArray * the character array to print to the target. * @see #print(String) */ public void print(char[] charArray) { synchronized (lock) { try { appendLocked(charArray, 0, charArray.length); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Prints the string representation of the specified character to the * target. * * @param ch * the character to print to the target. * @see #print(String) */ public void print(char ch) { synchronized (lock) { try { appendLocked(ch); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Prints a string to the target. The string is converted to an array of * bytes using the encoding chosen during the construction of this writer. * The bytes are then written to the target with {@code write(int)}. *

* If an I/O error occurs, this writer's error flag is set to {@code true}. * * @param str * the string to print to the target. * @see #write(int) */ public void print(String str) { if (str == null) { str = String.valueOf((Object) null); } synchronized (lock) { try { appendLocked(str, 0, str.length()); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } @Override public void print(int inum) { if (inum == 0) { print("0"); } else { super.print(inum); } } @Override public void print(long lnum) { if (lnum == 0) { print("0"); } else { super.print(lnum); } } /** * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}. */ public void println() { synchronized (lock) { try { appendLocked(mSeparator, 0, mSeparator.length()); if (mAutoFlush) { flushLocked(); } } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } @Override public void println(int inum) { if (inum == 0) { println("0"); } else { super.println(inum); } } @Override public void println(long lnum) { if (lnum == 0) { println("0"); } else { super.println(lnum); } } /** * Prints the string representation of the character array {@code chars} followed by a newline. * Flushes this writer if the autoFlush flag is set to {@code true}. */ public void println(char[] chars) { print(chars); println(); } /** * Prints the string representation of the char {@code c} followed by a newline. * Flushes this writer if the autoFlush flag is set to {@code true}. */ public void println(char c) { print(c); println(); } /** * Writes {@code count} characters from {@code buffer} starting at {@code * offset} to the target. *

* This writer's error flag is set to {@code true} if this writer is closed * or an I/O error occurs. * * @param buf * the buffer to write to the target. * @param offset * the index of the first character in {@code buffer} to write. * @param count * the number of characters in {@code buffer} to write. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code buf}. */ @Override public void write(char[] buf, int offset, int count) { synchronized (lock) { try { appendLocked(buf, offset, count); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Writes one character to the target. Only the two least significant bytes * of the integer {@code oneChar} are written. *

* This writer's error flag is set to {@code true} if this writer is closed * or an I/O error occurs. * * @param oneChar * the character to write to the target. */ @Override public void write(int oneChar) { synchronized (lock) { try { appendLocked((char) oneChar); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Writes the characters from the specified string to the target. * * @param str * the non-null string containing the characters to write. */ @Override public void write(String str) { synchronized (lock) { try { appendLocked(str, 0, str.length()); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Writes {@code count} characters from {@code str} starting at {@code * offset} to the target. * * @param str * the non-null string containing the characters to write. * @param offset * the index of the first character in {@code str} to write. * @param count * the number of characters from {@code str} to write. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code str}. */ @Override public void write(String str, int offset, int count) { synchronized (lock) { try { appendLocked(str, offset, count); } catch (IOException e) { Log.w("FastPrintWriter", "Write failure", e); setError(); } } } /** * Appends a subsequence of the character sequence {@code csq} to the * target. This method works the same way as {@code * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code * csq} is {@code null}, then the specified subsequence of the string "null" * will be written to the target. * * @param csq * the character sequence appended to the target. * @param start * the index of the first char in the character sequence appended * to the target. * @param end * the index of the character following the last character of the * subsequence appended to the target. * @return this writer. * @throws StringIndexOutOfBoundsException * if {@code start > end}, {@code start < 0}, {@code end < 0} or * either {@code start} or {@code end} are greater or equal than * the length of {@code csq}. */ @Override public PrintWriter append(CharSequence csq, int start, int end) { if (csq == null) { csq = "null"; } String output = csq.subSequence(start, end).toString(); write(output, 0, output.length()); return this; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy