Index: third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java |
diff --git a/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java b/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dd9179b0300c9aef084b08785a6c2817378393ca |
--- /dev/null |
+++ b/third_party/protobuf/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java |
@@ -0,0 +1,269 @@ |
+/* |
+ * Protocol Buffers - Google's data interchange format |
+ * Copyright 2014 Google Inc. All rights reserved. |
+ * https://developers.google.com/protocol-buffers/ |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+package com.google.protobuf.jruby; |
+ |
+import com.google.protobuf.DescriptorProtos; |
+import com.google.protobuf.Descriptors; |
+import org.jruby.*; |
+import org.jruby.anno.JRubyClass; |
+import org.jruby.anno.JRubyMethod; |
+import org.jruby.runtime.Block; |
+import org.jruby.runtime.ObjectAllocator; |
+import org.jruby.runtime.ThreadContext; |
+import org.jruby.runtime.builtin.IRubyObject; |
+ |
+import java.util.HashMap; |
+import java.util.Map; |
+ |
+ |
+@JRubyClass(name = "Descriptor", include = "Enumerable") |
+public class RubyDescriptor extends RubyObject { |
+ public static void createRubyDescriptor(Ruby runtime) { |
+ RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); |
+ RubyClass cDescriptor = protobuf.defineClassUnder("Descriptor", runtime.getObject(), new ObjectAllocator() { |
+ @Override |
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
+ return new RubyDescriptor(runtime, klazz); |
+ } |
+ }); |
+ cDescriptor.includeModule(runtime.getEnumerable()); |
+ cDescriptor.defineAnnotatedMethods(RubyDescriptor.class); |
+ } |
+ |
+ public RubyDescriptor(Ruby runtime, RubyClass klazz) { |
+ super(runtime, klazz); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.new => descriptor |
+ * |
+ * Creates a new, empty, message type descriptor. At a minimum, its name must be |
+ * set before it is added to a pool. It cannot be used to create messages until |
+ * it is added to a pool, after which it becomes immutable (as part of a |
+ * finalization process). |
+ */ |
+ @JRubyMethod |
+ public IRubyObject initialize(ThreadContext context) { |
+ this.builder = DescriptorProtos.DescriptorProto.newBuilder(); |
+ this.fieldDefMap = new HashMap<String, RubyFieldDescriptor>(); |
+ this.oneofDefs = new HashMap<IRubyObject, RubyOneofDescriptor>(); |
+ return this; |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.name => name |
+ * |
+ * Returns the name of this message type as a fully-qualfied string (e.g., |
+ * My.Package.MessageType). |
+ */ |
+ @JRubyMethod(name = "name") |
+ public IRubyObject getName(ThreadContext context) { |
+ return this.name; |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.name = name |
+ * |
+ * Assigns a name to this message type. The descriptor must not have been added |
+ * to a pool yet. |
+ */ |
+ @JRubyMethod(name = "name=") |
+ public IRubyObject setName(ThreadContext context, IRubyObject name) { |
+ this.name = name; |
+ this.builder.setName(Utils.escapeIdentifier(this.name.asJavaString())); |
+ return context.runtime.getNil(); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.add_field(field) => nil |
+ * |
+ * Adds the given FieldDescriptor to this message type. The descriptor must not |
+ * have been added to a pool yet. Raises an exception if a field with the same |
+ * name or number already exists. Sub-type references (e.g. for fields of type |
+ * message) are not resolved at this point. |
+ */ |
+ @JRubyMethod(name = "add_field") |
+ public IRubyObject addField(ThreadContext context, IRubyObject obj) { |
+ RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) obj; |
+ this.fieldDefMap.put(fieldDef.getName(context).asJavaString(), fieldDef); |
+ this.builder.addField(fieldDef.build()); |
+ return context.runtime.getNil(); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.lookup(name) => FieldDescriptor |
+ * |
+ * Returns the field descriptor for the field with the given name, if present, |
+ * or nil if none. |
+ */ |
+ @JRubyMethod |
+ public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) { |
+ return this.fieldDefMap.get(fieldName.asJavaString()); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.msgclass => message_klass |
+ * |
+ * Returns the Ruby class created for this message type. Valid only once the |
+ * message type has been added to a pool. |
+ */ |
+ @JRubyMethod |
+ public IRubyObject msgclass(ThreadContext context) { |
+ if (this.klazz == null) { |
+ this.klazz = buildClassFromDescriptor(context); |
+ } |
+ return this.klazz; |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.each(&block) |
+ * |
+ * Iterates over fields in this message type, yielding to the block on each one. |
+ */ |
+ @JRubyMethod |
+ public IRubyObject each(ThreadContext context, Block block) { |
+ for (Map.Entry<String, RubyFieldDescriptor> entry : fieldDefMap.entrySet()) { |
+ block.yield(context, entry.getValue()); |
+ } |
+ return context.runtime.getNil(); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.add_oneof(oneof) => nil |
+ * |
+ * Adds the given OneofDescriptor to this message type. This descriptor must not |
+ * have been added to a pool yet. Raises an exception if a oneof with the same |
+ * name already exists, or if any of the oneof's fields' names or numbers |
+ * conflict with an existing field in this message type. All fields in the oneof |
+ * are added to the message descriptor. Sub-type references (e.g. for fields of |
+ * type message) are not resolved at this point. |
+ */ |
+ @JRubyMethod(name = "add_oneof") |
+ public IRubyObject addOneof(ThreadContext context, IRubyObject obj) { |
+ RubyOneofDescriptor def = (RubyOneofDescriptor) obj; |
+ builder.addOneofDecl(def.build(builder.getOneofDeclCount())); |
+ for (RubyFieldDescriptor fieldDescriptor : def.getFields()) { |
+ addField(context, fieldDescriptor); |
+ } |
+ oneofDefs.put(def.getName(context), def); |
+ return context.runtime.getNil(); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.each_oneof(&block) => nil |
+ * |
+ * Invokes the given block for each oneof in this message type, passing the |
+ * corresponding OneofDescriptor. |
+ */ |
+ @JRubyMethod(name = "each_oneof") |
+ public IRubyObject eachOneof(ThreadContext context, Block block) { |
+ for (RubyOneofDescriptor oneofDescriptor : oneofDefs.values()) { |
+ block.yieldSpecific(context, oneofDescriptor); |
+ } |
+ return context.runtime.getNil(); |
+ } |
+ |
+ /* |
+ * call-seq: |
+ * Descriptor.lookup_oneof(name) => OneofDescriptor |
+ * |
+ * Returns the oneof descriptor for the oneof with the given name, if present, |
+ * or nil if none. |
+ */ |
+ @JRubyMethod(name = "lookup_oneof") |
+ public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) { |
+ if (name instanceof RubySymbol) { |
+ name = ((RubySymbol) name).id2name(); |
+ } |
+ return oneofDefs.containsKey(name) ? oneofDefs.get(name) : context.runtime.getNil(); |
+ } |
+ |
+ public void setDescriptor(Descriptors.Descriptor descriptor) { |
+ this.descriptor = descriptor; |
+ } |
+ |
+ public Descriptors.Descriptor getDescriptor() { |
+ return this.descriptor; |
+ } |
+ |
+ public DescriptorProtos.DescriptorProto.Builder getBuilder() { |
+ return builder; |
+ } |
+ |
+ public void setMapEntry(boolean isMapEntry) { |
+ this.builder.setOptions(DescriptorProtos.MessageOptions.newBuilder().setMapEntry(isMapEntry)); |
+ } |
+ |
+ private RubyModule buildClassFromDescriptor(ThreadContext context) { |
+ Ruby runtime = context.runtime; |
+ |
+ ObjectAllocator allocator = new ObjectAllocator() { |
+ @Override |
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
+ return new RubyMessage(runtime, klazz, descriptor); |
+ } |
+ }; |
+ |
+ // rb_define_class_id |
+ RubyClass klass = RubyClass.newClass(runtime, runtime.getObject()); |
+ klass.setAllocator(allocator); |
+ klass.makeMetaClass(runtime.getObject().getMetaClass()); |
+ klass.inherit(runtime.getObject()); |
+ RubyModule messageExts = runtime.getClassFromPath("Google::Protobuf::MessageExts"); |
+ klass.include(new IRubyObject[] {messageExts}); |
+ klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this); |
+ klass.defineAnnotatedMethods(RubyMessage.class); |
+ return klass; |
+ } |
+ |
+ protected RubyFieldDescriptor lookup(String fieldName) { |
+ return fieldDefMap.get(Utils.unescapeIdentifier(fieldName)); |
+ } |
+ |
+ private IRubyObject name; |
+ private RubyModule klazz; |
+ |
+ private DescriptorProtos.DescriptorProto.Builder builder; |
+ private Descriptors.Descriptor descriptor; |
+ private Map<String, RubyFieldDescriptor> fieldDefMap; |
+ private Map<IRubyObject, RubyOneofDescriptor> oneofDefs; |
+} |