From cd7ebbe54f16999b74c2c32a64336bad131ec5f3 Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Sun, 3 May 2015 20:42:11 -0700 Subject: make repeated_field quack like an array --- .../google/protobuf/jruby/RubyFieldDescriptor.java | 43 +++++-- .../google/protobuf/jruby/RubyRepeatedField.java | 136 +++++++++++---------- 2 files changed, 107 insertions(+), 72 deletions(-) (limited to 'ruby/src') diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java index 38226c4e..f3c488bc 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java @@ -71,6 +71,17 @@ public class RubyFieldDescriptor extends RubyObject { return this; } + /* + * call-seq: + * FieldDescriptor.label + * + * Return the label of this field. + */ + @JRubyMethod(name = "label") + public IRubyObject getLabel(ThreadContext context) { + return this.label; + } + /* * call-seq: * FieldDescriptor.label = label @@ -80,8 +91,10 @@ public class RubyFieldDescriptor extends RubyObject { */ @JRubyMethod(name = "label=") public IRubyObject setLabel(ThreadContext context, IRubyObject value) { + String labelName = value.asJavaString(); + this.label = context.runtime.newSymbol(labelName.toLowerCase()); this.builder.setLabel( - DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + value.asJavaString().toUpperCase())); + DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + labelName.toUpperCase())); return context.runtime.getNil(); } @@ -89,18 +102,13 @@ public class RubyFieldDescriptor extends RubyObject { * call-seq: * FieldDescriptor.name => name * - * Returns the name of this field. + * Returns the name of this field as a Ruby String, or nil if it is not set. */ @JRubyMethod(name = "name") public IRubyObject getName(ThreadContext context) { return this.name; } - @JRubyMethod(name = "subtype") - public IRubyObject getSubType(ThreadContext context) { - return subType; - } - /* * call-seq: * FieldDescriptor.name = name @@ -116,6 +124,12 @@ public class RubyFieldDescriptor extends RubyObject { return context.runtime.getNil(); } + + @JRubyMethod(name = "subtype") + public IRubyObject getSubType(ThreadContext context) { + return subType; + } + /* * call-seq: * FieldDescriptor.type => type @@ -144,6 +158,18 @@ public class RubyFieldDescriptor extends RubyObject { return context.runtime.getNil(); } + /* + * call-seq: + * FieldDescriptor.number => number + * + * Returns this field's number, as a Ruby Integer, or nil if not yet set. + * + */ + @JRubyMethod(name = "number") + public IRubyObject getnumber(ThreadContext context) { + return this.number; + } + /* * call-seq: * FieldDescriptor.number = number @@ -153,6 +179,7 @@ public class RubyFieldDescriptor extends RubyObject { */ @JRubyMethod(name = "number=") public IRubyObject setNumber(ThreadContext context, IRubyObject value) { + this.number = value; this.builder.setNumber(RubyNumeric.num2int(value)); return context.runtime.getNil(); } @@ -240,6 +267,8 @@ public class RubyFieldDescriptor extends RubyObject { private DescriptorProtos.FieldDescriptorProto.Builder builder; private IRubyObject name; + private IRubyObject label; + private IRubyObject number; private IRubyObject subType; private IRubyObject oneofName; private Descriptors.FieldDescriptor fieldDef; diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java index 84bf8956..946f9e74 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java @@ -40,6 +40,7 @@ import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; +import java.util.Arrays; @JRubyClass(name = "RepeatedClass", include = "Enumerable") public class RubyRepeatedField extends RubyObject { @@ -110,6 +111,10 @@ public class RubyRepeatedField extends RubyObject { public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) { int arrIndex = normalizeArrayIndex(index); Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + IRubyObject defaultValue = defaultValue(context); + for (int i = this.storage.size(); i < arrIndex; i++) { + this.storage.set(i, defaultValue); + } this.storage.set(arrIndex, value); return context.runtime.getNil(); } @@ -120,27 +125,35 @@ public class RubyRepeatedField extends RubyObject { * * Accesses the element at the given index. Returns nil on out-of-bounds */ - @JRubyMethod(name = "[]") - public IRubyObject index(ThreadContext context, IRubyObject index) { - int arrIndex = normalizeArrayIndex(index); - if (arrIndex < 0 || arrIndex >= this.storage.size()) { - return context.runtime.getNil(); + @JRubyMethod(required=1, optional=1, name = {"at", "[]"}) + public IRubyObject index(ThreadContext context, IRubyObject[] args) { + if (args.length == 1){ + IRubyObject arg = args[0]; + if (Utils.isRubyNum(arg)) { + /* standard case */ + int arrIndex = normalizeArrayIndex(arg); + if (arrIndex < 0 || arrIndex >= this.storage.size()) { + return context.runtime.getNil(); + } + return this.storage.eltInternal(arrIndex); + } else if (arg instanceof RubyRange) { + RubyRange range = ((RubyRange) arg); + int beg = RubyNumeric.num2int(range.first(context)); + int to = RubyNumeric.num2int(range.last(context)); + int len = to - beg + 1; + return this.storage.subseq(beg, len); + } } - return this.storage.eltInternal(arrIndex); - } - - /* - * call-seq: - * RepeatedField.insert(*args) - * - * Pushes each arg in turn onto the end of the repeated field. - */ - @JRubyMethod(rest = true) - public IRubyObject insert(ThreadContext context, IRubyObject[] args) { - for (int i = 0; i < args.length; i++) { - push(context, args[i]); + /* assume 2 arguments */ + int beg = RubyNumeric.num2int(args[0]); + int len = RubyNumeric.num2int(args[1]); + if (beg < 0) { + beg += this.storage.size(); } - return context.runtime.getNil(); + if (beg >= this.storage.size()) { + return context.runtime.getNil(); + } + return this.storage.subseq(beg, len); } /* @@ -151,20 +164,19 @@ public class RubyRepeatedField extends RubyObject { */ @JRubyMethod(name = {"push", "<<"}) public IRubyObject push(ThreadContext context, IRubyObject value) { - Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + if (!(fieldType == Descriptors.FieldDescriptor.Type.MESSAGE && + value == context.runtime.getNil())) { + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + } this.storage.add(value); - return this; + return this.storage; } /* - * call-seq: - * RepeatedField.pop => value - * - * Removes the last element and returns it. Throws an exception if the repeated - * field is empty. + * private Ruby method used by RepeatedField.pop */ - @JRubyMethod - public IRubyObject pop(ThreadContext context) { + @JRubyMethod(visibility = org.jruby.runtime.Visibility.PRIVATE) + public IRubyObject pop_one(ThreadContext context) { IRubyObject ret = this.storage.last(); this.storage.remove(ret); return ret; @@ -181,7 +193,7 @@ public class RubyRepeatedField extends RubyObject { RubyArray arr = (RubyArray) list; checkArrayElementType(context, arr); this.storage = arr; - return context.runtime.getNil(); + return this.storage; } /* @@ -193,7 +205,7 @@ public class RubyRepeatedField extends RubyObject { @JRubyMethod public IRubyObject clear(ThreadContext context) { this.storage.clear(); - return context.runtime.getNil(); + return this.storage; } /* @@ -202,7 +214,7 @@ public class RubyRepeatedField extends RubyObject { * * Returns the length of this repeated field. */ - @JRubyMethod(name = {"count", "length"}) + @JRubyMethod(name = {"length", "size"}) public IRubyObject length(ThreadContext context) { return context.runtime.newFixnum(this.storage.size()); } @@ -215,7 +227,7 @@ public class RubyRepeatedField extends RubyObject { * repeated field's elements and other's elements. The other (second) list may * be either another repeated field or a Ruby array. */ - @JRubyMethod(name = "+") + @JRubyMethod(name = {"+"}) public IRubyObject plus(ThreadContext context, IRubyObject list) { RubyRepeatedField dup = (RubyRepeatedField) dup(context); if (list instanceof RubyArray) { @@ -231,6 +243,27 @@ public class RubyRepeatedField extends RubyObject { return dup; } + /* + * call-seq: + * RepeatedField.concat(other) => self + * + * concats the passed in array to self. Returns a Ruby array. + */ + @JRubyMethod + public IRubyObject concat(ThreadContext context, IRubyObject list) { + if (list instanceof RubyArray) { + checkArrayElementType(context, (RubyArray) list); + this.storage.addAll((RubyArray) list); + } else { + RubyRepeatedField repeatedField = (RubyRepeatedField) list; + if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && ! + typeClass.equals(repeatedField.typeClass))) + throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type."); + this.storage.addAll((RubyArray) repeatedField.toArray(context)); + } + return this.storage; + } + /* * call-seq: * RepeatedField.hash => hash_value @@ -239,7 +272,7 @@ public class RubyRepeatedField extends RubyObject { */ @JRubyMethod public IRubyObject hash(ThreadContext context) { - int hashCode = System.identityHashCode(this.storage); + int hashCode = this.storage.hashCode(); return context.runtime.newFixnum(hashCode); } @@ -268,17 +301,12 @@ public class RubyRepeatedField extends RubyObject { @JRubyMethod public IRubyObject each(ThreadContext context, Block block) { this.storage.each(context, block); - return context.runtime.getNil(); + return this.storage; } + @JRubyMethod(name = {"to_ary", "to_a"}) public IRubyObject toArray(ThreadContext context) { - for (int i = 0; i < this.storage.size(); i++) { - IRubyObject defaultValue = defaultValue(context); - if (storage.eltInternal(i).isNil()) { - storage.set(i, defaultValue); - } - } return this.storage; } @@ -298,31 +326,6 @@ public class RubyRepeatedField extends RubyObject { return dup; } - /* - * call-seq: - * RepeatedField.inspect => string - * - * Returns a string representing this repeated field's elements. It will be - * formated as "[, , ...]", with each element's string - * representation computed by its own #inspect method. - */ - @JRubyMethod - public IRubyObject inspect() { - StringBuilder str = new StringBuilder("["); - for (int i = 0; i < this.storage.size(); i++) { - str.append(storage.eltInternal(i).inspect()); - str.append(", "); - } - - if (str.length() > 1) { - str.replace(str.length() - 2, str.length(), "]"); - } else { - str.append("]"); - } - - return getRuntime().newString(str.toString()); - } - // Java API protected IRubyObject get(int index) { return this.storage.eltInternal(index); @@ -376,6 +379,9 @@ public class RubyRepeatedField extends RubyObject { case STRING: value = sentinel.getDefaultString(); break; + case ENUM: + IRubyObject defaultEnumLoc = context.runtime.newFixnum(0); + return RubyEnum.lookup(context, typeClass, defaultEnumLoc); default: return context.runtime.getNil(); } -- cgit v1.2.3