1   // ========================================================================
2   // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.io.nio;
16  
17  import java.io.File;
18  import java.io.FileInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.nio.ByteBuffer;
23  import java.nio.channels.Channels;
24  import java.nio.channels.FileChannel;
25  import java.nio.channels.ReadableByteChannel;
26  import java.nio.channels.WritableByteChannel;
27  
28  import org.mortbay.io.AbstractBuffer;
29  import org.mortbay.io.Buffer;
30  
31  /* ------------------------------------------------------------------------------- */
32  /** 
33   * 
34   * @author gregw
35   */
36  public class NIOBuffer extends AbstractBuffer
37  {
38    	public final static boolean 
39    		DIRECT=true,
40    		INDIRECT=false;
41    	
42      protected ByteBuffer _buf;
43      private ReadableByteChannel _in;
44      private InputStream _inStream;
45      private WritableByteChannel _out;
46      private OutputStream _outStream;
47  
48      public NIOBuffer(int size, boolean direct)
49      {
50          super(READWRITE,NON_VOLATILE);
51          _buf = direct
52          	?ByteBuffer.allocateDirect(size)
53          	:ByteBuffer.allocate(size);
54          _buf.position(0);
55          _buf.limit(_buf.capacity());
56      }
57      
58      /**
59       * @param file
60       */
61      public NIOBuffer(File file) throws IOException
62      {
63          super(READONLY,NON_VOLATILE);
64          FileInputStream fis = new FileInputStream(file);
65          FileChannel fc = fis.getChannel();
66          _buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
67          setGetIndex(0);
68          setPutIndex((int)file.length());
69          _access=IMMUTABLE;
70      }
71  
72      public byte[] array()
73      {
74          if (!_buf.hasArray())
75              return null;
76          return _buf.array();
77      }
78      
79      public int capacity()
80      {
81          return _buf.capacity();
82      }
83  
84      public byte peek(int position)
85      {
86          return _buf.get(position);
87      }
88  
89      public int peek(int index, byte[] b, int offset, int length)
90      {
91          int l = length;
92          if (index+l > capacity())
93          {
94              l=capacity()-index;
95              if (l==0)
96                  return -1;
97          }
98          
99          if (l < 0) 
100             return -1;
101         try
102         {
103             _buf.position(index);
104             _buf.get(b,offset,l);
105         }
106         finally
107         {
108             _buf.position(0);
109         }
110         
111         return l;
112     }
113 
114     public void poke(int index, byte b)
115     {
116         if (isReadOnly()) throw new IllegalStateException(__READONLY);
117         if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
118         if (index > capacity())
119                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
120         _buf.put(index, b);
121     }
122 
123     public int poke(int index, Buffer src)
124     {
125         if (isReadOnly()) throw new IllegalStateException(__READONLY);
126 
127         byte[] array=src.array();
128         if (array!=null)
129         {
130             int length = poke(index,array,src.getIndex(),src.length());
131             return length;
132         }
133         else
134         {
135             Buffer src_buf=src.buffer();
136             if (src_buf instanceof NIOBuffer)
137             {
138                 ByteBuffer src_bytebuf = ((NIOBuffer)src_buf)._buf;
139                 if (src_bytebuf==_buf)
140                     src_bytebuf=_buf.duplicate();
141                 try
142                 {   
143                     _buf.position(index);
144                     int space = _buf.remaining();
145 
146                     int length=src.length();
147                     if (length>space)    
148                         length=space;
149                     
150                     src_bytebuf.position(src.getIndex());
151                     src_bytebuf.limit(src.getIndex()+length);
152                     
153                     _buf.put(src_bytebuf);
154                     return length;
155                 }
156                 finally
157                 {
158                     _buf.position(0);
159                     src_bytebuf.limit(src_bytebuf.capacity());
160                     src_bytebuf.position(0);
161                 }
162             }
163             else
164                 return super.poke(index,src);
165         }
166     }
167     
168     public int poke(int index, byte[] b, int offset, int length)
169     {
170         if (isReadOnly()) throw new IllegalStateException(__READONLY);
171 
172         if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
173 
174         if (index + length > capacity())
175         {
176             length=capacity()-index;
177             if (length<0)
178                 throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
179         }
180 
181         try
182         {
183             _buf.position(index);
184             
185             int space=_buf.remaining();
186             
187             if (length>space)
188                 length=space;
189             if (length>0)
190                 _buf.put(b,offset,length);
191             return length;
192         }
193         finally
194         {
195             _buf.position(0);
196         }
197     }
198     
199     public ByteBuffer getByteBuffer()
200     {
201         return _buf;
202     }
203     
204     
205     public void setByteBuffer(ByteBuffer buf)
206     {
207         this._buf = buf;
208     }
209 
210 
211     /* ------------------------------------------------------------ */
212     public int readFrom(InputStream in, int max) throws IOException
213     {
214         if (_in==null || !_in.isOpen() || in!=_inStream)
215         {
216             _in=Channels.newChannel(in);
217             _inStream=in;
218         }
219 
220         if (max<0 || max>space())
221             max=space();
222         int p = putIndex();
223         
224         try
225         {
226             int len=0, total=0, available=max;
227             int loop=0;
228             while (total<max) 
229             {
230                 _buf.position(p);
231                 _buf.limit(p+available);
232                 len=_in.read(_buf);
233                 if (len<0)
234                 {
235                     _in=null;
236                     _inStream=in;
237                     break;
238                 }
239                 else if (len>0)
240                 {
241                     p += len;
242                     total += len;
243                     available -= len;
244                     setPutIndex(p);
245                     loop=0;
246                 }
247                 else if (loop++>1)
248                     break;
249                 if (in.available()<=0)
250                     break;
251             }
252             if (len<0 && total==0)
253                 return -1;
254             return total;
255             
256         }
257         catch(IOException e)
258         {
259             _in=null;
260             _inStream=in;
261             throw e;
262         }
263         finally
264         {
265             if (_in!=null && !_in.isOpen())
266             {
267                 _in=null;
268                 _inStream=in;
269             }
270             _buf.position(0);
271             _buf.limit(_buf.capacity());
272         }
273     }
274 
275     /* ------------------------------------------------------------ */
276     public void writeTo(OutputStream out) throws IOException
277     {
278         if (_out==null || !_out.isOpen() || _out!=_outStream)
279         {
280             _out=Channels.newChannel(out);
281             _outStream=out;
282         }
283 
284         try
285         {
286             int loop=0;
287             while(hasContent() && _out.isOpen())
288             {
289                 _buf.position(getIndex());
290                 _buf.limit(putIndex());
291                 int len=_out.write(_buf);
292                 if (len<0)
293                     break;
294                 else if (len>0)
295                 {
296                     skip(len);
297                     loop=0;
298                 }
299                 else if (loop++>1)
300                     break;
301             }
302         }
303         catch(IOException e)
304         {
305             _out=null;
306             _outStream=null;
307             throw e;
308         }
309         finally
310         {
311             if (_out!=null && !_out.isOpen())
312             {
313                 _out=null;
314                 _outStream=null;
315             }
316             _buf.position(0);
317             _buf.limit(_buf.capacity());
318         }
319         
320     }
321     
322     
323 }