Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: CPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Common Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/cpl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr> Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se> Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de> Copyright (C) 2004 Thomas E Enebo <enebo@acm.org> Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de> Copyright (C) 2005 Charles O Nutter <headius@headius.com> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the CPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the CPL, the GPL or the LGPL. END LICENSE BLOCK ***
 
 package org.jruby;
 
 import static org.jruby.RubyEnumerator.enumeratorize;
 
 
 
 import static org.jruby.runtime.Visibility.*;
 
 import static org.jruby.javasupport.util.RuntimeHelpers.invokedynamic;
 import static org.jruby.runtime.invokedynamic.MethodNames.HASH;

Author(s):
jpetersen
 
 @JRubyClass(name="Struct")
 public class RubyStruct extends RubyObject {
     private final IRubyObject[] values;

    
Constructor for RubyStruct.

Parameters:
runtime
rubyClass
 
     private RubyStruct(Ruby runtimeRubyClass rubyClass) {
         super(runtimerubyClass);
         
         int size = RubyNumeric.fix2int(getInternalVariable((RubyClass)rubyClass"__size__"));
 
          = new IRubyObject[size];
 
         RuntimeHelpers.fillNil(runtime);
     }
 
     public static RubyClass createStructClass(Ruby runtime) {
         RubyClass structClass = runtime.defineClass("Struct"runtime.getObject(), .);
         runtime.setStructClass(structClass);
         structClass.index = .;
         structClass.includeModule(runtime.getEnumerable());
         structClass.defineAnnotatedMethods(RubyStruct.class);
 
         return structClass;
     }
     
     @Override
     public int getNativeTypeIndex() {
         return .;
     }
     
     private static IRubyObject getInternalVariable(RubyClass typeString internedName) {
        RubyClass structClass = type.getRuntime().getStructClass();
        IRubyObject variable;
        while (type != null && type != structClass) {
            if ((variable = (IRubyObject)type.getInternalVariable(internedName)) != null) {
                return variable;
            }
            type = type.getSuperClass();
        }
        return type.getRuntime().getNil();
    }
    private RubyClass classOf() {
        return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass();
    }
    private void modify() {
        testFrozen();
    }
    
    public RubyFixnum hash(ThreadContext context) {
        Ruby runtime = getRuntime();
        int h = getMetaClass().getRealClass().hashCode();
        for (int i = 0; i < .i++) {
            h = (h << 1) | (h < 0 ? 1 : 0);
            h ^= RubyNumeric.num2long(invokedynamic(context[i], ));
        }
        
        return runtime.newFixnum(h);
    }
    private IRubyObject setByName(String nameIRubyObject value) {
        RubyArray member = (RubyArraygetInternalVariable(classOf(), "__member__");
        assert !member.isNil() : "uninitialized struct";
        modify();
        for (int i = 0,k=member.getLength(); i < ki++) {
            if (member.eltInternal(i).asJavaString().equals(name)) {
                return [i] = value;
            }
        }
        throw notStructMemberError(name);
    }
    private IRubyObject getByName(String name) {
        RubyArray member = (RubyArraygetInternalVariable(classOf(), "__member__");
        assert !member.isNil() : "uninitialized struct";
        for (int i = 0,k=member.getLength(); i < ki++) {
            if (member.eltInternal(i).asJavaString().equals(name)) {
                return [i];
            }
        }
        throw notStructMemberError(name);
    }
    // Struct methods

    
Create new Struct class. MRI: rb_struct_s_def / make_struct
    @JRubyMethod(name = "new", required = 1, rest = true, meta = true)
    public static RubyClass newInstance(IRubyObject recvIRubyObject[] argsBlock block) {
        String name = null;
        boolean nilName = false;
        Ruby runtime = recv.getRuntime();
        if (args.length > 0) {
            IRubyObject firstArgAsString = args[0].checkStringType();
            if (!firstArgAsString.isNil()) {
                name = ((RubyString)firstArgAsString).getByteList().toString();
            } else if (args[0].isNil()) {
                nilName = true;
            }
        }
        RubyArray member = runtime.newArray();
        for (int i = (name == null && !nilName) ? 0 : 1; i < args.lengthi++) {
            member.append(runtime.newSymbol(args[i].asJavaString()));
        }
        RubyClass newStruct;
        RubyClass superClass = (RubyClass)recv;
        if (name == null || nilName) {
            newStruct = RubyClass.newClass(runtimesuperClass);
            newStruct.setAllocator();
            newStruct.makeMetaClass(superClass.getMetaClass());
            newStruct.inherit(superClass);
        } else {
            if (!IdUtil.isConstant(name)) {
                throw runtime.newNameError("identifier " + name + " needs to be constant"name);
            }
            IRubyObject type = superClass.getConstantAt(name);
            if (type != null) {
                ThreadContext context = runtime.getCurrentContext();
                runtime.getWarnings().warn(.context.getFile(), context.getLine(), "redefining constant Struct::" + name);
                superClass.remove_const(contextruntime.newString(name));
            }
            newStruct = superClass.defineClassUnder(namesuperClass);
        }
        // set reified class to RubyStruct, for Java subclasses to use
        newStruct.setReifiedClass(RubyStruct.class);
        newStruct.index = .;
        
        newStruct.setInternalVariable("__size__"member.length());
        newStruct.setInternalVariable("__member__"member);
        newStruct.getSingletonClass().defineAnnotatedMethods(StructMethods.class);
        // define access methods.
        for (int i = (name == null && !nilName) ? 0 : 1; i < args.lengthi++) {
            final String memberName = args[i].asJavaString();
            // if we are storing a name as well, index is one too high for values
            final int index = (name == null && !nilName) ? i : i - 1;
            newStruct.addMethod(memberNamenew DynamicMethod(newStruct..) {
                @Override
                public IRubyObject call(ThreadContext contextIRubyObject selfRubyModule clazzString nameIRubyObject[] argsBlock block) {
                    Arity.checkArgumentCount(context.runtimenameargs, 0, 0);
                    return ((RubyStruct)self).get(index);
                }
                @Override
                public IRubyObject call(ThreadContext contextIRubyObject selfRubyModule clazzString name) {
                    return ((RubyStruct)self).get(index);
                }
                @Override
                public DynamicMethod dup() {
                    return this;
                }
            });
            newStruct.addMethod(memberName + "="new DynamicMethod(newStruct..) {
                @Override
                public IRubyObject call(ThreadContext contextIRubyObject selfRubyModule clazzString nameIRubyObject[] argsBlock block) {
                    Arity.checkArgumentCount(context.runtimenameargs, 1, 1);
                    return ((RubyStruct)self).set(args[0], index);
                }
                @Override
                public IRubyObject call(ThreadContext contextIRubyObject selfRubyModule clazzString nameIRubyObject arg) {
                    return ((RubyStruct)self).set(argindex);
                }
                @Override
                public DynamicMethod dup() {
                    return this;
                }
            });
        }
        
        if (block.isGiven()) {
            // Struct bodies should be public by default, so set block visibility to public. JRUBY-1185.
            block.getBinding().setVisibility(.);
            block.yieldNonArray(runtime.getCurrentContext(), nullnewStructnewStruct);
        }
        return newStruct;
    }
    
    // For binding purposes on the newly created struct types
    public static class StructMethods {
        @JRubyMethod(name = {"new""[]"}, rest = true)
        public static IRubyObject newStruct(IRubyObject recvIRubyObject[] argsBlock block) {
            return RubyStruct.newStruct(recvargsblock);
        }
        @JRubyMethod(name = {"new""[]"})
        public static IRubyObject newStruct(IRubyObject recvBlock block) {
            return RubyStruct.newStruct(recvblock);
        }
        @JRubyMethod(name = {"new""[]"})
        public static IRubyObject newStruct(IRubyObject recvIRubyObject arg0Block block) {
            return RubyStruct.newStruct(recvarg0block);
        }
        @JRubyMethod(name = {"new""[]"})
        public static IRubyObject newStruct(IRubyObject recvIRubyObject arg0IRubyObject arg1Block block) {
            return RubyStruct.newStruct(recvarg0arg1block);
        }
        @JRubyMethod(name = {"new""[]"})
        public static IRubyObject newStruct(IRubyObject recvIRubyObject arg0IRubyObject arg1IRubyObject arg2Block block) {
            return RubyStruct.newStruct(recvarg0arg1arg2block);
        }
        @JRubyMethod(name = "members", compat = .)
        public static IRubyObject members(IRubyObject recvBlock block) {
            return RubyStruct.members(recvblock);
        }
        @JRubyMethod(name = "members", compat = .)
        public static IRubyObject members19(IRubyObject recvBlock block) {
            return RubyStruct.members19(recvblock);
        }
    }

    
Create new Structure. MRI: struct_alloc
    public static RubyStruct newStruct(IRubyObject recvIRubyObject[] argsBlock block) {
        RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClassrecv);
        struct.callInit(argsblock);
        return struct;
    }
    public static RubyStruct newStruct(IRubyObject recvBlock block) {
        RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClassrecv);
        struct.callInit(block);
        return struct;
    }
    public static RubyStruct newStruct(IRubyObject recvIRubyObject arg0Block block) {
        RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClassrecv);
        struct.callInit(arg0block);
        return struct;
    }
    public static RubyStruct newStruct(IRubyObject recvIRubyObject arg0IRubyObject arg1Block block) {
        RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClassrecv);
        struct.callInit(arg0arg1block);
        return struct;
    }
    public static RubyStruct newStruct(IRubyObject recvIRubyObject arg0IRubyObject arg1IRubyObject arg2Block block) {
        RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClassrecv);
        struct.callInit(arg0arg1arg2block);
        return struct;
    }
    private void checkSize(int length) {
        if (length > .) {
            throw getRuntime().newArgumentError("struct size differs (" + length +" for " + . + ")");
        }
    }
    @JRubyMethod(rest = true, visibility = )
    public IRubyObject initialize(ThreadContext contextIRubyObject[] args) {
        modify();
        checkSize(args.length);
        System.arraycopy(args, 0, , 0, args.length);
        RuntimeHelpers.fillNil(args.length.context.runtime);
        return context.nil;
    }
    @JRubyMethod(visibility = )
    public IRubyObject initialize(ThreadContext context) {
        IRubyObject nil = context.nil;
        return initializeInternal(context, 0, nilnilnil);
    }
    @JRubyMethod(visibility = )
    public IRubyObject initialize(ThreadContext contextIRubyObject arg0) {
        IRubyObject nil = context.nil;
        return initializeInternal(context, 1, arg0nilnil);
    }
    @JRubyMethod(visibility = )
    public IRubyObject initialize(ThreadContext contextIRubyObject arg0IRubyObject arg1) {
        return initializeInternal(context, 2, arg0arg1context.nil);
    }
    @JRubyMethod(visibility = )
    public IRubyObject initialize(ThreadContext contextIRubyObject arg0IRubyObject arg1IRubyObject arg2) {
        return initializeInternal(context, 3, arg0arg1arg2);
    }
    
    public IRubyObject initializeInternal(ThreadContext contextint providedIRubyObject arg0IRubyObject arg1IRubyObject arg2) {
        modify();
        checkSize(provided);
        switch (provided) {
        case 3:
            [2] = arg2;
        case 2:
            [1] = arg1;
        case 1:
            [0] = arg0;
        }
        if (provided < .) {
            RuntimeHelpers.fillNil(provided.context.runtime);
        }
        return getRuntime().getNil();
    }
    
    public static RubyArray members(IRubyObject recvBlock block) {
        RubyArray member = (RubyArraygetInternalVariable((RubyClassrecv"__member__");
        assert !member.isNil() : "uninitialized struct";
        RubyArray result = recv.getRuntime().newArray(member.getLength());
        for (int i = 0,k=member.getLength(); i < ki++) {
            // this looks weird, but it's because they're RubySymbol and that's java.lang.String internally
            result.append(recv.getRuntime().newString(member.eltInternal(i).asJavaString()));
        }
        return result;
    }
    public static RubyArray members19(IRubyObject recvBlock block) {
        RubyArray member = (RubyArraygetInternalVariable((RubyClassrecv"__member__");
        assert !member.isNil() : "uninitialized struct";
        RubyArray result = recv.getRuntime().newArray(member.getLength());
        for (int i = 0,k=member.getLength(); i < ki++) {
            result.append(member.eltInternal(i));
        }
        return result;
    }
    @JRubyMethod(name = "members", compat = .)
    public RubyArray members() {
        return members(classOf(), .);
    }
    @JRubyMethod(name = "members", compat = .)
    public RubyArray members19() {
        return members19(classOf(), .);
    }
    
    public RubyArray select(ThreadContext contextBlock block) {
        RubyArray array = RubyArray.newArray(context.runtime);
        
        for (int i = 0; i < .i++) {
            if (block.yield(context[i]).isTrue()) {
                array.append([i]);
            }
        }
        
        return array;
    }
    public IRubyObject set(IRubyObject valueint index) {
        modify();
        return [index] = value;
    }
    private RaiseException notStructMemberError(String name) {
        return getRuntime().newNameError("no member '" + name + "' in struct"name);
    }
    public IRubyObject get(int index) {
        return [index];
    }
    @Override
    public void copySpecialInstanceVariables(IRubyObject clone) {
        RubyStruct struct = (RubyStruct)clone;
        System.arraycopy(, 0, struct.values, 0, .);
    }
    @JRubyMethod(name = "==", required = 1)
    public IRubyObject op_equal(final ThreadContext contextIRubyObject other) {
        if (this == otherreturn getRuntime().getTrue();
        if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
        if (getMetaClass().getRealClass() != other.getMetaClass().getRealClass()) return getRuntime().getFalse();
        
        final Ruby runtime = getRuntime();
        final RubyStruct otherStruct = (RubyStruct)other;
        // identical
        if (other == thisreturn runtime.getTrue();
        // recursion guard
        return runtime.execRecursiveOuter(new Ruby.RecursiveFunction() {
            public IRubyObject call(IRubyObject objboolean recur) {
                if (recur) {
                    return runtime.getTrue();
                }
                for (int i = 0; i < .i++) {
                    if (!equalInternal(context[i], otherStruct.values[i])) return runtime.getFalse();
                }
                return runtime.getTrue();
            }
        }, this);
    }
    
    @JRubyMethod(name = "eql?", required = 1)
    public IRubyObject eql_p(final ThreadContext contextIRubyObject other) {
        if (this == otherreturn getRuntime().getTrue();
        if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
        if (getMetaClass() != other.getMetaClass()) return getRuntime().getFalse();
        
        final Ruby runtime = getRuntime();
        final RubyStruct otherStruct = (RubyStruct)other;
        // identical
        if (other == thisreturn runtime.getTrue();
        // recursion guard
        return runtime.execRecursiveOuter(new Ruby.RecursiveFunction() {
            public IRubyObject call(IRubyObject objboolean recur) {
                if (recur) {
                    return runtime.getTrue();
                }
                for (int i = 0; i < .i++) {
                    if (!eqlInternal(context[i], otherStruct.values[i])) return runtime.getFalse();
                }
                return runtime.getTrue();
            }
        }, this);
    }

    
inspect_struct
    private IRubyObject inspectStruct(final ThreadContext context) {    
        RubyArray member = (RubyArraygetInternalVariable(classOf(), "__member__");
        assert !member.isNil() : "uninitialized struct";
        ByteList buffer = new ByteList("#<struct ".getBytes());
        buffer.append(getMetaClass().getRealClass().getRealClass().getName().getBytes());
        buffer.append(' ');
        for (int i = 0,k=member.getLength(); i < ki++) {
            if (i > 0) buffer.append(',').append(' ');
            // FIXME: MRI has special case for constants here 
            buffer.append(RubyString.objAsString(contextmember.eltInternal(i)).getByteList());
            buffer.append('=');
            buffer.append(inspect(context[i]).getByteList());
        }
        buffer.append('>');
        return getRuntime().newString(buffer); // OBJ_INFECT        
    }
    @JRubyMethod(name = {"inspect""to_s"})
    public IRubyObject inspect(ThreadContext context) {
        if (getRuntime().isInspecting(this)) return getRuntime().newString("#<struct " + getMetaClass().getRealClass().getName() + ":...>");
        try {
            getRuntime().registerInspecting(this);
            return inspectStruct(context);
        } finally {
            getRuntime().unregisterInspecting(this);
        }
    }
    @JRubyMethod(name = {"to_a""values"})
    public RubyArray to_a() {
        return getRuntime().newArray();
    }
    @JRubyMethod(name = {"size""length"} )
    public RubyFixnum size() {
        return getRuntime().newFixnum(.);
    }
    public IRubyObject eachInternal(ThreadContext contextBlock block) {
        for (int i = 0; i < .i++) {
            block.yield(context[i]);
        }
        return this;
    }
    public IRubyObject each(final ThreadContext contextfinal Block block) {
        return block.isGiven() ? eachInternal(contextblock) : enumeratorize(context.runtimethis"each");
    }
    public IRubyObject each_pairInternal(ThreadContext contextBlock block) {
        RubyArray member = (RubyArraygetInternalVariable(classOf(), "__member__");
        assert !member.isNil() : "uninitialized struct";
        for (int i = 0; i < .i++) {
            block.yield(contextgetRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), [i]}));
        }
        return this;
    }
    public IRubyObject each_pair(final ThreadContext contextfinal Block block) {
        return block.isGiven() ? each_pairInternal(contextblock) : enumeratorize(context.runtimethis"each_pair");
    }
    @JRubyMethod(name = "[]", required = 1)
    public IRubyObject aref(IRubyObject key) {
        if (key instanceof RubyString || key instanceof RubySymbol) {
            return getByName(key.asJavaString());
        }
        int idx = RubyNumeric.fix2int(key);
        idx = idx < 0 ? . + idx : idx;
        if (idx < 0) {
            throw getRuntime().newIndexError("offset " + idx + " too small for struct(size:" + . + ")");
        } else if (idx >= .) {
            throw getRuntime().newIndexError("offset " + idx + " too large for struct(size:" + . + ")");
        }
        return [idx];
    }
    @JRubyMethod(name = "[]=", required = 2)
    public IRubyObject aset(IRubyObject keyIRubyObject value) {
        if (key instanceof RubyString || key instanceof RubySymbol) {
            return setByName(key.asJavaString(), value);
        }
        int idx = RubyNumeric.fix2int(key);
        idx = idx < 0 ? . + idx : idx;
        if (idx < 0) {
            throw getRuntime().newIndexError("offset " + idx + " too small for struct(size:" + . + ")");
        } else if (idx >= .) {
            throw getRuntime().newIndexError("offset " + idx + " too large for struct(size:" + . + ")");
        }
        modify();
        return [idx] = value;
    }
    
    // FIXME: This is copied code from RubyArray.  Both RE, Struct, and Array should share one impl
    // This is also hacky since I construct ruby objects to access ruby arrays through aref instead
    // of something lower.
    @JRubyMethod(rest = true)
    public IRubyObject values_at(IRubyObject[] args) {
        int olen = .;
        RubyArray result = getRuntime().newArray(args.length);
        for (int i = 0; i < args.lengthi++) {
            if (args[iinstanceof RubyFixnum) {
                result.append(aref(args[i]));
                continue;
            }
            int beglen[];
            if (!(args[iinstanceof RubyRange)) {
            } else if ((beglen = ((RubyRangeargs[i]).begLenInt(olen, 0)) == null) {
                continue;
            } else {
                int beg = beglen[0];
                int len = beglen[1];
                int end = len;
                for (int j = 0; j < endj++) {
                    result.append(aref(getRuntime().newFixnum(j + beg)));
                }
                continue;
            }
            result.append(aref(getRuntime().newFixnum(RubyNumeric.num2long(args[i]))));
        }
        return result;
    }
    public static void marshalTo(RubyStruct structMarshalStream outputthrows java.io.IOException {
        output.registerLinkTarget(struct);
        output.dumpDefaultObjectHeader('S'struct.getMetaClass());
        RubyArray array = (RubyArray)getInternalVariable(struct.classOf(), "__member__");
        output.writeInt(array.size());
        for (int i = 0; i < array.size(); i++) {
            RubySymbol name = (RubySymbolarray.eltInternal(i);
            output.dumpObject(name);
            output.dumpObject(struct.values[i]);
        }
    }
    public static RubyStruct unmarshalFrom(UnmarshalStream inputthrows java.io.IOException {
        Ruby runtime = input.getRuntime();
        RubySymbol className = (RubySymbolinput.unmarshalObject(false);
        RubyClass rbClass = pathToClass(runtimeclassName.asJavaString());
        if (rbClass == null) {
            throw runtime.newNameError("uninitialized constant " + classNameclassName.asJavaString());
        }
        RubyArray mem = members(rbClass.);
        int len = input.unmarshalInt();
        IRubyObject[] values;
        if (len == 0) {
            values = .;
        } else {
            values = new IRubyObject[len];
            RuntimeHelpers.fillNil(valuesruntime);
        }
        // FIXME: This could all be more efficient, but it's how struct works
        RubyStruct result;
        if (runtime.is1_9()) {
            // 1.9 does not appear to call initialize (JRUBY-5875)
            result = new RubyStruct(runtimerbClass);
        } else {
            result = newStruct(rbClassvalues.);
        }
        input.registerLinkTarget(result);
        
        for(int i = 0; i < leni++) {
            IRubyObject slot = input.unmarshalObject(false);
            if(!mem.eltInternal(i).toString().equals(slot.toString())) {
                throw runtime.newTypeError("struct " + rbClass.getName() + " not compatible (:" + slot + " for :" + mem.eltInternal(i) + ")");
            }
            result.aset(runtime.newFixnum(i), input.unmarshalObject());
        }
        return result;
    }
    private static RubyClass pathToClass(Ruby runtimeString path) {
        // FIXME: Throw the right ArgumentError's if the class is missing
        // or if it's a module.
        return (RubyClassruntime.getClassFromPath(path);
    }
    
    private static ObjectAllocator STRUCT_INSTANCE_ALLOCATOR = new ObjectAllocator() {
        public IRubyObject allocate(Ruby runtimeRubyClass klass) {
            RubyStruct instance = new RubyStruct(runtimeklass);
            
            instance.setMetaClass(klass);
            
            return instance;
        }
    };
    
    @Override
    @JRubyMethod(required = 1)
    public IRubyObject initialize_copy(IRubyObject arg) {
        if (this == argreturn this;
        RubyStruct original = (RubyStructarg;
        
        System.arraycopy(original.values, 0, , 0, original.values.length);
        return this;
    }