KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyStruct


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) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
15  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
16  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
17  * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
18  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
19  * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
20  *
21  * Alternatively, the contents of this file may be used under the terms of
22  * either of the GNU General Public License Version 2 or later (the "GPL"),
23  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24  * in which case the provisions of the GPL or the LGPL are applicable instead
25  * of those above. If you wish to allow use of your version of this file only
26  * under the terms of either the GPL or the LGPL, and not to allow others to
27  * use your version of this file under the terms of the CPL, indicate your
28  * decision by deleting the provisions above and replace them with the notice
29  * and other provisions required by the GPL or the LGPL. If you do not delete
30  * the provisions above, a recipient may use your version of this file under
31  * the terms of any one of the CPL, the GPL or the LGPL.
32  ***** END LICENSE BLOCK *****/

33 package org.jruby;
34
35 import java.util.List JavaDoc;
36 import org.jruby.runtime.Block;
37 import org.jruby.runtime.CallbackFactory;
38 import org.jruby.runtime.ObjectAllocator;
39 import org.jruby.runtime.ThreadContext;
40 import org.jruby.runtime.builtin.IRubyObject;
41 import org.jruby.runtime.marshal.MarshalStream;
42 import org.jruby.runtime.marshal.UnmarshalStream;
43 import org.jruby.util.IdUtil;
44 import org.jruby.exceptions.RaiseException;
45 import org.jruby.runtime.ClassIndex;
46
47 /**
48  * @author jpetersen
49  */

50 public class RubyStruct extends RubyObject {
51     private IRubyObject[] values;
52
53     /**
54      * Constructor for RubyStruct.
55      * @param runtime
56      * @param rubyClass
57      */

58     public RubyStruct(Ruby runtime, RubyClass rubyClass) {
59         super(runtime, rubyClass);
60     }
61
62     public static RubyClass createStructClass(Ruby runtime) {
63         // TODO: NOT_ALLOCATABLE_ALLOCATOR may be ok here, but it's unclear how Structs
64
// work with marshalling. Confirm behavior and ensure we're doing this correctly. JRUBY-415
65
RubyClass structClass = runtime.defineClass("Struct", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
66         structClass.index = ClassIndex.STRUCT;
67         
68         CallbackFactory callbackFactory = runtime.callbackFactory(RubyStruct.class);
69         structClass.includeModule(runtime.getModule("Enumerable"));
70
71         structClass.getMetaClass().defineMethod("new", callbackFactory.getOptSingletonMethod("newInstance"));
72
73         structClass.defineMethod("initialize", callbackFactory.getOptMethod("initialize"));
74         structClass.defineMethod("clone", callbackFactory.getMethod("rbClone"));
75
76         structClass.defineFastMethod("==", callbackFactory.getFastMethod("equal", RubyKernel.IRUBY_OBJECT));
77
78         structClass.defineFastMethod("to_s", callbackFactory.getFastMethod("to_s"));
79         structClass.defineFastMethod("inspect", callbackFactory.getFastMethod("inspect"));
80         structClass.defineFastMethod("to_a", callbackFactory.getFastMethod("to_a"));
81         structClass.defineFastMethod("values", callbackFactory.getFastMethod("to_a"));
82         structClass.defineFastMethod("size", callbackFactory.getFastMethod("size"));
83         structClass.defineFastMethod("length", callbackFactory.getFastMethod("size"));
84
85         structClass.defineMethod("each", callbackFactory.getMethod("each"));
86         structClass.defineMethod("each_pair", callbackFactory.getMethod("each_pair"));
87         structClass.defineFastMethod("[]", callbackFactory.getFastMethod("aref", RubyKernel.IRUBY_OBJECT));
88         structClass.defineFastMethod("[]=", callbackFactory.getFastMethod("aset", RubyKernel.IRUBY_OBJECT, RubyKernel.IRUBY_OBJECT));
89
90         structClass.defineFastMethod("members", callbackFactory.getFastMethod("members"));
91
92         return structClass;
93     }
94     
95     public int getNativeTypeIndex() {
96         return ClassIndex.STRUCT;
97     }
98
99     private static IRubyObject getInstanceVariable(RubyClass type, String JavaDoc name) {
100         RubyClass structClass = type.getRuntime().getClass("Struct");
101
102         while (type != null && type != structClass) {
103             IRubyObject variable = type.getInstanceVariable(name);
104             if (variable != null) {
105                 return variable;
106             }
107
108             type = type.getSuperClass();
109         }
110
111         return type.getRuntime().getNil();
112     }
113
114     private RubyClass classOf() {
115         return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass();
116     }
117
118     private void modify() {
119         testFrozen("Struct is frozen");
120
121         if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
122             throw getRuntime().newSecurityError("Insecure: can't modify struct");
123         }
124     }
125
126     private IRubyObject setByName(String JavaDoc name, IRubyObject value) {
127         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
128
129         assert !member.isNil() : "uninitialized struct";
130
131         modify();
132
133         for (int i = 0,k=member.getLength(); i < k; i++) {
134             if (member.eltInternal(i).asSymbol().equals(name)) {
135                 return values[i] = value;
136             }
137         }
138
139         throw notStructMemberError(name);
140     }
141
142     private IRubyObject getByName(String JavaDoc name) {
143         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
144
145         assert !member.isNil() : "uninitialized struct";
146
147         for (int i = 0,k=member.getLength(); i < k; i++) {
148             if (member.eltInternal(i).asSymbol().equals(name)) {
149                 return values[i];
150             }
151         }
152
153         throw notStructMemberError(name);
154     }
155
156     // Struct methods
157

158     /** Create new Struct class.
159      *
160      * MRI: rb_struct_s_def / make_struct
161      *
162      */

163     public static RubyClass newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
164         String JavaDoc name = null;
165         Ruby runtime = recv.getRuntime();
166
167         if (args.length > 0 && args[0] instanceof RubyString) {
168             name = args[0].toString();
169         }
170
171         RubyArray member = recv.getRuntime().newArray();
172
173         for (int i = name == null ? 0 : 1; i < args.length; i++) {
174             member.append(RubySymbol.newSymbol(recv.getRuntime(), args[i].asSymbol()));
175         }
176
177         RubyClass newStruct;
178         RubyClass superClass = (RubyClass)recv;
179
180         if (name == null) {
181             newStruct = new RubyClass(superClass, superClass.getAllocator());
182         } else {
183             if (!IdUtil.isConstant(name)) {
184                 throw runtime.newNameError("identifier " + name + " needs to be constant", name);
185             }
186
187             IRubyObject type = superClass.getConstantAt(name);
188
189             if (type != null) {
190                 runtime.getWarnings().warn(runtime.getCurrentContext().getFramePosition(), "redefining constant Struct::" + name);
191             }
192             newStruct = superClass.newSubClass(name, superClass.getAllocator(), superClass.getCRef());
193         }
194
195         newStruct.index = ClassIndex.STRUCT;
196         
197         newStruct.setInstanceVariable("__size__", member.length());
198         newStruct.setInstanceVariable("__member__", member);
199
200         CallbackFactory callbackFactory = recv.getRuntime().callbackFactory(RubyStruct.class);
201         newStruct.defineSingletonMethod("new", callbackFactory.getOptSingletonMethod("newStruct"));
202         newStruct.defineSingletonMethod("[]", callbackFactory.getOptSingletonMethod("newStruct"));
203         newStruct.defineSingletonMethod("members", callbackFactory.getSingletonMethod("members"));
204
205         // define access methods.
206
for (int i = name == null ? 0 : 1; i < args.length; i++) {
207             String JavaDoc memberName = args[i].asSymbol();
208             newStruct.defineMethod(memberName, callbackFactory.getMethod("get"));
209             newStruct.defineMethod(memberName + "=", callbackFactory.getMethod("set", RubyKernel.IRUBY_OBJECT));
210         }
211         
212         if (block.isGiven()) {
213             block.yield(recv.getRuntime().getCurrentContext(), null, newStruct, newStruct, false);
214         }
215
216         return newStruct;
217     }
218
219     /** Create new Structure.
220      *
221      * MRI: struct_alloc
222      *
223      */

224     public static RubyStruct newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
225         RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClass) recv);
226
227         int size = RubyNumeric.fix2int(getInstanceVariable((RubyClass) recv, "__size__"));
228
229         struct.values = new IRubyObject[size];
230
231         struct.callInit(args, block);
232
233         return struct;
234     }
235
236     public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
237         modify();
238
239         int size = RubyNumeric.fix2int(getInstanceVariable(getMetaClass(), "__size__"));
240
241         if (args.length > size) {
242             throw getRuntime().newArgumentError("struct size differs (" + args.length +" for " + size + ")");
243         }
244
245         for (int i = 0; i < args.length; i++) {
246             values[i] = args[i];
247         }
248
249         for (int i = args.length; i < size; i++) {
250             values[i] = getRuntime().getNil();
251         }
252
253         return getRuntime().getNil();
254     }
255     
256     public static RubyArray members(IRubyObject recv, Block block) {
257         RubyArray member = (RubyArray) getInstanceVariable((RubyClass) recv, "__member__");
258
259         assert !member.isNil() : "uninitialized struct";
260
261         RubyArray result = recv.getRuntime().newArray(member.getLength());
262         for (int i = 0,k=member.getLength(); i < k; i++) {
263             result.append(recv.getRuntime().newString(member.eltInternal(i).asSymbol()));
264         }
265
266         return result;
267     }
268
269     public RubyArray members() {
270         return members(classOf(), Block.NULL_BLOCK);
271     }
272
273     public IRubyObject set(IRubyObject value, Block block) {
274         String JavaDoc name = getRuntime().getCurrentContext().getFrameName();
275         if (name.endsWith("=")) {
276             name = name.substring(0, name.length() - 1);
277         }
278
279         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
280
281         assert !member.isNil() : "uninitialized struct";
282
283         modify();
284
285         for (int i = 0,k=member.getLength(); i < k; i++) {
286             if (member.eltInternal(i).asSymbol().equals(name)) {
287                 return values[i] = value;
288             }
289         }
290
291         throw notStructMemberError(name);
292     }
293
294     private RaiseException notStructMemberError(String JavaDoc name) {
295         return getRuntime().newNameError(name + " is not struct member", name);
296     }
297
298     public IRubyObject get(Block block) {
299         String JavaDoc name = getRuntime().getCurrentContext().getFrameName();
300
301         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
302
303         assert !member.isNil() : "uninitialized struct";
304
305         for (int i = 0,k=member.getLength(); i < k; i++) {
306             if (member.eltInternal(i).asSymbol().equals(name)) {
307                 return values[i];
308             }
309         }
310
311         throw notStructMemberError(name);
312     }
313
314     public IRubyObject rbClone(Block block) {
315         RubyStruct clone = new RubyStruct(getRuntime(), getMetaClass());
316
317         clone.values = new IRubyObject[values.length];
318         System.arraycopy(values, 0, clone.values, 0, values.length);
319
320         clone.setFrozen(this.isFrozen());
321         clone.setTaint(this.isTaint());
322
323         return clone;
324     }
325
326     public IRubyObject equal(IRubyObject other) {
327         if (this == other) {
328             return getRuntime().getTrue();
329         } else if (!(other instanceof RubyStruct)) {
330             return getRuntime().getFalse();
331         } else if (getMetaClass() != other.getMetaClass()) {
332             return getRuntime().getFalse();
333         } else {
334             for (int i = 0; i < values.length; i++) {
335                 if (!values[i].equals(((RubyStruct) other).values[i])) {
336                     return getRuntime().getFalse();
337                 }
338             }
339             return getRuntime().getTrue();
340         }
341     }
342
343     public IRubyObject to_s() {
344         return inspect();
345     }
346
347     public IRubyObject inspect() {
348         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
349
350         assert !member.isNil() : "uninitialized struct";
351
352         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(100);
353
354         sb.append("#<struct ").append(getMetaClass().getName()).append(' ');
355
356         for (int i = 0,k=member.getLength(); i < k; i++) {
357             if (i > 0) {
358                 sb.append(", ");
359             }
360
361             sb.append(member.eltInternal(i).asSymbol()).append("=");
362             sb.append(values[i].callMethod(getRuntime().getCurrentContext(), "inspect"));
363         }
364
365         sb.append('>');
366
367         return getRuntime().newString(sb.toString()); // OBJ_INFECT
368
}
369
370     public RubyArray to_a() {
371         return getRuntime().newArray(values);
372     }
373
374     public RubyFixnum size() {
375         return getRuntime().newFixnum(values.length);
376     }
377
378     public IRubyObject each(Block block) {
379         ThreadContext context = getRuntime().getCurrentContext();
380         for (int i = 0; i < values.length; i++) {
381             context.yield(values[i], block);
382         }
383
384         return this;
385     }
386
387     public IRubyObject each_pair(Block block) {
388         RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__");
389
390         assert !member.isNil() : "uninitialized struct";
391
392         ThreadContext context = getRuntime().getCurrentContext();
393         for (int i = 0; i < values.length; i++) {
394             context.yield(getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), values[i]}), block);
395         }
396
397         return this;
398     }
399
400     public IRubyObject aref(IRubyObject key) {
401         if (key instanceof RubyString || key instanceof RubySymbol) {
402             return getByName(key.asSymbol());
403         }
404
405         int idx = RubyNumeric.fix2int(key);
406
407         idx = idx < 0 ? values.length + idx : idx;
408
409         if (idx < 0) {
410             throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
411         } else if (idx >= values.length) {
412             throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
413         }
414
415         return values[idx];
416     }
417
418     public IRubyObject aset(IRubyObject key, IRubyObject value) {
419         if (key instanceof RubyString || key instanceof RubySymbol) {
420             return setByName(key.asSymbol(), value);
421         }
422
423         int idx = RubyNumeric.fix2int(key);
424
425         idx = idx < 0 ? values.length + idx : idx;
426
427         if (idx < 0) {
428             throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
429         } else if (idx >= values.length) {
430             throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
431         }
432
433         modify();
434         return values[idx] = value;
435     }
436
437     public static void marshalTo(RubyStruct struct, MarshalStream output) throws java.io.IOException JavaDoc {
438         String JavaDoc className = struct.getMetaClass().getName();
439         if (className == null) {
440             throw struct.getRuntime().newArgumentError("can't dump anonymous class");
441         }
442         output.dumpObject(RubySymbol.newSymbol(struct.getRuntime(), className));
443
444         List JavaDoc members = ((RubyArray) getInstanceVariable(struct.classOf(), "__member__")).getList();
445         output.writeInt(members.size());
446
447         for (int i = 0; i < members.size(); i++) {
448             RubySymbol name = (RubySymbol) members.get(i);
449             output.dumpObject(name);
450             output.dumpObject(struct.values[i]);
451         }
452     }
453
454     public static RubyStruct unmarshalFrom(UnmarshalStream input) throws java.io.IOException JavaDoc {
455         Ruby runtime = input.getRuntime();
456
457         RubySymbol className = (RubySymbol) input.unmarshalObject();
458         RubyClass rbClass = pathToClass(runtime, className.asSymbol());
459         if (rbClass == null) {
460             throw runtime.newNameError("uninitialized constant " + className, className.asSymbol());
461         }
462
463         int size = input.unmarshalInt();
464
465         IRubyObject[] values = new IRubyObject[size];
466         for (int i = 0; i < size; i++) {
467             input.unmarshalObject(); // Read and discard a Symbol, which is the name
468
values[i] = input.unmarshalObject();
469         }
470
471         RubyStruct result = newStruct(rbClass, values, Block.NULL_BLOCK);
472         input.registerLinkTarget(result);
473         return result;
474     }
475
476     private static RubyClass pathToClass(Ruby runtime, String JavaDoc path) {
477         // FIXME: Throw the right ArgumentError's if the class is missing
478
// or if it's a module.
479
return (RubyClass) runtime.getClassFromPath(path);
480     }
481 }
482
Popular Tags