KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > runtime > builtin > meta > IOMetaClass


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
15  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
16  * Copyright (C) 2005 Thomas E Enebo <enebo@acm.org>
17  * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
18  * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
19  *
20  * Alternatively, the contents of this file may be used under the terms of
21  * either of the GNU General Public License Version 2 or later (the "GPL"),
22  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23  * in which case the provisions of the GPL or the LGPL are applicable instead
24  * of those above. If you wish to allow use of your version of this file only
25  * under the terms of either the GPL or the LGPL, and not to allow others to
26  * use your version of this file under the terms of the CPL, indicate your
27  * decision by deleting the provisions above and replace them with the notice
28  * and other provisions required by the GPL or the LGPL. If you do not delete
29  * the provisions above, a recipient may use your version of this file under
30  * the terms of any one of the CPL, the GPL or the LGPL.
31  ***** END LICENSE BLOCK *****/

32 package org.jruby.runtime.builtin.meta;
33
34 import java.io.IOException JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.nio.channels.SelectableChannel JavaDoc;
39 import java.nio.channels.Selector JavaDoc;
40 import java.nio.channels.SelectionKey JavaDoc;
41 import java.nio.channels.Channel JavaDoc;
42 import java.nio.channels.Pipe JavaDoc;
43
44 import org.jruby.Ruby;
45 import org.jruby.RubyArray;
46 import org.jruby.RubyFixnum;
47 import org.jruby.RubyFloat;
48 import org.jruby.RubyClass;
49 import org.jruby.RubyIO;
50 import org.jruby.RubyKernel;
51 import org.jruby.runtime.Arity;
52 import org.jruby.runtime.Block;
53 import org.jruby.runtime.ObjectAllocator;
54 import org.jruby.runtime.ThreadContext;
55 import org.jruby.runtime.builtin.IRubyObject;
56 import org.jruby.util.IOHandler;
57 import org.jruby.util.collections.SinglyLinkedList;
58
59 public class IOMetaClass extends ObjectMetaClass {
60     
61     public IOMetaClass(Ruby runtime) {
62         this("IO", RubyIO.class, runtime.getObject(), IO_ALLOCATOR);
63     }
64     
65     public IOMetaClass(String JavaDoc name, RubyClass superClass, ObjectAllocator allocator, SinglyLinkedList parentCRef) {
66         this(name, RubyIO.class, superClass, allocator, parentCRef);
67     }
68     
69     public IOMetaClass(String JavaDoc name, Class JavaDoc clazz, RubyClass superClass, ObjectAllocator allocator) {
70         super(name, clazz, superClass, allocator);
71     }
72     
73     public IOMetaClass(String JavaDoc name, Class JavaDoc clazz, RubyClass superClass, ObjectAllocator allocator, SinglyLinkedList parentCRef) {
74         super(name, clazz, superClass, allocator, parentCRef);
75     }
76     
77     protected class IOMeta extends Meta {
78         protected void initializeClass() {
79             includeModule(getRuntime().getModule("Enumerable"));
80             
81             // TODO: Implement tty? and isatty. We have no real capability to
82
// determine this from java, but if we could set tty status, then
83
// we could invoke jruby differently to allow stdin to return true
84
// on this. This would allow things like cgi.rb to work properly.
85

86             defineSingletonMethod("foreach", Arity.optional());
87             defineSingletonMethod("read", Arity.optional());
88             defineSingletonMethod("readlines", Arity.optional());
89             defineSingletonMethod("popen", Arity.optional());
90             defineFastSingletonMethod("select", Arity.optional());
91             defineFastSingletonMethod("pipe", Arity.noArguments());
92             
93             defineFastMethod("<<", Arity.singleArgument(), "addString");
94             defineFastMethod("binmode", Arity.noArguments());
95             defineFastMethod("close", Arity.noArguments());
96             defineFastMethod("closed?", Arity.noArguments(), "closed");
97             defineMethod("each", Arity.optional(), "each_line");
98             defineMethod("each_byte", Arity.noArguments());
99             defineMethod("each_line", Arity.optional());
100             defineFastMethod("eof", Arity.noArguments());
101             defineAlias("eof?", "eof");
102             defineFastMethod("fcntl", Arity.twoArguments());
103             defineFastMethod("fileno", Arity.noArguments());
104             defineFastMethod("flush", Arity.noArguments());
105             defineFastMethod("fsync", Arity.noArguments());
106             defineFastMethod("getc", Arity.noArguments());
107             defineFastMethod("gets", Arity.optional());
108             defineMethod("initialize", Arity.optional());
109             defineFastMethod("initialize_copy", Arity.singleArgument());
110             defineFastMethod("lineno", Arity.noArguments());
111             defineFastMethod("lineno=", Arity.singleArgument(), "lineno_set");
112             defineFastMethod("pid", Arity.noArguments());
113             defineFastMethod("pos", Arity.noArguments());
114             defineFastMethod("pos=", Arity.singleArgument(), "pos_set");
115             defineFastMethod("print", Arity.optional());
116             defineFastMethod("printf", Arity.optional());
117             defineFastMethod("putc", Arity.singleArgument());
118             defineFastMethod("puts", Arity.optional());
119             defineFastMethod("readpartial", Arity.optional());
120             defineFastMethod("read", Arity.optional());
121             defineFastMethod("readchar", Arity.noArguments());
122             defineFastMethod("readline", Arity.optional());
123             defineFastMethod("readlines", Arity.optional());
124             defineFastMethod("reopen", Arity.optional());
125             defineFastMethod("rewind", Arity.noArguments());
126             defineFastMethod("seek", Arity.optional());
127             defineFastMethod("sync", Arity.noArguments());
128             defineFastMethod("sync=", Arity.singleArgument(), "sync_set");
129             defineFastMethod("sysread", Arity.singleArgument());
130             defineFastMethod("syswrite", Arity.singleArgument());
131             defineAlias("tell", "pos");
132             defineAlias("to_i", "fileno");
133             defineFastMethod("to_io", Arity.noArguments());
134             defineFastMethod("ungetc", Arity.singleArgument());
135             defineFastMethod("write", Arity.singleArgument());
136             defineFastMethod("tty?", Arity.noArguments(), "tty");
137             defineAlias("isatty", "tty?");
138             
139             // Constants for seek
140
setConstant("SEEK_SET", getRuntime().newFixnum(IOHandler.SEEK_SET));
141             setConstant("SEEK_CUR", getRuntime().newFixnum(IOHandler.SEEK_CUR));
142             setConstant("SEEK_END", getRuntime().newFixnum(IOHandler.SEEK_END));
143         }
144     };
145     
146     protected Meta getMeta() {
147         return new IOMeta();
148     }
149     
150     public RubyClass newSubClass(String JavaDoc name, SinglyLinkedList parentCRef) {
151         return new IOMetaClass(name, this, IO_ALLOCATOR, parentCRef);
152     }
153     
154     private static ObjectAllocator IO_ALLOCATOR = new ObjectAllocator() {
155         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
156             return new RubyIO(runtime, klass);
157         }
158     };
159     
160     /** rb_io_s_foreach
161      *
162      */

163     public IRubyObject foreach(IRubyObject[] args, Block block) {
164         int count = checkArgumentCount(args, 1, -1);
165         IRubyObject filename = args[0].convertToString();
166         filename.checkSafeString();
167         RubyIO io = (RubyIO) ((FileMetaClass) getRuntime().getClass("File")).open(new IRubyObject[] { filename }, false, block);
168         
169         if (!io.isNil() && io.isOpen()) {
170             try {
171                 IRubyObject[] newArgs = new IRubyObject[count - 1];
172                 System.arraycopy(args, 1, newArgs, 0, count - 1);
173                 
174                 IRubyObject nextLine = io.internalGets(newArgs);
175                 while (!nextLine.isNil()) {
176                     getRuntime().getCurrentContext().yield(nextLine, block);
177                     nextLine = io.internalGets(newArgs);
178                 }
179             } finally {
180                 io.close();
181             }
182         }
183         
184         return getRuntime().getNil();
185     }
186     
187     private static void registerSelect(Selector JavaDoc selector, IRubyObject obj, int ops) throws IOException JavaDoc {
188         RubyIO ioObj;
189         
190         if(!(obj instanceof RubyIO)) {
191             // invoke to_io
192
if(!obj.respondsTo("to_io")) {
193                 return;
194             }
195             ioObj = (RubyIO) obj.callMethod(obj.getRuntime().getCurrentContext(), "to_io");
196         } else {
197             ioObj = (RubyIO) obj;
198         }
199         
200         Channel JavaDoc channel = ioObj.getChannel();
201         if(channel == null || !(channel instanceof SelectableChannel JavaDoc)) {
202             return;
203         }
204         
205         ((SelectableChannel JavaDoc) channel).configureBlocking(false);
206         int real_ops = ((SelectableChannel JavaDoc) channel).validOps() & ops;
207         SelectionKey JavaDoc key = ((SelectableChannel JavaDoc) channel).keyFor(selector);
208         
209         if(key == null) {
210             ((SelectableChannel JavaDoc) channel).register(selector, real_ops, obj);
211         } else {
212             key.interestOps(key.interestOps()|real_ops);
213         }
214     }
215     
216     public IRubyObject select(IRubyObject[] args) {
217         return select_static(getRuntime(), args);
218     }
219     
220     public static IRubyObject select_static(Ruby runtime, IRubyObject[] args) {
221         try {
222             boolean atLeastOneDescriptor = false;
223             
224             Selector JavaDoc selector = Selector.open();
225             if (!args[0].isNil()) {
226                 atLeastOneDescriptor = true;
227                 
228                 // read
229
for (Iterator JavaDoc i = ((RubyArray) args[0]).getList().iterator(); i.hasNext(); ) {
230                     IRubyObject obj = (IRubyObject) i.next();
231                     registerSelect(selector, obj, SelectionKey.OP_READ|SelectionKey.OP_ACCEPT);
232                 }
233             }
234             if (args.length > 1 && !args[1].isNil()) {
235                 atLeastOneDescriptor = true;
236                 // write
237
for (Iterator JavaDoc i = ((RubyArray) args[1]).getList().iterator(); i.hasNext(); ) {
238                     IRubyObject obj = (IRubyObject) i.next();
239                     registerSelect(selector, obj, SelectionKey.OP_WRITE);
240                 }
241             }
242             if (args.length > 2 && !args[2].isNil()) {
243                 atLeastOneDescriptor = true;
244                 // Java's select doesn't do anything about this, so we leave it be.
245
}
246             
247             long timeout = 0;
248             if(args.length > 3 && !args[3].isNil()) {
249                 if (args[3] instanceof RubyFloat) {
250                     timeout = Math.round(((RubyFloat) args[3]).getDoubleValue() * 1000);
251                 } else {
252                     timeout = Math.round(((RubyFixnum) args[3]).getDoubleValue() * 1000);
253                 }
254                 
255                 if (timeout < 0) {
256                     throw runtime.newArgumentError("negative timeout given");
257                 }
258             }
259             
260             if (!atLeastOneDescriptor) {
261                 return runtime.getNil();
262             }
263             
264             if(args.length > 3) {
265                 selector.select(timeout);
266             } else {
267                 selector.select();
268             }
269             
270             List JavaDoc r = new ArrayList JavaDoc();
271             List JavaDoc w = new ArrayList JavaDoc();
272             List JavaDoc e = new ArrayList JavaDoc();
273             for (Iterator JavaDoc i = selector.selectedKeys().iterator(); i.hasNext(); ) {
274                 SelectionKey JavaDoc key = (SelectionKey JavaDoc) i.next();
275                 if ((key.interestOps() & key.readyOps()
276                         & (SelectionKey.OP_READ|SelectionKey.OP_ACCEPT|SelectionKey.OP_CONNECT)) != 0) {
277                     r.add(key.attachment());
278                 }
279                 if ((key.interestOps() & key.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
280                     w.add(key.attachment());
281                 }
282             }
283             
284             // make all sockets blocking as configured again
285
for (Iterator JavaDoc i = selector.keys().iterator(); i.hasNext(); ) {
286                 SelectionKey JavaDoc key = (SelectionKey JavaDoc) i.next();
287                 SelectableChannel JavaDoc channel = key.channel();
288                 synchronized(channel.blockingLock()) {
289                     boolean blocking = ((RubyIO) key.attachment()).getBlocking();
290                     key.cancel();
291                     channel.configureBlocking(blocking);
292                 }
293             }
294             selector.close();
295             
296             if (r.size() == 0 && w.size() == 0 && e.size() == 0) {
297                 return runtime.getNil();
298             }
299             
300             List JavaDoc ret = new ArrayList JavaDoc();
301             
302             ret.add(RubyArray.newArray(runtime, r));
303             ret.add(RubyArray.newArray(runtime, w));
304             ret.add(RubyArray.newArray(runtime, e));
305             
306             return RubyArray.newArray(runtime, ret);
307         } catch(IOException JavaDoc e) {
308             throw runtime.newIOError(e.getMessage());
309         }
310     }
311     
312     public IRubyObject read(IRubyObject[] args, Block block) {
313         checkArgumentCount(args, 1, 3);
314         IRubyObject[] fileArguments = new IRubyObject[] {args[0]};
315         RubyIO file = (RubyIO) RubyKernel.open(this, fileArguments, block);
316         IRubyObject[] readArguments;
317         
318         if (args.length >= 2) {
319             readArguments = new IRubyObject[] {args[1].convertToType("Fixnum", "to_int", true)};
320         } else {
321             readArguments = new IRubyObject[] {};
322         }
323         
324         try {
325             
326             if (args.length == 3) {
327                 file.seek(new IRubyObject[] {args[2].convertToType("Fixnum", "to_int", true)});
328             }
329             
330             return file.read(readArguments);
331         } finally {
332             file.close();
333         }
334     }
335     
336     public RubyArray readlines(IRubyObject[] args, Block block) {
337         int count = checkArgumentCount(args, 1, 2);
338         
339         IRubyObject[] fileArguments = new IRubyObject[] {args[0]};
340         IRubyObject[] separatorArguments = count >= 2 ? new IRubyObject[]{args[1]} : IRubyObject.NULL_ARRAY;
341         RubyIO file = (RubyIO) RubyKernel.open(this, fileArguments, block);
342         try {
343             return file.readlines(separatorArguments);
344         } finally {
345             file.close();
346         }
347     }
348     
349     //XXX Hacked incomplete popen implementation to make
350
public IRubyObject popen(IRubyObject[] args, Block block) {
351         Ruby runtime = getRuntime();
352         checkArgumentCount(args, 1, 2);
353         IRubyObject cmdObj = args[0].convertToString();
354         cmdObj.checkSafeString();
355         String JavaDoc command = cmdObj.toString();
356         ThreadContext tc = runtime.getCurrentContext();
357         
358         /*
359         // only r works so throw error if anything else specified.
360         if (args.length >= 2) {
361             String mode = args[1].convertToString().toString();
362             if (!mode.equals("r")) {
363                 throw runtime.newNotImplementedError("only 'r' currently supported");
364             }
365         }
366          */

367         try {
368             //TODO Unify with runInShell()
369
Process JavaDoc process;
370             String JavaDoc shell = System.getProperty("jruby.shell");
371             if (shell != null) {
372                 String JavaDoc shellSwitch = "-c";
373                 if (!shell.endsWith("sh")) {
374                     shellSwitch = "/c";
375                 }
376                 process = Runtime.getRuntime().exec(new String JavaDoc[] { shell, shellSwitch, command });
377             } else {
378                 process = Runtime.getRuntime().exec(command);
379             }
380             
381             RubyIO io = new RubyIO(runtime, process);
382             
383             if (block.isGiven()) {
384                 try {
385                     tc.yield(io, block);
386                     return runtime.getNil();
387                 } finally {
388                     io.close();
389                     runtime.getGlobalVariables().set("$?", runtime.newFixnum(process.waitFor() * 256));
390                 }
391             }
392             return io;
393         } catch (IOException JavaDoc e) {
394             throw runtime.newIOErrorFromException(e);
395         } catch (InterruptedException JavaDoc e) {
396             throw runtime.newThreadError("unexpected interrupt");
397         }
398     }
399     
400     // NIO based pipe
401
public IRubyObject pipe() throws Exception JavaDoc {
402         Ruby runtime = getRuntime();
403         Pipe JavaDoc pipe = Pipe.open();
404         return runtime.newArrayNoCopy(new IRubyObject[]{
405             new RubyIO(runtime, pipe.source()),
406             new RubyIO(runtime, pipe.sink())
407         });
408     }
409 }
410
Popular Tags