1 28 package org.jruby.ext; 29 30 import java.io.IOException ; 31 32 import org.jruby.Ruby; 33 import org.jruby.RubyClass; 34 import org.jruby.RubyObject; 35 import org.jruby.RubyProc; 36 37 import org.jruby.runtime.Arity; 38 import org.jruby.runtime.Block; 39 import org.jruby.runtime.CallBlock; 40 import org.jruby.runtime.BlockCallback; 41 import org.jruby.runtime.ThreadContext; 42 import org.jruby.runtime.Visibility; 43 import org.jruby.runtime.load.Library; 44 import org.jruby.runtime.builtin.IRubyObject; 45 46 import org.jruby.internal.runtime.methods.MultiStub; 47 import org.jruby.internal.runtime.methods.MultiStubMethod; 48 49 52 public class Generator { 53 public static class Service implements Library { 54 public void load(final Ruby runtime) throws IOException { 55 createGenerator(runtime); 56 } 57 } 58 59 public static void createGenerator(Ruby runtime) throws IOException { 60 RubyClass cGen = runtime.defineClass("Generator",runtime.getObject(), runtime.getObject().getAllocator()); 61 cGen.includeModule(runtime.getModule("Enumerable")); 62 63 GenStub0 gstub = new GenStub0(); 64 gstub.gen_new = new MultiStubMethod(gstub,0,cGen,Arity.optional(),Visibility.PUBLIC); 65 gstub.gen_initialize = new MultiStubMethod(gstub,1,cGen,Arity.optional(),Visibility.PUBLIC); 66 gstub.gen_yield = new MultiStubMethod(gstub,2,cGen,Arity.singleArgument(),Visibility.PUBLIC); 67 gstub.gen_end_p = new MultiStubMethod(gstub,3,cGen,Arity.noArguments(),Visibility.PUBLIC); 68 gstub.gen_next_p = new MultiStubMethod(gstub,4,cGen,Arity.noArguments(),Visibility.PUBLIC); 69 gstub.gen_index = new MultiStubMethod(gstub,5,cGen,Arity.noArguments(),Visibility.PUBLIC); 70 gstub.gen_next = new MultiStubMethod(gstub,6,cGen,Arity.noArguments(),Visibility.PUBLIC); 71 gstub.gen_current = new MultiStubMethod(gstub,7,cGen,Arity.noArguments(),Visibility.PUBLIC); 72 gstub.gen_rewind = new MultiStubMethod(gstub,8,cGen,Arity.noArguments(),Visibility.PUBLIC); 73 gstub.gen_each = new MultiStubMethod(gstub,9,cGen,Arity.noArguments(),Visibility.PUBLIC); 74 75 cGen.getMetaClass().addMethod("new",gstub.gen_new); 76 cGen.addMethod("initialize",gstub.gen_initialize); 77 cGen.addMethod("yield",gstub.gen_yield); 78 cGen.addMethod("end?",gstub.gen_end_p); 79 cGen.addMethod("next?",gstub.gen_next_p); 80 cGen.addMethod("index",gstub.gen_index); 81 cGen.defineAlias("pos","index"); 82 cGen.addMethod("next",gstub.gen_next); 83 cGen.addMethod("current",gstub.gen_current); 84 cGen.addMethod("rewind",gstub.gen_rewind); 85 cGen.addMethod("each",gstub.gen_each); 86 } 87 88 static class GeneratorData implements Runnable { 89 private IRubyObject gen; 90 private Object mutex = new Object (); 91 92 private IRubyObject enm; 93 private RubyProc proc; 94 95 private Thread t; 96 private boolean end; 97 private IterBlockCallback ibc; 98 99 public GeneratorData(IRubyObject gen) { 100 this.gen = gen; 101 } 102 103 public void setEnum(IRubyObject enm) { 104 this.proc = null; 105 this.enm = enm; 106 start(); 107 } 108 109 public void setProc(RubyProc proc) { 110 this.proc = proc; 111 this.enm = null; 112 start(); 113 } 114 115 public void start() { 116 end = false; 117 ibc = new IterBlockCallback(); 118 t = new Thread (this); 119 t.setDaemon(true); 120 t.start(); 121 generate(); 122 } 123 124 public boolean isEnd() { 125 return end; 126 } 127 128 private boolean available = false; 129 130 public void doWait() { 131 available = true; 132 if(proc != null) { 133 boolean inter = true; 134 synchronized(mutex) { 135 mutex.notifyAll(); 136 while(inter) { 137 try { 138 mutex.wait(); 139 inter = false; 140 } catch(InterruptedException e) { 141 } 142 } 143 } 144 } 145 } 146 147 public void generate() { 148 if(proc == null) { 149 boolean inter = true; 150 synchronized(mutex) { 151 while(!ibc.haveValue() && !end) { 152 mutex.notifyAll(); 153 inter = true; 154 while(inter) { 155 try { 156 mutex.wait(); 157 inter = false; 158 } catch(InterruptedException e) { 159 } 160 } 161 } 162 if(!end && proc == null) { 163 gen.callMethod(gen.getRuntime().getCurrentContext(),"yield",ibc.pop()); 164 } 165 } 166 } else { 167 synchronized(mutex) { 168 while(!available && !end) { 169 boolean inter = true; 170 mutex.notifyAll(); 171 while(inter) { 172 try { 173 mutex.wait(20); 174 inter = false; 175 } catch(InterruptedException e) { 176 } 177 } 178 } 179 available = false; 180 } 181 } 182 183 } 184 185 private class IterBlockCallback implements BlockCallback { 186 private IRubyObject obj; 187 public IRubyObject call(ThreadContext context, IRubyObject[] iargs, IRubyObject iself, Block block) { 188 boolean inter = true; 189 synchronized(mutex) { 190 mutex.notifyAll(); 191 while(inter) { 192 try { 193 mutex.wait(); 194 inter = false; 195 } catch(InterruptedException e) { 196 } 197 } 198 if(iargs.length > 1) { 199 obj = gen.getRuntime().newArrayNoCopy(iargs); 200 } else { 201 obj = iargs[0]; 202 } 203 mutex.notifyAll(); 204 return gen.getRuntime().getNil(); 205 } 206 } 207 public boolean haveValue() { 208 return obj != null; 209 } 210 public IRubyObject pop() { 211 IRubyObject a = obj; 212 obj = null; 213 return a; 214 } 215 } 216 217 public void run() { 218 if(enm != null) { 219 ThreadContext context = gen.getRuntime().getCurrentContext(); 220 enm.callMethod(context, "each", new CallBlock(enm,enm.getMetaClass().getRealClass(),Arity.noArguments(),ibc,context)); 221 } else { 222 proc.call(new IRubyObject[]{gen}); 223 } 224 end = true; 225 } 226 } 227 228 public static class GenStub0 implements MultiStub { 229 public MultiStubMethod gen_new; 230 public MultiStubMethod gen_initialize; 231 public MultiStubMethod gen_yield; 232 public MultiStubMethod gen_end_p; 233 public MultiStubMethod gen_next_p; 234 public MultiStubMethod gen_index; 235 public MultiStubMethod gen_next; 236 public MultiStubMethod gen_current; 237 public MultiStubMethod gen_rewind; 238 public MultiStubMethod gen_each; 239 240 public IRubyObject method0(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 241 IRubyObject result = new RubyObject(self.getRuntime(),(RubyClass)self); 243 result.dataWrapStruct(new GeneratorData(result)); 244 result.callInit(args, block); 245 return result; 246 } 247 248 public IRubyObject method1(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 249 GeneratorData d = (GeneratorData)self.dataGetStruct(); 251 252 self.setInstanceVariable("@queue",self.getRuntime().newArray()); 253 self.setInstanceVariable("@index",self.getRuntime().newFixnum(0)); 254 255 if(self.checkArgumentCount(args,0,1) == 1) { 256 d.setEnum(args[0]); 257 } else { 258 d.setProc(self.getRuntime().newProc(false, Block.NULL_BLOCK)); 259 } 260 return self; 261 } 262 263 public IRubyObject method2(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 264 self.getInstanceVariable("@queue").callMethod(context,"<<",args[0]); 266 GeneratorData d = (GeneratorData)self.dataGetStruct(); 267 d.doWait(); 268 return self; 269 } 270 271 public IRubyObject method3(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 272 GeneratorData d = (GeneratorData)self.dataGetStruct(); 274 return d.isEnd() ? self.getRuntime().getTrue() : self.getRuntime().getFalse(); 275 } 276 277 public IRubyObject method4(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 278 GeneratorData d = (GeneratorData)self.dataGetStruct(); 280 return !d.isEnd() ? self.getRuntime().getTrue() : self.getRuntime().getFalse(); 281 } 282 283 public IRubyObject method5(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 284 return self.getInstanceVariable("@index"); 286 } 287 288 public IRubyObject method6(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 289 GeneratorData d = (GeneratorData)self.dataGetStruct(); 291 if(d.isEnd()) { 292 throw self.getRuntime().newEOFError(); 293 } 294 d.generate(); 295 self.setInstanceVariable("@index",self.getInstanceVariable("@index").callMethod(context,"+",self.getRuntime().newFixnum(1))); 296 return self.getInstanceVariable("@queue").callMethod(context,"shift"); 297 } 298 299 public IRubyObject method7(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 300 if(self.getInstanceVariable("@queue").callMethod(context,"empty?").isTrue()) { 302 throw self.getRuntime().newEOFError(); 303 } 304 return self.getInstanceVariable("@queue").callMethod(context,"first"); 305 } 306 307 public IRubyObject method8(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 308 if(self.getInstanceVariable("@index").callMethod(context,"nonzero?").isTrue()) { 310 GeneratorData d = (GeneratorData)self.dataGetStruct(); 311 312 self.setInstanceVariable("@queue",self.getRuntime().newArray()); 313 self.setInstanceVariable("@index",self.getRuntime().newFixnum(0)); 314 315 d.start(); 316 } 317 318 return self; 319 } 320 321 public IRubyObject method9(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { 322 self.callMethod(context,"rewind"); 324 while(self.callMethod(context,"next?").isTrue()) { 325 context.yield(self.callMethod(context,"next"), block); 326 } 327 return self; 328 } 329 } 330 } | Popular Tags |