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.Ruby;
     38 import org.jruby.RubyClass;
     39 import org.jruby.RubyModule;
     40 import org.jruby.RubyObject;
     41 import org.jruby.RubyNumeric;
     42 import org.jruby.anno.JRubyClass;
     43 import org.jruby.anno.JRubyMethod;
     44 import org.jruby.runtime.Block;
     45 import org.jruby.runtime.ObjectAllocator;
     46 import org.jruby.runtime.ThreadContext;
     47 import org.jruby.runtime.builtin.IRubyObject;
     48 
     49 @JRubyClass(name = "EnumDescriptor", include = "Enumerable")
     50 public class RubyEnumDescriptor extends RubyObject {
     51     public static void createRubyEnumDescriptor(Ruby runtime) {
     52         RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
     53         RubyClass cEnumDescriptor = mProtobuf.defineClassUnder("EnumDescriptor", runtime.getObject(), new ObjectAllocator() {
     54             @Override
     55             public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
     56                 return new RubyEnumDescriptor(runtime, klazz);
     57             }
     58         });
     59         cEnumDescriptor.includeModule(runtime.getEnumerable());
     60         cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class);
     61     }
     62 
     63     public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) {
     64         super(runtime, klazz);
     65     }
     66 
     67     /*
     68      * call-seq:
     69      *     EnumDescriptor.new => enum_descriptor
     70      *
     71      * Creates a new, empty, enum descriptor. Must be added to a pool before the
     72      * enum type can be used. The enum type may only be modified prior to adding to
     73      * a pool.
     74      */
     75     @JRubyMethod
     76     public IRubyObject initialize(ThreadContext context) {
     77         this.builder = DescriptorProtos.EnumDescriptorProto.newBuilder();
     78         return this;
     79     }
     80 
     81     /*
     82      * call-seq:
     83      *     EnumDescriptor.name => name
     84      *
     85      * Returns the name of this enum type.
     86      */
     87     @JRubyMethod(name = "name")
     88     public IRubyObject getName(ThreadContext context) {
     89         return this.name;
     90     }
     91 
     92     /*
     93      * call-seq:
     94      *     EnumDescriptor.name = name
     95      *
     96      * Sets the name of this enum type. Cannot be called if the enum type has
     97      * already been added to a pool.
     98      */
     99     @JRubyMethod(name = "name=")
    100     public IRubyObject setName(ThreadContext context, IRubyObject name) {
    101         this.name = name;
    102         this.builder.setName(Utils.escapeIdentifier(name.asJavaString()));
    103         return context.runtime.getNil();
    104     }
    105 
    106     /*
    107      * call-seq:
    108      *     EnumDescriptor.add_value(key, value)
    109      *
    110      * Adds a new key => value mapping to this enum type. Key must be given as a
    111      * Ruby symbol. Cannot be called if the enum type has already been added to a
    112      * pool. Will raise an exception if the key or value is already in use.
    113      */
    114     @JRubyMethod(name = "add_value")
    115     public IRubyObject addValue(ThreadContext context, IRubyObject name, IRubyObject number) {
    116         DescriptorProtos.EnumValueDescriptorProto.Builder valueBuilder = DescriptorProtos.EnumValueDescriptorProto.newBuilder();
    117         valueBuilder.setName(name.asJavaString());
    118         valueBuilder.setNumber(RubyNumeric.num2int(number));
    119         this.builder.addValue(valueBuilder);
    120         return context.runtime.getNil();
    121     }
    122 
    123     /*
    124      * call-seq:
    125      *     EnumDescriptor.each(&block)
    126      *
    127      * Iterates over key => value mappings in this enum's definition, yielding to
    128      * the block with (key, value) arguments for each one.
    129      */
    130     @JRubyMethod
    131     public IRubyObject each(ThreadContext context, Block block) {
    132         Ruby runtime = context.runtime;
    133         for (Descriptors.EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) {
    134             block.yield(context, runtime.newArray(runtime.newSymbol(enumValueDescriptor.getName()),
    135                     runtime.newFixnum(enumValueDescriptor.getNumber())));
    136         }
    137         return runtime.getNil();
    138     }
    139 
    140     /*
    141      * call-seq:
    142      *     EnumDescriptor.enummodule => module
    143      *
    144      * Returns the Ruby module corresponding to this enum type. Cannot be called
    145      * until the enum descriptor has been added to a pool.
    146      */
    147     @JRubyMethod
    148     public IRubyObject enummodule(ThreadContext context) {
    149         if (this.klazz == null) {
    150             this.klazz = buildModuleFromDescriptor(context);
    151         }
    152         return this.klazz;
    153     }
    154 
    155     public void setDescriptor(Descriptors.EnumDescriptor descriptor) {
    156         this.descriptor = descriptor;
    157     }
    158 
    159     public Descriptors.EnumDescriptor getDescriptor() {
    160         return this.descriptor;
    161     }
    162 
    163     public DescriptorProtos.EnumDescriptorProto.Builder getBuilder() {
    164         return this.builder;
    165     }
    166 
    167     private RubyModule buildModuleFromDescriptor(ThreadContext context) {
    168         Ruby runtime = context.runtime;
    169         Utils.checkNameAvailability(context, name.asJavaString());
    170 
    171         RubyModule enumModule = RubyModule.newModule(runtime);
    172         for (Descriptors.EnumValueDescriptor value : descriptor.getValues()) {
    173             enumModule.defineConstant(value.getName(), runtime.newFixnum(value.getNumber()));
    174         }
    175 
    176         enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
    177         enumModule.defineAnnotatedMethods(RubyEnum.class);
    178         return enumModule;
    179     }
    180 
    181     private IRubyObject name;
    182     private RubyModule klazz;
    183     private Descriptors.EnumDescriptor descriptor;
    184     private DescriptorProtos.EnumDescriptorProto.Builder builder;
    185 }
    186