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

org.owasp.encoder.EncodedWriter Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
// Copyright (c) 2012 Jeff Ichnowski
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above
//       copyright notice, this list of conditions and the following
//       disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
//     * Neither the name of the OWASP nor the names of its
//       contributors may be used to endorse or promote products
//       derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
package org.owasp.encoder;

import java.io.IOException;
import java.io.Writer;
import java.nio.CharBuffer;
import java.nio.charset.CoderResult;

/**
 * EncodedWriter -- A writer the encodes all input for a specific context and writes the encoded output to another writer.
 *
 * @author Jeff Ichnowski
 */
public class EncodedWriter extends Writer {

    /**
     * Buffer size to allocate.
     *
     */
    static final int BUFFER_SIZE = 1024;
    /**
     * Buffer to use for handling characters remaining in the input buffer after an encode. The value is set high enough to handle
     * the lookaheads of all the encoders in the package.
     */
    static final int LEFT_OVER_BUFFER = 16;

    /**
     * The wrapped writer.
     */
    private Writer _out;

    /**
     * The encoder used to encode input to the output writer.
     */
    private Encoder _encoder;

    /**
     * Where encoded output is buffered before sending on to the output writer.
     */
    private CharBuffer _buffer = CharBuffer.allocate(BUFFER_SIZE);

    /**
     * Some encoders require more input or an explicit end-of-input flag before they will process the remaining characters of an
     * input buffer. Because the writer API cannot pass this information on to the caller (e.g. by returning how many bytes were
     * actually written), this writer implementation must buffer up the remaining characters between calls. The
     * _hasLeftOver boolean is a flag used to indicate that there are left over characters in the buffer.
     */
    private boolean _hasLeftOver;

    /**
     * See comment on _hasLeftOver. This buffer is created on-demand once. Whether it has anything to flush is determined by the
     * _hasLeftOver flag.
     */
    private CharBuffer _leftOverBuffer;

    /**
     * Creates an EncodedWriter that uses the specified encoder to encode all input before sending it to the wrapped writer.
     *
     * @param out the target for all writes
     * @param encoder the encoder to use
     */
    public EncodedWriter(Writer out, Encoder encoder) {
        super(out);

//      Reduntant null check, super(out) checks for null and throws NPE.
//        if (out == null) {
//            throw new NullPointerException("writer must not be null");
//        }
        if (encoder == null) {
            throw new NullPointerException("encoder must not be null");
        }

        _out = out;

        _encoder = encoder;
    }

    /**
     * Creates an EncodedWriter that uses the specified encoder to encode all input before sending it to the wrapped writer. This
     * method is equivalent to calling:
     * 
     *     new EncodedWriter(out, Encoders.forName(contextName));
     * 
* * @param out the target for all writes * @param contextName the encoding context name. * @throws UnsupportedContextException if the contextName is unrecognized or not supported. */ public EncodedWriter(Writer out, String contextName) throws UnsupportedContextException { this(out, Encoders.forName(contextName)); } @Override public void write(char[] cbuf, int off, int len) throws IOException { synchronized (lock) { CharBuffer input = CharBuffer.wrap(cbuf); input.limit(off + len).position(off); flushLeftOver(input); for (;;) { CoderResult cr = _encoder.encode(input, _buffer, false); if (cr.isUnderflow()) { if (input.hasRemaining()) { if (_leftOverBuffer == null) { _leftOverBuffer = CharBuffer.allocate(LEFT_OVER_BUFFER); } _leftOverBuffer.put(input); _hasLeftOver = true; } return; } if (cr.isOverflow()) { flushBufferToWriter(); } } } } /** * Flushes the contents of the buffer to the writer and resets the buffer to make room for more input. * * @throws IOException thrown by the wrapped output. */ private void flushBufferToWriter() throws IOException { _out.write(_buffer.array(), 0, _buffer.position()); _buffer.clear(); } /** * Flushes the left-over buffer. Characters from the input buffer are used to add more data to the _leftOverBuffer in order to * make the flush happen. * * @param input the next input to encode, or null if at end of file. * @throws IOException from the underlying writer. */ private void flushLeftOver(CharBuffer input) throws IOException { if (!_hasLeftOver) { return; } for (;;) { if (input != null && input.hasRemaining()) { _leftOverBuffer.put(input.get()); } _leftOverBuffer.flip(); CoderResult cr = _encoder.encode(_leftOverBuffer, _buffer, input == null); if (cr.isUnderflow()) { if (_leftOverBuffer.hasRemaining()) { _leftOverBuffer.compact(); } else { break; } } if (cr.isOverflow()) { flushBufferToWriter(); } } _hasLeftOver = false; _leftOverBuffer.clear(); } @Override public void flush() throws IOException { synchronized (lock) { flushBufferToWriter(); _out.flush(); } } @Override public void close() throws IOException { synchronized (lock) { flushLeftOver(null); flushBufferToWriter(); _out.close(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy