Home | History | Annotate | Download | only in jruby
      1 /*
      2  * Protocol Buffers - Google's data interchange format
      3  * Copyright 2014 Google Inc.  All rights reserved.
      4  * https://developers.google.com/protocol-buffers/
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are
      8  * met:
      9  *
     10  *     * Redistributions of source code must retain the above copyright
     11  * notice, this list of conditions and the following disclaimer.
     12  *     * Redistributions in binary form must reproduce the above
     13  * copyright notice, this list of conditions and the following disclaimer
     14  * in the documentation and/or other materials provided with the
     15  * distribution.
     16  *     * Neither the name of Google Inc. nor the names of its
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.google.protobuf.jruby;
     34 
     35 import com.google.protobuf.DescriptorProtos;
     36 import com.google.protobuf.Descriptors;
     37 import org.jruby.*;
     38 import org.jruby.anno.JRubyClass;
     39 import org.jruby.anno.JRubyMethod;
     40 import org.jruby.runtime.ObjectAllocator;
     41 import org.jruby.runtime.ThreadContext;
     42 import org.jruby.runtime.builtin.IRubyObject;
     43 
     44 @JRubyClass(name = "FieldDescriptor")
     45 public class RubyFieldDescriptor extends RubyObject {
     46     public static void createRubyFileDescriptor(Ruby runtime) {
     47         RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
     48         RubyClass cFieldDescriptor = mProtobuf.defineClassUnder("FieldDescriptor", runtime.getObject(), new ObjectAllocator() {
     49             @Override
     50             public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
     51                 return new RubyFieldDescriptor(runtime, klazz);
     52             }
     53         });
     54         cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class);
     55     }
     56 
     57     public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) {
     58         super(runtime, klazz);
     59     }
     60 
     61     /*
     62      * call-seq:
     63      *     FieldDescriptor.new => field
     64      *
     65      * Returns a new field descriptor. Its name, type, etc. must be set before it is
     66      * added to a message type.
     67      */
     68     @JRubyMethod
     69     public IRubyObject initialize(ThreadContext context) {
     70         builder = DescriptorProtos.FieldDescriptorProto.newBuilder();
     71         return this;
     72     }
     73 
     74     /*
     75      * call-seq:
     76      *     FieldDescriptor.label
     77      *
     78      * Return the label of this field.
     79      */
     80     @JRubyMethod(name = "label")
     81     public IRubyObject getLabel(ThreadContext context) {
     82         return this.label;
     83     }
     84 
     85     /*
     86      * call-seq:
     87      *     FieldDescriptor.label = label
     88      *
     89      * Sets the label on this field. Cannot be called if field is part of a message
     90      * type already in a pool.
     91      */
     92     @JRubyMethod(name = "label=")
     93     public IRubyObject setLabel(ThreadContext context, IRubyObject value) {
     94         String labelName = value.asJavaString();
     95         this.label = context.runtime.newSymbol(labelName.toLowerCase());
     96         this.builder.setLabel(
     97                 DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + labelName.toUpperCase()));
     98         return context.runtime.getNil();
     99     }
    100 
    101     /*
    102      * call-seq:
    103      *     FieldDescriptor.name => name
    104      *
    105      * Returns the name of this field as a Ruby String, or nil if it is not set.
    106      */
    107     @JRubyMethod(name = "name")
    108     public IRubyObject getName(ThreadContext context) {
    109         return this.name;
    110     }
    111 
    112     /*
    113      * call-seq:
    114      *     FieldDescriptor.name = name
    115      *
    116      * Sets the name of this field. Cannot be called once the containing message
    117      * type, if any, is added to a pool.
    118      */
    119     @JRubyMethod(name = "name=")
    120     public IRubyObject setName(ThreadContext context, IRubyObject value) {
    121         String nameStr = value.asJavaString();
    122         this.name = context.runtime.newString(nameStr);
    123         this.builder.setName(Utils.escapeIdentifier(nameStr));
    124         return context.runtime.getNil();
    125     }
    126 
    127 
    128     @JRubyMethod(name = "subtype")
    129     public IRubyObject getSubType(ThreadContext context) {
    130         return subType;
    131     }
    132 
    133     /*
    134      * call-seq:
    135      *     FieldDescriptor.type => type
    136      *
    137      * Returns this field's type, as a Ruby symbol, or nil if not yet set.
    138      *
    139      * Valid field types are:
    140      *     :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string,
    141      *     :bytes, :message.
    142      */
    143     @JRubyMethod(name = "type")
    144     public IRubyObject getType(ThreadContext context) {
    145         return Utils.fieldTypeToRuby(context, this.builder.getType());
    146     }
    147 
    148     /*
    149      * call-seq:
    150      *     FieldDescriptor.type = type
    151      *
    152      * Sets this field's type. Cannot be called if field is part of a message type
    153      * already in a pool.
    154      */
    155     @JRubyMethod(name = "type=")
    156     public IRubyObject setType(ThreadContext context, IRubyObject value) {
    157         this.builder.setType(DescriptorProtos.FieldDescriptorProto.Type.valueOf("TYPE_" + value.asJavaString().toUpperCase()));
    158         return context.runtime.getNil();
    159     }
    160 
    161     /*
    162      * call-seq:
    163      *     FieldDescriptor.number => number
    164      *
    165      * Returns this field's number, as a Ruby Integer, or nil if not yet set.
    166      *
    167      */
    168     @JRubyMethod(name = "number")
    169     public IRubyObject getnumber(ThreadContext context) {
    170         return this.number;
    171     }
    172 
    173     /*
    174      * call-seq:
    175      *     FieldDescriptor.number = number
    176      *
    177      * Sets the tag number for this field. Cannot be called if field is part of a
    178      * message type already in a pool.
    179      */
    180     @JRubyMethod(name = "number=")
    181     public IRubyObject setNumber(ThreadContext context, IRubyObject value) {
    182         this.number = value;
    183         this.builder.setNumber(RubyNumeric.num2int(value));
    184         return context.runtime.getNil();
    185     }
    186 
    187     /*
    188      * call-seq:
    189      *     FieldDescriptor.submsg_name = submsg_name
    190      *
    191      * Sets the name of the message or enum type corresponding to this field, if it
    192      * is a message or enum field (respectively). This type name will be resolved
    193      * within the context of the pool to which the containing message type is added.
    194      * Cannot be called on field that are not of message or enum type, or on fields
    195      * that are part of a message type already added to a pool.
    196      */
    197     @JRubyMethod(name = "submsg_name=")
    198     public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) {
    199         this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString()));
    200         return context.runtime.getNil();
    201     }
    202 
    203     /*
    204      * call-seq:
    205      *     FieldDescriptor.get(message) => value
    206      *
    207      * Returns the value set for this field on the given message. Raises an
    208      * exception if message is of the wrong type.
    209      */
    210     @JRubyMethod(name = "get")
    211     public IRubyObject getValue(ThreadContext context, IRubyObject msgRb) {
    212         RubyMessage message = (RubyMessage) msgRb;
    213         if (message.getDescriptor() != fieldDef.getContainingType()) {
    214             throw context.runtime.newTypeError("set method called on wrong message type");
    215         }
    216         return message.getField(context, fieldDef);
    217     }
    218 
    219     /*
    220      * call-seq:
    221      *     FieldDescriptor.set(message, value)
    222      *
    223      * Sets the value corresponding to this field to the given value on the given
    224      * message. Raises an exception if message is of the wrong type. Performs the
    225      * ordinary type-checks for field setting.
    226      */
    227     @JRubyMethod(name = "set")
    228     public IRubyObject setValue(ThreadContext context, IRubyObject msgRb, IRubyObject value) {
    229         RubyMessage message = (RubyMessage) msgRb;
    230         if (message.getDescriptor() != fieldDef.getContainingType()) {
    231             throw context.runtime.newTypeError("set method called on wrong message type");
    232         }
    233         message.setField(context, fieldDef, value);
    234         return context.runtime.getNil();
    235     }
    236 
    237     protected void setSubType(IRubyObject rubyDescriptor) {
    238         this.subType = rubyDescriptor;
    239     }
    240 
    241     protected void setFieldDef(Descriptors.FieldDescriptor fieldDescriptor) {
    242         this.fieldDef = fieldDescriptor;
    243     }
    244 
    245     protected void setOneofName(IRubyObject name) {
    246         oneofName = name;
    247     }
    248 
    249     protected void setOneofIndex(int index) {
    250         hasOneofIndex = true;
    251         oneofIndex = index;
    252     }
    253 
    254     protected IRubyObject getOneofName() {
    255         return oneofName;
    256     }
    257 
    258     protected Descriptors.FieldDescriptor getFieldDef() {
    259         return fieldDef;
    260     }
    261 
    262     protected DescriptorProtos.FieldDescriptorProto build() {
    263         if (hasOneofIndex)
    264             builder.setOneofIndex(oneofIndex);
    265         return this.builder.build();
    266     }
    267 
    268     private DescriptorProtos.FieldDescriptorProto.Builder builder;
    269     private IRubyObject name;
    270     private IRubyObject label;
    271     private IRubyObject number;
    272     private IRubyObject subType;
    273     private IRubyObject oneofName;
    274     private Descriptors.FieldDescriptor fieldDef;
    275     private int oneofIndex;
    276     private boolean hasOneofIndex = false;
    277 }
    278