1 32 package org.jruby.runtime.builtin.meta; 33 34 import java.io.IOException ; 35 import java.util.ArrayList ; 36 import java.util.Iterator ; 37 import java.util.List ; 38 import java.nio.channels.SelectableChannel ; 39 import java.nio.channels.Selector ; 40 import java.nio.channels.SelectionKey ; 41 import java.nio.channels.Channel ; 42 import java.nio.channels.Pipe ; 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 name, RubyClass superClass, ObjectAllocator allocator, SinglyLinkedList parentCRef) { 66 this(name, RubyIO.class, superClass, allocator, parentCRef); 67 } 68 69 public IOMetaClass(String name, Class clazz, RubyClass superClass, ObjectAllocator allocator) { 70 super(name, clazz, superClass, allocator); 71 } 72 73 public IOMetaClass(String name, Class 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 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 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 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 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 selector, IRubyObject obj, int ops) throws IOException { 188 RubyIO ioObj; 189 190 if(!(obj instanceof RubyIO)) { 191 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 channel = ioObj.getChannel(); 201 if(channel == null || !(channel instanceof SelectableChannel )) { 202 return; 203 } 204 205 ((SelectableChannel ) channel).configureBlocking(false); 206 int real_ops = ((SelectableChannel ) channel).validOps() & ops; 207 SelectionKey key = ((SelectableChannel ) channel).keyFor(selector); 208 209 if(key == null) { 210 ((SelectableChannel ) 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 selector = Selector.open(); 225 if (!args[0].isNil()) { 226 atLeastOneDescriptor = true; 227 228 for (Iterator 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 for (Iterator 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 } 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 r = new ArrayList (); 271 List w = new ArrayList (); 272 List e = new ArrayList (); 273 for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) { 274 SelectionKey key = (SelectionKey ) 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 for (Iterator i = selector.keys().iterator(); i.hasNext(); ) { 286 SelectionKey key = (SelectionKey ) i.next(); 287 SelectableChannel 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 ret = new ArrayList (); 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 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 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 command = cmdObj.toString(); 356 ThreadContext tc = runtime.getCurrentContext(); 357 358 367 try { 368 Process process; 370 String shell = System.getProperty("jruby.shell"); 371 if (shell != null) { 372 String shellSwitch = "-c"; 373 if (!shell.endsWith("sh")) { 374 shellSwitch = "/c"; 375 } 376 process = Runtime.getRuntime().exec(new String [] { 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 e) { 394 throw runtime.newIOErrorFromException(e); 395 } catch (InterruptedException e) { 396 throw runtime.newThreadError("unexpected interrupt"); 397 } 398 } 399 400 public IRubyObject pipe() throws Exception { 402 Ruby runtime = getRuntime(); 403 Pipe 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 |