/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ //@@3RD PARTY CODE@@ // =========================================================================== // Copyright (c) 1996 Mort Bay Consulting Pty. Ltd. All rights reserved. // Id: LineInput.java,v 1.9 2003/01/19 21:39:29 gregwilkins Exp // --------------------------------------------------------------------------- package com.sun.xml.bind.webapp.multipart; import java.io.ByteArrayInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; /* ------------------------------------------------------------ */ /** Fast LineInput InputStream. * This buffered InputStream provides methods for reading lines * of bytes. The lines can be converted to String or character * arrays either using the default encoding or a user supplied * encoding. * * Buffering and data copying are highly optimized, making this * an ideal class for protocols that mix character encoding lines * with arbitrary byte data (eg HTTP). * * The buffer size is also the maximum line length in bytes and/or * characters. If the byte length of a line is less than the max, * but the character length is greater, than then trailing characters * are lost. * * Line termination is forgiving and accepts CR, LF, CRLF or EOF. * Line input uses the mark/reset mechanism, so any marks set * prior to a readLine call are lost. * * @version Id: LineInput.java,v 1.9 2003/01/19 21:39:29 gregwilkins Exp * @author Greg Wilkins (gregw) */ public class LineInput extends FilterInputStream { /* ------------------------------------------------------------ */ private byte _buf[]; private ByteBuffer _byteBuffer; private InputStreamReader _reader; private int _mark=-1; // reset marker private int _pos; // Start marker private int _avail; // Available back marker, may be byte limited private int _contents; // Absolute back marker of buffer private int _byteLimit=-1; private boolean _newByteLimit; private LineBuffer _lineBuffer; private String _encoding; private boolean _eof=false; private boolean _lastCr=false; private boolean _seenCrLf=false; private final static int LF=10; private final static int CR=13; /* ------------------------------------------------------------ */ /** Constructor. * Default buffer and maximum line size is 2048. * @param in The underlying input stream. */ public LineInput(InputStream in) { this(in,0); } /* ------------------------------------------------------------ */ /** Constructor. * @param in The underlying input stream. * @param bufferSize The buffer size and maximum line length. */ public LineInput(InputStream in, int bufferSize) { super(in); _mark=-1; if (bufferSize==0) bufferSize=2048; _buf=new byte[bufferSize]; _byteBuffer=new ByteBuffer(_buf); _lineBuffer=new LineBuffer(bufferSize); _reader=new InputStreamReader(_byteBuffer); } /* ------------------------------------------------------------ */ /** Constructor. * @param in The underlying input stream. * @param bufferSize The buffer size and maximum line length. * @param encoding the character encoding to use for readLine methods. * @exception UnsupportedEncodingException */ public LineInput(InputStream in, int bufferSize, String encoding) throws UnsupportedEncodingException { super(in); _mark=-1; if (bufferSize==0) bufferSize=2048; _buf=new byte[bufferSize]; _byteBuffer=new ByteBuffer(_buf); _lineBuffer=new LineBuffer(bufferSize); _reader=new InputStreamReader(_byteBuffer,encoding); _encoding=encoding; } /* ------------------------------------------------------------ */ public InputStream getInputStream() { return in; } /* ------------------------------------------------------------ */ /** Set the byte limit. * If set, only this number of bytes are read before EOF. * @param bytes Limit number of bytes, or -1 for no limit. */ public void setByteLimit(int bytes) { _byteLimit=bytes; if (bytes>=0) { _newByteLimit=true; _byteLimit-=_contents-_pos; if (_byteLimit<0) { _avail+=_byteLimit; _byteLimit=0; } } else { _newByteLimit=false; _avail=_contents; _eof=false; } } /* ------------------------------------------------------------ */ /** Get the byte limit. * @return Number of bytes until EOF is returned or -1 for no limit. */ public int getByteLimit() { if (_byteLimit<0) return _byteLimit; return _byteLimit+_avail-_pos; } /* ------------------------------------------------------------ */ /** Read a line ended by CR, LF or CRLF. * The default or supplied encoding is used to convert bytes to * characters. * @return The line as a String or null for EOF. * @exception IOException */ public synchronized String readLine() throws IOException { int len=fillLine(_buf.length); if (len<0) return null; String s=null; if (_encoding==null) s=new String(_buf,_mark,len); else { try { s=new String(_buf,_mark,len,_encoding); } catch(UnsupportedEncodingException e) { System.err.println(e); } } _mark=-1; return s; } /* ------------------------------------------------------------ */ /** Read a line ended by CR, LF or CRLF. * The default or supplied encoding is used to convert bytes to * characters. * @param c Character buffer to place the line into. * @param off Offset into the buffer. * @param len Maximum length of line. * @return The length of the line or -1 for EOF. * @exception IOException */ public int readLine(char[] c,int off,int len) throws IOException { int blen=fillLine(len); if (blen<0) return -1; if (blen==0) return 0; _byteBuffer.setStream(_mark,blen); len=_reader.read(c,off,len); _mark=-1; return len; } /* ------------------------------------------------------------ */ /** Read a line ended by CR, LF or CRLF. * @param b Byte array to place the line into. * @param off Offset into the buffer. * @param len Maximum length of line. * @return The length of the line or -1 for EOF. * @exception IOException */ public int readLine(byte[] b,int off,int len) throws IOException { len=fillLine(len); if (len<0) return -1; if (len==0) return 0; System.arraycopy(_buf,_mark, b, off, len); _mark=-1; return len; } /* ------------------------------------------------------------ */ /** Read a Line ended by CR, LF or CRLF. * Read a line into a shared LineBuffer instance. The LineBuffer is * resused between calls and should not be held by the caller. * The default or supplied encoding is used to convert bytes to * characters. * @return LineBuffer instance or null for EOF. * @exception IOException */ public LineBuffer readLineBuffer() throws IOException { return readLineBuffer(_buf.length); } /* ------------------------------------------------------------ */ /** Read a Line ended by CR, LF or CRLF. * Read a line into a shared LineBuffer instance. The LineBuffer is * resused between calls and should not be held by the caller. * The default or supplied encoding is used to convert bytes to * characters. * @param len Maximum length of a line, or 0 for default * @return LineBuffer instance or null for EOF. * @exception IOException */ public LineBuffer readLineBuffer(int len) throws IOException { len=fillLine(len>0?len:_buf.length); if (len<0) return null; if (len==0) { _lineBuffer.size=0; return _lineBuffer; } _byteBuffer.setStream(_mark,len); _lineBuffer.size= _reader.read(_lineBuffer.buffer,0,_lineBuffer.buffer.length); _mark=-1; return _lineBuffer; } /* ------------------------------------------------------------ */ public synchronized int read() throws IOException { int b; if (_pos >=_avail) fill(); if (_pos >=_avail) b=-1; else b=_buf[_pos++]&255; return b; } /* ------------------------------------------------------------ */ public synchronized int read(byte b[], int off, int len) throws IOException { int avail=_avail-_pos; if (avail <=0) { fill(); avail=_avail-_pos; } if (avail <=0) len=-1; else { len=(avail < len) ? avail : len; System.arraycopy(_buf,_pos,b,off,len); _pos +=len; } return len; } /* ------------------------------------------------------------ */ public long skip(long n) throws IOException { int avail=_avail-_pos; if (avail <=0) { fill(); avail=_avail-_pos; } if (avail <=0) n=0; else { n=(avail < n) ? avail : n; _pos +=n; } return n; } /* ------------------------------------------------------------ */ public synchronized int available() throws IOException { int in_stream=in.available(); if (_byteLimit>=0 && in_stream>_byteLimit) in_stream=_byteLimit; return _avail - _pos + in_stream; } /* ------------------------------------------------------------ */ public synchronized void mark(int limit) throws IllegalArgumentException { if (limit>_buf.length) { byte[] new_buf=new byte[limit]; System.arraycopy(_buf,_pos,new_buf,_pos,_avail-_pos); _buf=new_buf; if (_byteBuffer!=null) _byteBuffer.setBuffer(_buf); } _mark=_pos; } /* ------------------------------------------------------------ */ public synchronized void reset() throws IOException { if (_mark < 0) throw new IOException("Resetting to invalid mark"); if (_byteLimit>=0) _byteLimit+=_pos-_mark; _pos=_mark; _mark=-1; } /* ------------------------------------------------------------ */ public boolean markSupported() { return true; } /* ------------------------------------------------------------ */ private void fill() throws IOException { // if the mark is in the middle of the buffer if (_mark > 0) { // moved saved bytes to start of buffer int saved=_contents - _mark; System.arraycopy(_buf, _mark, _buf, 0, saved); _pos-=_mark; _avail-=_mark; _contents=saved; _mark=0; } else if (_mark<0 && _pos>0) { // move remaining bytes to start of buffer int saved=_contents-_pos; System.arraycopy(_buf,_pos, _buf, 0, saved); _avail-=_pos; _contents=saved; _pos=0; } else if (_mark==0 && _pos>0 && _contents==_buf.length) { // Discard the mark as we need the space. _mark=-1; fill(); return; } // Get ready to top up the buffer int n=0; _eof=false; // Handle byte limited EOF if (_byteLimit==0) _eof=true; // else loop until something is read. else while (!_eof && n==0 && _buf.length>_contents) { // try to read as much as will fit. int space=_buf.length-_contents; n=in.read(_buf,_contents,space); if (n<=0) { // If no bytes - we could be NBIO, so we want to avoid // a busy loop. if (n==0) { // Yield to give a chance for some bytes to turn up Thread.yield(); // Do a byte read as that is blocking int b = in.read(); if (b>=0) { n=1; _buf[_contents++]=(byte)b; } else _eof=true; } else _eof=true; } else _contents+=n; _avail=_contents; // If we have a byte limit if (_byteLimit>0) { // adjust the bytes available if (_contents-_pos >=_byteLimit) _avail=_byteLimit+_pos; if (n>_byteLimit) _byteLimit=0; else if (n>=0) _byteLimit-=n; else if (n==-1) throw new IOException("Premature EOF"); } } // If we have some characters and the last read was a CR and // the first char is a LF, skip it if (_avail-_pos>0 && _lastCr && _buf[_pos]==LF) { _seenCrLf=true; _pos++; if (_mark>=0) _mark++; _lastCr=false; // If the byte limit has just been imposed, dont count // LF as content. if(_byteLimit>=0 && _newByteLimit) { if (_avail<_contents) _avail++; else _byteLimit++; } // If we ate all that ws filled, fill some more if (_pos==_avail) fill(); } _newByteLimit=false; } /* ------------------------------------------------------------ */ private int fillLine(int maxLen) throws IOException { _mark=_pos; if (_pos>=_avail) fill(); if (_pos>=_avail) return -1; byte b; boolean cr=_lastCr; boolean lf=false; _lastCr=false; int len=0; LineLoop: while (_pos<=_avail) { // if we have gone past the end of the buffer while (_pos==_avail) { // If EOF or no more space in the buffer, // return a line. if (_eof || (_mark==0 && _contents==_buf.length)) { _lastCr=!_eof && _buf[_avail-1]==CR; cr=true; lf=true; break LineLoop; } // If we have a CR and no more characters are available if (cr && in.available()==0 && !_seenCrLf) { _lastCr=true; cr=true; lf=true; break LineLoop; } else { // Else just wait for more... _pos=_mark; fill(); _pos=len; cr=false; } } // Get the byte b=_buf[_pos++]; switch(b) { case LF: if (cr) _seenCrLf=true; lf=true; break LineLoop; case CR: if (cr) { // Double CR if (_pos>1) { _pos--; break LineLoop; } } cr=true; break; default: if(cr) { if (_pos==1) cr=false; else { _pos--; break LineLoop; } } len++; if (len==maxLen) { // look for EOL if (_mark!=0 && _pos+2>=_avail && _avail<_buf.length) fill(); if (_pos<_avail && _buf[_pos]==CR) { cr=true; _pos++; } if (_pos<_avail && _buf[_pos]==LF) { lf=true; _pos++; } if (!cr && !lf) { // fake EOL lf=true; cr=true; } break LineLoop; } break; } } if (!cr && !lf && len==0) len=-1; return len; } /* ------------------------------------------------------------ */ private static class ByteBuffer extends ByteArrayInputStream { ByteBuffer(byte[] buffer) { super(buffer); } void setBuffer(byte[] buffer) { buf=buffer; } void setStream(int offset,int length) { pos=offset; count=offset+length; mark=-1; } } /* ------------------------------------------------------------ */ /** Reusable LineBuffer. * Externalized LineBuffer for fast line parsing. */ public static class LineBuffer { public char[] buffer; public int size; public LineBuffer(int maxLineLength) {buffer=new char[maxLineLength];} public String toString(){return new String(buffer,0,size);} } /* ------------------------------------------------------------ */ public void destroy() { _byteBuffer=null; _reader=null; _lineBuffer=null; _encoding=null; } }