001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2006 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jruby.ext;
028:
029: import java.io.IOException;
030:
031: import org.jruby.Ruby;
032: import org.jruby.RubyClass;
033: import org.jruby.RubyObject;
034: import org.jruby.RubyProc;
035:
036: import org.jruby.runtime.Arity;
037: import org.jruby.runtime.Block;
038: import org.jruby.runtime.CallBlock;
039: import org.jruby.runtime.CallbackFactory;
040: import org.jruby.runtime.BlockCallback;
041: import org.jruby.runtime.ThreadContext;
042: import org.jruby.runtime.load.Library;
043: import org.jruby.runtime.builtin.IRubyObject;
044:
045: import org.jruby.runtime.MethodIndex;
046:
047: /**
048: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
049: */
050: public class Generator {
051: public static class Service implements Library {
052: public void load(final Ruby runtime) throws IOException {
053: createGenerator(runtime);
054: }
055: }
056:
057: public static void createGenerator(Ruby runtime) throws IOException {
058: RubyClass cGen = runtime.defineClass("Generator", runtime
059: .getObject(), runtime.getObject().getAllocator());
060: cGen.includeModule(runtime.getModule("Enumerable"));
061:
062: CallbackFactory callbackFactory = runtime
063: .callbackFactory(Generator.class);
064:
065: cGen.getMetaClass().defineMethod("new",
066: callbackFactory.getOptSingletonMethod("new_instance"));
067: cGen.defineMethod("initialize", callbackFactory
068: .getOptSingletonMethod("initialize"));
069: cGen.defineMethod("yield", callbackFactory.getSingletonMethod(
070: "yield", IRubyObject.class));
071: cGen.defineFastMethod("end?", callbackFactory
072: .getFastSingletonMethod("end_p"));
073: cGen.defineFastMethod("next?", callbackFactory
074: .getFastSingletonMethod("next_p"));
075: cGen.defineFastMethod("index", callbackFactory
076: .getFastSingletonMethod("index"));
077: cGen.defineAlias("pos", "index");
078: cGen.defineMethod("next", callbackFactory
079: .getSingletonMethod("next"));
080: cGen.defineMethod("current", callbackFactory
081: .getSingletonMethod("current"));
082: cGen.defineMethod("rewind", callbackFactory
083: .getSingletonMethod("rewind"));
084: cGen.defineMethod("each", callbackFactory
085: .getSingletonMethod("each"));
086: }
087:
088: static class GeneratorData implements Runnable {
089: private IRubyObject gen;
090: private Object mutex = new Object();
091:
092: private IRubyObject enm;
093: private RubyProc proc;
094:
095: private Thread t;
096: private boolean end;
097: private IterBlockCallback ibc;
098:
099: 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()
164: .getCurrentContext(), "yield", ibc
165: .pop());
166: }
167: }
168: } else {
169: synchronized (mutex) {
170: while (!available && !end) {
171: boolean inter = true;
172: mutex.notifyAll();
173: while (inter) {
174: try {
175: mutex.wait(20);
176: inter = false;
177: } catch (InterruptedException e) {
178: }
179: }
180: }
181: available = false;
182: }
183: }
184:
185: }
186:
187: private class IterBlockCallback implements BlockCallback {
188: private IRubyObject obj;
189:
190: public IRubyObject call(ThreadContext context,
191: IRubyObject[] iargs, Block block) {
192: boolean inter = true;
193: synchronized (mutex) {
194: mutex.notifyAll();
195: while (inter) {
196: try {
197: mutex.wait();
198: inter = false;
199: } catch (InterruptedException e) {
200: }
201: }
202: if (iargs.length > 1) {
203: obj = gen.getRuntime().newArrayNoCopy(iargs);
204: } else {
205: obj = iargs[0];
206: }
207: mutex.notifyAll();
208: return gen.getRuntime().getNil();
209: }
210: }
211:
212: public boolean haveValue() {
213: return obj != null;
214: }
215:
216: public IRubyObject pop() {
217: IRubyObject a = obj;
218: obj = null;
219: return a;
220: }
221: }
222:
223: public void run() {
224: if (enm != null) {
225: ThreadContext context = gen.getRuntime()
226: .getCurrentContext();
227: enm.callMethod(context, "each", new CallBlock(enm, enm
228: .getMetaClass().getRealClass(), Arity
229: .noArguments(), ibc, context));
230: } else {
231: proc.call(new IRubyObject[] { gen });
232: }
233: end = true;
234: }
235: }
236:
237: public static IRubyObject new_instance(IRubyObject self,
238: IRubyObject[] args, Block block) {
239: // Generator#new
240: IRubyObject result = new RubyObject(self.getRuntime(),
241: (RubyClass) self);
242: result.dataWrapStruct(new GeneratorData(result));
243: result.callMethod(self.getRuntime().getCurrentContext(),
244: "initialize", args, block);
245: return result;
246: }
247:
248: public static IRubyObject initialize(IRubyObject self,
249: IRubyObject[] args, Block block) {
250: // Generator#initialize
251: GeneratorData d = (GeneratorData) self.dataGetStruct();
252:
253: self
254: .setInstanceVariable("@queue", self.getRuntime()
255: .newArray());
256: self.setInstanceVariable("@index", self.getRuntime().newFixnum(
257: 0));
258:
259: if (Arity.checkArgumentCount(self.getRuntime(), args, 0, 1) == 1) {
260: d.setEnum(args[0]);
261: } else {
262: d.setProc(self.getRuntime()
263: .newProc(false, Block.NULL_BLOCK));
264: }
265: return self;
266: }
267:
268: public static IRubyObject yield(IRubyObject self,
269: IRubyObject value, Block block) {
270: // Generator#yield
271: self.getInstanceVariable("@queue").callMethod(
272: self.getRuntime().getCurrentContext(), "<<", value);
273: GeneratorData d = (GeneratorData) self.dataGetStruct();
274: d.doWait();
275: return self;
276: }
277:
278: public static IRubyObject end_p(IRubyObject self) {
279: // Generator#end_p
280: GeneratorData d = (GeneratorData) self.dataGetStruct();
281: return d.isEnd() ? self.getRuntime().getTrue() : self
282: .getRuntime().getFalse();
283: }
284:
285: public static IRubyObject next_p(IRubyObject self) {
286: // Generator#next_p
287: GeneratorData d = (GeneratorData) self.dataGetStruct();
288: return !d.isEnd() ? self.getRuntime().getTrue() : self
289: .getRuntime().getFalse();
290: }
291:
292: public static IRubyObject index(IRubyObject self) {
293: // Generator#index
294: return self.getInstanceVariable("@index");
295: }
296:
297: public static IRubyObject next(IRubyObject self, Block block) {
298: // Generator#next
299: GeneratorData d = (GeneratorData) self.dataGetStruct();
300: if (d.isEnd()) {
301: throw self.getRuntime().newEOFError();
302: }
303: d.generate();
304: self.setInstanceVariable("@index", self.getInstanceVariable(
305: "@index").callMethod(
306: self.getRuntime().getCurrentContext(),
307: MethodIndex.OP_PLUS, "+",
308: self.getRuntime().newFixnum(1)));
309: return self.getInstanceVariable("@queue").callMethod(
310: self.getRuntime().getCurrentContext(), "shift");
311: }
312:
313: public static IRubyObject current(IRubyObject self, Block block) {
314: // Generator#current
315: if (self.getInstanceVariable("@queue").callMethod(
316: self.getRuntime().getCurrentContext(),
317: MethodIndex.EMPTY_P, "empty?").isTrue()) {
318: throw self.getRuntime().newEOFError();
319: }
320: return self.getInstanceVariable("@queue").callMethod(
321: self.getRuntime().getCurrentContext(), "first");
322: }
323:
324: public static IRubyObject rewind(IRubyObject self, Block block) {
325: // Generator#rewind
326: if (self.getInstanceVariable("@index").callMethod(
327: self.getRuntime().getCurrentContext(), "nonzero?")
328: .isTrue()) {
329: GeneratorData d = (GeneratorData) self.dataGetStruct();
330:
331: self.setInstanceVariable("@queue", self.getRuntime()
332: .newArray());
333: self.setInstanceVariable("@index", self.getRuntime()
334: .newFixnum(0));
335:
336: d.start();
337: }
338:
339: return self;
340: }
341:
342: public static IRubyObject each(IRubyObject self, Block block) {
343: // Generator#each
344: rewind(self, Block.NULL_BLOCK);
345: ThreadContext ctx = self.getRuntime().getCurrentContext();
346: while (next_p(self).isTrue()) {
347: block.yield(ctx, next(self, Block.NULL_BLOCK));
348: }
349: return self;
350: }
351: }// Generator
|