forked from I2P_Developers/i2p.i2p
base files from Jetty 5.1.15 for reference
This commit is contained in:
445
apps/susimail/src/src/org/mortbay/servlet/MultiPartRequest.java
Normal file
445
apps/susimail/src/src/org/mortbay/servlet/MultiPartRequest.java
Normal file
@ -0,0 +1,445 @@
|
||||
// ========================================================================
|
||||
// $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
|
||||
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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 org.mortbay.servlet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.mortbay.log.LogFactory;
|
||||
import org.mortbay.http.HttpFields;
|
||||
import org.mortbay.util.LineInput;
|
||||
import org.mortbay.util.MultiMap;
|
||||
import org.mortbay.util.StringUtil;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Multipart Form Data request.
|
||||
* <p>
|
||||
* This class decodes the multipart/form-data stream sent by
|
||||
* a HTML form that uses a file input item.
|
||||
*
|
||||
* <p><h4>Usage</h4>
|
||||
* Each part of the form data is named from the HTML form and
|
||||
* is available either via getString(name) or getInputStream(name).
|
||||
* Furthermore the MIME parameters and filename can be requested for
|
||||
* each part.
|
||||
* <pre>
|
||||
* </pre>
|
||||
*
|
||||
* @version $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
|
||||
* @author Greg Wilkins
|
||||
* @author Jim Crossley
|
||||
*/
|
||||
public class MultiPartRequest
|
||||
{
|
||||
private static Log log = LogFactory.getLog(MultiPartRequest.class);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
HttpServletRequest _request;
|
||||
LineInput _in;
|
||||
String _boundary;
|
||||
String _encoding;
|
||||
byte[] _byteBoundary;
|
||||
MultiMap _partMap = new MultiMap(10);
|
||||
int _char=-2;
|
||||
boolean _lastPart=false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param request The request containing a multipart/form-data
|
||||
* request
|
||||
* @exception IOException IOException
|
||||
*/
|
||||
public MultiPartRequest(HttpServletRequest request)
|
||||
throws IOException
|
||||
{
|
||||
_request=request;
|
||||
String content_type = request.getHeader(HttpFields.__ContentType);
|
||||
if (!content_type.startsWith("multipart/form-data"))
|
||||
throw new IOException("Not multipart/form-data request");
|
||||
|
||||
if(log.isDebugEnabled())log.debug("Multipart content type = "+content_type);
|
||||
_encoding = request.getCharacterEncoding();
|
||||
if (_encoding != null)
|
||||
_in = new LineInput(request.getInputStream(), 2048, _encoding);
|
||||
else
|
||||
_in = new LineInput(request.getInputStream());
|
||||
|
||||
// Extract boundary string
|
||||
_boundary="--"+
|
||||
value(content_type.substring(content_type.indexOf("boundary=")));
|
||||
|
||||
if(log.isDebugEnabled())log.debug("Boundary="+_boundary);
|
||||
_byteBoundary= (_boundary+"--").getBytes(StringUtil.__ISO_8859_1);
|
||||
|
||||
loadAllParts();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the part names.
|
||||
* @return an array of part names
|
||||
*/
|
||||
public String[] getPartNames()
|
||||
{
|
||||
Set s = _partMap.keySet();
|
||||
return (String[]) s.toArray(new String[s.size()]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Check if a named part is present
|
||||
* @param name The part
|
||||
* @return true if it was included
|
||||
*/
|
||||
public boolean contains(String name)
|
||||
{
|
||||
Part part = (Part)_partMap.get(name);
|
||||
return (part!=null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the data of a part as a string.
|
||||
* @param name The part name
|
||||
* @return The part data
|
||||
*/
|
||||
public String getString(String name)
|
||||
{
|
||||
List part = _partMap.getValues(name);
|
||||
if (part==null)
|
||||
return null;
|
||||
if (_encoding != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new String(((Part)part.get(0))._data, _encoding);
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
return new String(((Part)part.get(0))._data);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param name The part name
|
||||
* @return The parts data
|
||||
*/
|
||||
public String[] getStrings(String name)
|
||||
{
|
||||
List parts = _partMap.getValues(name);
|
||||
if (parts==null)
|
||||
return null;
|
||||
String[] strings = new String[parts.size()];
|
||||
|
||||
if (_encoding == null)
|
||||
{
|
||||
for (int i=0; i<strings.length; i++)
|
||||
strings[i] = new String(((Part)parts.get(i))._data);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i=0; i<strings.length; i++)
|
||||
strings[i] = new String(((Part)parts.get(i))._data, _encoding);
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the data of a part as a stream.
|
||||
* @param name The part name
|
||||
* @return Stream providing the part data
|
||||
*/
|
||||
public InputStream getInputStream(String name)
|
||||
{
|
||||
List part = (List)_partMap.getValues(name);
|
||||
if (part==null)
|
||||
return null;
|
||||
return new ByteArrayInputStream(((Part)part.get(0))._data);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public InputStream[] getInputStreams(String name)
|
||||
{
|
||||
List parts = (List)_partMap.getValues(name);
|
||||
if (parts==null)
|
||||
return null;
|
||||
InputStream[] streams = new InputStream[parts.size()];
|
||||
for (int i=0; i<streams.length; i++) {
|
||||
streams[i] = new ByteArrayInputStream(((Part)parts.get(i))._data);
|
||||
}
|
||||
return streams;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME parameters associated with a part.
|
||||
* @param name The part name
|
||||
* @return Hashtable of parameters
|
||||
*/
|
||||
public Hashtable getParams(String name)
|
||||
{
|
||||
List part = (List)_partMap.getValues(name);
|
||||
if (part==null)
|
||||
return null;
|
||||
return ((Part)part.get(0))._headers;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Hashtable[] getMultipleParams(String name)
|
||||
{
|
||||
List parts = (List)_partMap.getValues(name);
|
||||
if (parts==null)
|
||||
return null;
|
||||
Hashtable[] params = new Hashtable[parts.size()];
|
||||
for (int i=0; i<params.length; i++) {
|
||||
params[i] = ((Part)parts.get(i))._headers;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get any file name associated with a part.
|
||||
* @param name The part name
|
||||
* @return The filename
|
||||
*/
|
||||
public String getFilename(String name)
|
||||
{
|
||||
List part = (List)_partMap.getValues(name);
|
||||
if (part==null)
|
||||
return null;
|
||||
return ((Part)part.get(0))._filename;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String[] getFilenames(String name)
|
||||
{
|
||||
List parts = (List)_partMap.getValues(name);
|
||||
if (parts==null)
|
||||
return null;
|
||||
String[] filenames = new String[parts.size()];
|
||||
for (int i=0; i<filenames.length; i++) {
|
||||
filenames[i] = ((Part)parts.get(i))._filename;
|
||||
}
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void loadAllParts()
|
||||
throws IOException
|
||||
{
|
||||
// Get first boundary
|
||||
String line = _in.readLine();
|
||||
if (!line.equals(_boundary))
|
||||
{
|
||||
log.warn(line);
|
||||
throw new IOException("Missing initial multi part boundary");
|
||||
}
|
||||
|
||||
// Read each part
|
||||
while (!_lastPart)
|
||||
{
|
||||
// Read Part headers
|
||||
Part part = new Part();
|
||||
|
||||
String content_disposition=null;
|
||||
while ((line=_in.readLine())!=null)
|
||||
{
|
||||
// If blank line, end of part headers
|
||||
if (line.length()==0)
|
||||
break;
|
||||
|
||||
if(log.isDebugEnabled())log.debug("LINE="+line);
|
||||
|
||||
// place part header key and value in map
|
||||
int c = line.indexOf(':',0);
|
||||
if (c>0)
|
||||
{
|
||||
String key = line.substring(0,c).trim().toLowerCase();
|
||||
String value = line.substring(c+1,line.length()).trim();
|
||||
String ev = (String) part._headers.get(key);
|
||||
part._headers.put(key,(ev!=null)?(ev+';'+value):value);
|
||||
if(log.isDebugEnabled())log.debug(key+": "+value);
|
||||
if (key.equals("content-disposition"))
|
||||
content_disposition=value;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract content-disposition
|
||||
boolean form_data=false;
|
||||
if (content_disposition==null)
|
||||
{
|
||||
throw new IOException("Missing content-disposition");
|
||||
}
|
||||
|
||||
StringTokenizer tok =
|
||||
new StringTokenizer(content_disposition,";");
|
||||
while (tok.hasMoreTokens())
|
||||
{
|
||||
String t = tok.nextToken().trim();
|
||||
String tl = t.toLowerCase();
|
||||
if (t.startsWith("form-data"))
|
||||
form_data=true;
|
||||
else if (tl.startsWith("name="))
|
||||
part._name=value(t);
|
||||
else if (tl.startsWith("filename="))
|
||||
part._filename=value(t);
|
||||
}
|
||||
|
||||
// Check disposition
|
||||
if (!form_data)
|
||||
{
|
||||
log.warn("Non form-data part in multipart/form-data");
|
||||
continue;
|
||||
}
|
||||
if (part._name==null || part._name.length()==0)
|
||||
{
|
||||
log.warn("Part with no name in multipart/form-data");
|
||||
continue;
|
||||
}
|
||||
if(log.isDebugEnabled())log.debug("name="+part._name);
|
||||
if(log.isDebugEnabled())log.debug("filename="+part._filename);
|
||||
_partMap.add(part._name,part);
|
||||
part._data=readBytes();
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private byte[] readBytes()
|
||||
throws IOException
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
int c;
|
||||
boolean cr=false;
|
||||
boolean lf=false;
|
||||
|
||||
// loop for all lines`
|
||||
while (true)
|
||||
{
|
||||
int b=0;
|
||||
while ((c=(_char!=-2)?_char:_in.read())!=-1)
|
||||
{
|
||||
_char=-2;
|
||||
|
||||
// look for CR and/or LF
|
||||
if (c==13 || c==10)
|
||||
{
|
||||
if (c==13) _char=_in.read();
|
||||
break;
|
||||
}
|
||||
|
||||
// look for boundary
|
||||
if (b>=0 && b<_byteBoundary.length && c==_byteBoundary[b])
|
||||
b++;
|
||||
else
|
||||
{
|
||||
// this is not a boundary
|
||||
if (cr) baos.write(13);
|
||||
if (lf) baos.write(10);
|
||||
cr=lf=false;
|
||||
|
||||
if (b>0)
|
||||
baos.write(_byteBoundary,0,b);
|
||||
b=-1;
|
||||
|
||||
baos.write(c);
|
||||
}
|
||||
}
|
||||
|
||||
// check partial boundary
|
||||
if ((b>0 && b<_byteBoundary.length-2) ||
|
||||
(b==_byteBoundary.length-1))
|
||||
{
|
||||
if (cr) baos.write(13);
|
||||
if (lf) baos.write(10);
|
||||
cr=lf=false;
|
||||
baos.write(_byteBoundary,0,b);
|
||||
b=-1;
|
||||
}
|
||||
|
||||
// boundary match
|
||||
if (b>0 || c==-1)
|
||||
{
|
||||
if (b==_byteBoundary.length)
|
||||
_lastPart=true;
|
||||
if (_char==10) _char=-2;
|
||||
break;
|
||||
}
|
||||
|
||||
// handle CR LF
|
||||
if (cr) baos.write(13);
|
||||
if (lf) baos.write(10);
|
||||
cr=(c==13);
|
||||
lf=(c==10 || _char==10);
|
||||
if (_char==10) _char=-2;
|
||||
}
|
||||
if(log.isTraceEnabled())log.trace(baos.toString());
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private String value(String nameEqualsValue)
|
||||
{
|
||||
String value =
|
||||
nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
|
||||
|
||||
int i=value.indexOf(';');
|
||||
if (i>0)
|
||||
value=value.substring(0,i);
|
||||
if (value.startsWith("\""))
|
||||
{
|
||||
value=value.substring(1,value.indexOf('"',1));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
i=value.indexOf(' ');
|
||||
if (i>0)
|
||||
value=value.substring(0,i);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private class Part
|
||||
{
|
||||
String _name=null;
|
||||
String _filename=null;
|
||||
Hashtable _headers= new Hashtable(10);
|
||||
byte[] _data=null;
|
||||
}
|
||||
};
|
112
apps/susimail/src/src/org/mortbay/util/ByteArrayPool.java
Normal file
112
apps/susimail/src/src/org/mortbay/util/ByteArrayPool.java
Normal file
@ -0,0 +1,112 @@
|
||||
// ========================================================================
|
||||
// $Id: ByteArrayPool.java,v 1.9 2004/05/09 20:32:49 gregwilkins Exp $
|
||||
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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 org.mortbay.util;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Byte Array Pool
|
||||
* Simple pool for recycling byte arrays of a fixed size.
|
||||
*
|
||||
* @version $Id: ByteArrayPool.java,v 1.9 2004/05/09 20:32:49 gregwilkins Exp $
|
||||
* @author Greg Wilkins (gregw)
|
||||
*/
|
||||
public class ByteArrayPool
|
||||
{
|
||||
public static final int __POOL_SIZE=
|
||||
Integer.getInteger("org.mortbay.util.ByteArrayPool.pool_size",8).intValue();
|
||||
|
||||
public static final ThreadLocal __pools=new BAThreadLocal();
|
||||
public static int __slot;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get a byte array from the pool of known size.
|
||||
* @param size Size of the byte array.
|
||||
* @return Byte array of known size.
|
||||
*/
|
||||
public static byte[] getByteArray(int size)
|
||||
{
|
||||
byte[][] pool = (byte[][])__pools.get();
|
||||
boolean full=true;
|
||||
for (int i=pool.length;i-->0;)
|
||||
{
|
||||
if (pool[i]!=null && pool[i].length==size)
|
||||
{
|
||||
byte[]b = pool[i];
|
||||
pool[i]=null;
|
||||
return b;
|
||||
}
|
||||
else
|
||||
full=false;
|
||||
}
|
||||
|
||||
if (full)
|
||||
for (int i=pool.length;i-->0;)
|
||||
pool[i]=null;
|
||||
|
||||
return new byte[size];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static byte[] getByteArrayAtLeast(int minSize)
|
||||
{
|
||||
byte[][] pool = (byte[][])__pools.get();
|
||||
for (int i=pool.length;i-->0;)
|
||||
{
|
||||
if (pool[i]!=null && pool[i].length>=minSize)
|
||||
{
|
||||
byte[]b = pool[i];
|
||||
pool[i]=null;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
return new byte[minSize];
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static void returnByteArray(final byte[] b)
|
||||
{
|
||||
if (b==null)
|
||||
return;
|
||||
|
||||
byte[][] pool = (byte[][])__pools.get();
|
||||
for (int i=pool.length;i-->0;)
|
||||
{
|
||||
if (pool[i]==null)
|
||||
{
|
||||
pool[i]=b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// slot.
|
||||
int s = __slot++;
|
||||
if (s<0)s=-s;
|
||||
pool[s%pool.length]=b;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
private static final class BAThreadLocal extends ThreadLocal
|
||||
{
|
||||
protected Object initialValue()
|
||||
{
|
||||
return new byte[__POOL_SIZE][];
|
||||
}
|
||||
}
|
||||
}
|
719
apps/susimail/src/src/org/mortbay/util/LineInput.java
Normal file
719
apps/susimail/src/src/org/mortbay/util/LineInput.java
Normal file
@ -0,0 +1,719 @@
|
||||
// ========================================================================
|
||||
// $Id: LineInput.java,v 1.17 2005/10/05 11:32:40 gregwilkins Exp $
|
||||
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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 org.mortbay.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.mortbay.log.LogFactory;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** 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.17 2005/10/05 11:32:40 gregwilkins Exp $
|
||||
* @author Greg Wilkins (gregw)
|
||||
*/
|
||||
public class LineInput extends FilterInputStream
|
||||
{
|
||||
private static Log log = LogFactory.getLog(LineInput.class);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
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=8192;
|
||||
_buf=ByteArrayPool.getByteArray(bufferSize);
|
||||
_byteBuffer=new ByteBuffer(_buf);
|
||||
_lineBuffer=new LineBuffer(bufferSize);
|
||||
|
||||
try
|
||||
{
|
||||
_reader=new InputStreamReader(_byteBuffer,"UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
_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=ByteArrayPool.getByteArray(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)
|
||||
{
|
||||
log.warn(LogSupport.EXCEPTION,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);
|
||||
|
||||
int read=0;
|
||||
while(read<len && _reader.ready())
|
||||
{
|
||||
int r = _reader.read(c,off+read,len-read);
|
||||
if (r<=0)
|
||||
break;
|
||||
read+=r;
|
||||
}
|
||||
|
||||
_mark=-1;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** 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=0;
|
||||
int read=0;
|
||||
while(read<len && _reader.ready())
|
||||
{
|
||||
int r = _reader.read(_lineBuffer.buffer,
|
||||
read,
|
||||
len-read);
|
||||
if (r<=0)
|
||||
break;
|
||||
read+=r;
|
||||
}
|
||||
_lineBuffer.size=read;
|
||||
_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");
|
||||
_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()
|
||||
{
|
||||
ByteArrayPool.returnByteArray(_buf);
|
||||
_byteBuffer=null;
|
||||
_reader=null;
|
||||
_lineBuffer=null;
|
||||
_encoding=null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user