KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > util > LineInput


1 // ========================================================================
2
// $Id: LineInput.java,v 1.17 2005/10/05 11:32:40 gregwilkins Exp $
3
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.util;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.FilterInputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.InputStreamReader JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24
25 import org.apache.commons.logging.Log;
26 import org.mortbay.log.LogFactory;
27
28
29 /* ------------------------------------------------------------ */
30 /** Fast LineInput InputStream.
31  * This buffered InputStream provides methods for reading lines
32  * of bytes. The lines can be converted to String or character
33  * arrays either using the default encoding or a user supplied
34  * encoding.
35  *
36  * Buffering and data copying are highly optimized, making this
37  * an ideal class for protocols that mix character encoding lines
38  * with arbitrary byte data (eg HTTP).
39  *
40  * The buffer size is also the maximum line length in bytes and/or
41  * characters. If the byte length of a line is less than the max,
42  * but the character length is greater, than then trailing characters
43  * are lost.
44  *
45  * Line termination is forgiving and accepts CR, LF, CRLF or EOF.
46  * Line input uses the mark/reset mechanism, so any marks set
47  * prior to a readLine call are lost.
48  *
49  * @version $Id: LineInput.java,v 1.17 2005/10/05 11:32:40 gregwilkins Exp $
50  * @author Greg Wilkins (gregw)
51  */

52 public class LineInput extends FilterInputStream JavaDoc
53 {
54     private static Log log = LogFactory.getLog(LineInput.class);
55
56     /* ------------------------------------------------------------ */
57     private byte _buf[];
58     private ByteBuffer _byteBuffer;
59     private InputStreamReader JavaDoc _reader;
60     private int _mark=-1; // reset marker
61
private int _pos; // Start marker
62
private int _avail; // Available back marker, may be byte limited
63
private int _contents; // Absolute back marker of buffer
64
private int _byteLimit=-1;
65     private boolean _newByteLimit;
66     private LineBuffer _lineBuffer;
67     private String JavaDoc _encoding;
68     private boolean _eof=false;
69     private boolean _lastCr=false;
70     private boolean _seenCrLf=false;
71     
72     private final static int LF=10;
73     private final static int CR=13;
74
75     
76     /* ------------------------------------------------------------ */
77     /** Constructor.
78      * Default buffer and maximum line size is 2048.
79      * @param in The underlying input stream.
80      */

81     public LineInput(InputStream JavaDoc in)
82     {
83         this(in,0);
84     }
85     
86     /* ------------------------------------------------------------ */
87     /** Constructor.
88      * @param in The underlying input stream.
89      * @param bufferSize The buffer size and maximum line length.
90      */

91     public LineInput(InputStream JavaDoc in, int bufferSize)
92     {
93         super(in);
94         _mark=-1;
95         if (bufferSize==0)
96             bufferSize=8192;
97         _buf=ByteArrayPool.getByteArray(bufferSize);
98         _byteBuffer=new ByteBuffer(_buf);
99         _lineBuffer=new LineBuffer(bufferSize);
100         
101         try
102         {
103             _reader=new InputStreamReader JavaDoc(_byteBuffer,"UTF-8");
104         }
105         catch (UnsupportedEncodingException JavaDoc e)
106         {
107             _reader=new InputStreamReader JavaDoc(_byteBuffer);
108         }
109     }
110     
111     /* ------------------------------------------------------------ */
112     /** Constructor.
113      * @param in The underlying input stream.
114      * @param bufferSize The buffer size and maximum line length.
115      * @param encoding the character encoding to use for readLine methods.
116      * @exception UnsupportedEncodingException
117      */

118     public LineInput(InputStream JavaDoc in, int bufferSize, String JavaDoc encoding)
119         throws UnsupportedEncodingException JavaDoc
120     {
121         super(in);
122         _mark=-1;
123         if (bufferSize==0)
124             bufferSize=2048;
125         _buf=ByteArrayPool.getByteArray(bufferSize);
126         _byteBuffer=new ByteBuffer(_buf);
127         _lineBuffer=new LineBuffer(bufferSize);
128         _reader=new InputStreamReader JavaDoc(_byteBuffer,encoding);
129         _encoding=encoding;
130     }
131     
132     /* ------------------------------------------------------------ */
133     public InputStream JavaDoc getInputStream()
134     {
135         return in;
136     }
137     
138     /* ------------------------------------------------------------ */
139     /** Set the byte limit.
140      * If set, only this number of bytes are read before EOF.
141      * @param bytes Limit number of bytes, or -1 for no limit.
142      */

143     public void setByteLimit(int bytes)
144     {
145         _byteLimit=bytes;
146         
147         if (bytes>=0)
148         {
149             _newByteLimit=true;
150             _byteLimit-=_contents-_pos;
151             if (_byteLimit<0)
152             {
153                 _avail+=_byteLimit;
154                 _byteLimit=0;
155             }
156         }
157         else
158         {
159             _newByteLimit=false;
160             _avail=_contents;
161             _eof=false;
162         }
163     }
164     
165     
166     /* ------------------------------------------------------------ */
167     /** Get the byte limit.
168      * @return Number of bytes until EOF is returned or -1 for no limit.
169      */

170     public int getByteLimit()
171     {
172         if (_byteLimit<0)
173             return _byteLimit;
174         
175         return _byteLimit+_avail-_pos;
176     }
177     
178     /* ------------------------------------------------------------ */
179     /** Read a line ended by CR, LF or CRLF.
180      * The default or supplied encoding is used to convert bytes to
181      * characters.
182      * @return The line as a String or null for EOF.
183      * @exception IOException
184      */

185     public synchronized String JavaDoc readLine()
186         throws IOException JavaDoc
187     {
188         int len=fillLine(_buf.length);
189         
190         if (len<0)
191             return null;
192
193         String JavaDoc s=null;
194         if (_encoding==null)
195             s=new String JavaDoc(_buf,_mark,len);
196         else
197         {
198             try
199             {
200                 s=new String JavaDoc(_buf,_mark,len,_encoding);
201             }
202             catch(UnsupportedEncodingException JavaDoc e)
203             {
204                 log.warn(LogSupport.EXCEPTION,e);
205             }
206         }
207         _mark=-1;
208
209         return s;
210     }
211     
212     /* ------------------------------------------------------------ */
213     /** Read a line ended by CR, LF or CRLF.
214      * The default or supplied encoding is used to convert bytes to
215      * characters.
216      * @param c Character buffer to place the line into.
217      * @param off Offset into the buffer.
218      * @param len Maximum length of line.
219      * @return The length of the line or -1 for EOF.
220      * @exception IOException
221      */

222     public int readLine(char[] c,int off,int len)
223         throws IOException JavaDoc
224     {
225         int blen=fillLine(len);
226
227         if (blen<0)
228             return -1;
229         if (blen==0)
230             return 0;
231         
232         _byteBuffer.setStream(_mark,blen);
233         
234         int read=0;
235         while(read<len && _reader.ready())
236         {
237             int r = _reader.read(c,off+read,len-read);
238             if (r<=0)
239                 break;
240             read+=r;
241         }
242         
243         _mark=-1;
244
245         return read;
246     }
247     
248     /* ------------------------------------------------------------ */
249     /** Read a line ended by CR, LF or CRLF.
250      * @param b Byte array to place the line into.
251      * @param off Offset into the buffer.
252      * @param len Maximum length of line.
253      * @return The length of the line or -1 for EOF.
254      * @exception IOException
255      */

256     public int readLine(byte[] b,int off,int len)
257         throws IOException JavaDoc
258     {
259         len=fillLine(len);
260
261         if (len<0)
262             return -1;
263         if (len==0)
264             return 0;
265         
266         System.arraycopy(_buf,_mark, b, off, len);
267         _mark=-1;
268
269         return len;
270     }
271
272     
273     /* ------------------------------------------------------------ */
274     /** Read a Line ended by CR, LF or CRLF.
275      * Read a line into a shared LineBuffer instance. The LineBuffer is
276      * resused between calls and should not be held by the caller.
277      * The default or supplied encoding is used to convert bytes to
278      * characters.
279      * @return LineBuffer instance or null for EOF.
280      * @exception IOException
281      */

282     public LineBuffer readLineBuffer()
283         throws IOException JavaDoc
284     {
285         return readLineBuffer(_buf.length);
286     }
287     
288     /* ------------------------------------------------------------ */
289     /** Read a Line ended by CR, LF or CRLF.
290      * Read a line into a shared LineBuffer instance. The LineBuffer is
291      * resused between calls and should not be held by the caller.
292      * The default or supplied encoding is used to convert bytes to
293      * characters.
294      * @param len Maximum length of a line, or 0 for default
295      * @return LineBuffer instance or null for EOF.
296      * @exception IOException
297      */

298     public LineBuffer readLineBuffer(int len)
299         throws IOException JavaDoc
300     {
301         len=fillLine(len>0?len:_buf.length);
302
303         if (len<0)
304             return null;
305         
306         if (len==0)
307         {
308             _lineBuffer.size=0;
309             return _lineBuffer;
310         }
311
312         _byteBuffer.setStream(_mark,len);
313         
314         _lineBuffer.size=0;
315         int read=0;
316         while(read<len && _reader.ready())
317         {
318             int r = _reader.read(_lineBuffer.buffer,
319                                  read,
320                                  len-read);
321             if (r<=0)
322                 break;
323             read+=r;
324         }
325         _lineBuffer.size=read;
326         _mark=-1;
327
328         return _lineBuffer;
329     }
330     
331     /* ------------------------------------------------------------ */
332     public synchronized int read() throws IOException JavaDoc
333     {
334         int b;
335         if (_pos >=_avail)
336             fill();
337         if (_pos >=_avail)
338             b=-1;
339         else
340             b=_buf[_pos++]&255;
341         
342         return b;
343     }
344  
345  
346     /* ------------------------------------------------------------ */
347     public synchronized int read(byte b[], int off, int len) throws IOException JavaDoc
348     {
349         int avail=_avail-_pos;
350         if (avail <=0)
351         {
352             fill();
353             avail=_avail-_pos;
354         }
355
356         if (avail <=0)
357             len=-1;
358         else
359         {
360             len=(avail < len) ? avail : len;
361             System.arraycopy(_buf,_pos,b,off,len);
362             _pos +=len;
363         }
364         
365         return len;
366     }
367     
368     /* ------------------------------------------------------------ */
369     public long skip(long n) throws IOException JavaDoc
370     {
371         int avail=_avail-_pos;
372         if (avail <=0)
373         {
374             fill();
375             avail=_avail-_pos;
376         }
377
378         if (avail <=0)
379             n=0;
380         else
381         {
382             n=(avail < n) ? avail : n;
383             _pos +=n;
384         }
385         
386         return n;
387     }
388
389
390     /* ------------------------------------------------------------ */
391     public synchronized int available()
392         throws IOException JavaDoc
393     {
394         int in_stream=in.available();
395         if (_byteLimit>=0 && in_stream>_byteLimit)
396             in_stream=_byteLimit;
397         
398         return _avail - _pos + in_stream;
399     }
400
401     /* ------------------------------------------------------------ */
402     public synchronized void mark(int limit)
403         throws IllegalArgumentException JavaDoc
404     {
405         if (limit>_buf.length)
406         {
407             byte[] new_buf=new byte[limit];
408             System.arraycopy(_buf,_pos,new_buf,_pos,_avail-_pos);
409             _buf=new_buf;
410             if (_byteBuffer!=null)
411                 _byteBuffer.setBuffer(_buf);
412         }
413         _mark=_pos;
414     }
415
416     /* ------------------------------------------------------------ */
417     public synchronized void reset()
418         throws IOException JavaDoc
419     {
420         if (_mark < 0)
421             throw new IOException JavaDoc("Resetting to invalid mark");
422         _pos=_mark;
423         _mark=-1;
424     }
425
426     /* ------------------------------------------------------------ */
427     public boolean markSupported()
428     {
429         return true;
430     }
431     
432     /* ------------------------------------------------------------ */
433     private void fill()
434         throws IOException JavaDoc
435     {
436         // if the mark is in the middle of the buffer
437
if (_mark > 0)
438         {
439             // moved saved bytes to start of buffer
440
int saved=_contents - _mark;
441             System.arraycopy(_buf, _mark, _buf, 0, saved);
442             _pos-=_mark;
443             _avail-=_mark;
444             _contents=saved;
445             _mark=0;
446         }
447         else if (_mark<0 && _pos>0)
448         {
449             // move remaining bytes to start of buffer
450
int saved=_contents-_pos;
451             System.arraycopy(_buf,_pos, _buf, 0, saved);
452             _avail-=_pos;
453             _contents=saved;
454             _pos=0;
455         }
456         else if (_mark==0 && _pos>0 && _contents==_buf.length)
457         {
458             // Discard the mark as we need the space.
459
_mark=-1;
460             fill();
461             return;
462         }
463
464         // Get ready to top up the buffer
465
int n=0;
466         _eof=false;
467
468         // Handle byte limited EOF
469
if (_byteLimit==0)
470             _eof=true;
471         // else loop until something is read.
472
else while (!_eof && n==0 && _buf.length>_contents)
473         {
474             // try to read as much as will fit.
475
int space=_buf.length-_contents;
476
477             n=in.read(_buf,_contents,space);
478
479             if (n<=0)
480             {
481                 // If no bytes - we could be NBIO, so we want to avoid
482
// a busy loop.
483
if (n==0)
484                 {
485                     // Yield to give a chance for some bytes to turn up
486
Thread.yield();
487
488                     // Do a byte read as that is blocking
489
int b = in.read();
490                     if (b>=0)
491                     {
492                         n=1;
493                         _buf[_contents++]=(byte)b;
494                     }
495                     else
496                         _eof=true;
497                 }
498                 else
499                     _eof=true;
500             }
501             else
502                 _contents+=n;
503             _avail=_contents;
504
505             // If we have a byte limit
506
if (_byteLimit>0)
507             {
508                 // adjust the bytes available
509
if (_contents-_pos >=_byteLimit)
510                     _avail=_byteLimit+_pos;
511                 
512                 if (n>_byteLimit)
513                     _byteLimit=0;
514                 else if (n>=0)
515                     _byteLimit-=n;
516                 else if (n==-1)
517                     throw new IOException JavaDoc("Premature EOF");
518             }
519         }
520         
521         // If we have some characters and the last read was a CR and
522
// the first char is a LF, skip it
523
if (_avail-_pos>0 && _lastCr && _buf[_pos]==LF)
524         {
525             _seenCrLf=true;
526             _pos++;
527             if (_mark>=0)
528                 _mark++;
529             _lastCr=false;
530
531             // If the byte limit has just been imposed, dont count
532
// LF as content.
533
if(_byteLimit>=0 && _newByteLimit)
534             {
535                 if (_avail<_contents)
536                     _avail++;
537                 else
538                     _byteLimit++;
539             }
540             // If we ate all that ws filled, fill some more
541
if (_pos==_avail)
542                 fill();
543         }
544         _newByteLimit=false;
545     }
546
547     
548     /* ------------------------------------------------------------ */
549     private int fillLine(int maxLen)
550         throws IOException JavaDoc
551     {
552         _mark=_pos;
553         
554         if (_pos>=_avail)
555             fill();
556         if (_pos>=_avail)
557             return -1;
558         
559         byte b;
560         boolean cr=_lastCr;
561         boolean lf=false;
562         _lastCr=false;
563         int len=0;
564         
565     LineLoop:
566         while (_pos<=_avail)
567         {
568             // if we have gone past the end of the buffer
569
while (_pos==_avail)
570             {
571                 // If EOF or no more space in the buffer,
572
// return a line.
573
if (_eof || (_mark==0 && _contents==_buf.length))
574                 {
575                     _lastCr=!_eof && _buf[_avail-1]==CR;
576                     
577                     cr=true;
578                     lf=true;
579                     break LineLoop;
580                 }
581                 
582                 // If we have a CR and no more characters are available
583
if (cr && in.available()==0 && !_seenCrLf)
584                 {
585                     _lastCr=true;
586                     cr=true;
587                     lf=true;
588                     break LineLoop;
589                 }
590                 else
591                 {
592                     // Else just wait for more...
593
_pos=_mark;
594                     fill();
595                     _pos=len;
596                     cr=false;
597                 }
598             }
599
600             // Get the byte
601
b=_buf[_pos++];
602             
603             switch(b)
604             {
605               case LF:
606                   if (cr) _seenCrLf=true;
607                   lf=true;
608                   break LineLoop;
609                 
610               case CR:
611                   if (cr)
612                   {
613                       // Double CR
614
if (_pos>1)
615                       {
616                           _pos--;
617                           break LineLoop;
618                       }
619                   }
620                   cr=true;
621                   break;
622                 
623               default:
624                   if(cr)
625                   {
626                       if (_pos==1)
627                           cr=false;
628                       else
629                       {
630                           _pos--;
631                           break LineLoop;
632                       }
633                   }
634                   
635                   len++;
636                   if (len==maxLen)
637                   {
638                       // look for EOL
639
if (_mark!=0 && _pos+2>=_avail && _avail<_buf.length)
640                           fill();
641                           
642                       if (_pos<_avail && _buf[_pos]==CR)
643                       {
644                           cr=true;
645                           _pos++;
646                       }
647                       if (_pos<_avail && _buf[_pos]==LF)
648                       {
649                           lf=true;
650                           _pos++;
651                       }
652                       
653                       if (!cr && !lf)
654                       {
655                           // fake EOL
656
lf=true;
657                           cr=true;
658                       }
659                       break LineLoop;
660                   }
661                   
662                   break;
663             }
664         }
665         
666         if (!cr && !lf && len==0)
667             len=-1;
668         
669         return len;
670     }
671
672     /* ------------------------------------------------------------ */
673     private static class ByteBuffer extends ByteArrayInputStream JavaDoc
674     {
675         ByteBuffer(byte[] buffer)
676         {
677             super(buffer);
678         }
679         
680         void setBuffer(byte[] buffer)
681         {
682             buf=buffer;
683         }
684         
685         void setStream(int offset,int length)
686         {
687             pos=offset;
688             count=offset+length;
689             mark=-1;
690         }
691     }
692     
693     /* ------------------------------------------------------------ */
694     /** Reusable LineBuffer.
695      * Externalized LineBuffer for fast line parsing.
696      */

697     public static class LineBuffer
698     {
699         public char[] buffer;
700         public int size;
701         public LineBuffer(int maxLineLength)
702         {buffer=new char[maxLineLength];}
703
704         public String JavaDoc toString(){return new String JavaDoc(buffer,0,size);}
705     }
706
707     /* ------------------------------------------------------------ */
708     public void destroy()
709     {
710         ByteArrayPool.returnByteArray(_buf);
711         _byteBuffer=null;
712         _reader=null;
713         _lineBuffer=null;
714         _encoding=null;
715     }
716
717     
718 }
719
720
Popular Tags