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.*;
     36 import org.jruby.*;
     37 import org.jruby.anno.JRubyMethod;
     38 import org.jruby.runtime.Block;
     39 import org.jruby.runtime.Helpers;
     40 import org.jruby.runtime.ThreadContext;
     41 import org.jruby.runtime.builtin.IRubyObject;
     42 import org.jruby.util.ByteList;
     43 
     44 import java.util.HashMap;
     45 import java.util.Map;
     46 
     47 public class RubyMessage extends RubyObject {
     48     public RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor) {
     49         super(ruby, klazz);
     50         this.descriptor = descriptor;
     51     }
     52 
     53     /*
     54      * call-seq:
     55      *     Message.new(kwargs) => new_message
     56      *
     57      * Creates a new instance of the given message class. Keyword arguments may be
     58      * provided with keywords corresponding to field names.
     59      *
     60      * Note that no literal Message class exists. Only concrete classes per message
     61      * type exist, as provided by the #msgclass method on Descriptors after they
     62      * have been added to a pool. The method definitions described here on the
     63      * Message class are provided on each concrete message class.
     64      */
     65     @JRubyMethod(optional = 1)
     66     public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
     67         final Ruby runtime = context.runtime;
     68         this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
     69         this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
     70         this.builder = DynamicMessage.newBuilder(this.descriptor);
     71         this.repeatedFields = new HashMap<Descriptors.FieldDescriptor, RubyRepeatedField>();
     72         this.maps = new HashMap<Descriptors.FieldDescriptor, RubyMap>();
     73         this.fields = new HashMap<Descriptors.FieldDescriptor, IRubyObject>();
     74         this.oneofCases = new HashMap<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor>();
     75         if (args.length == 1) {
     76             if (!(args[0] instanceof RubyHash)) {
     77                 throw runtime.newArgumentError("expected Hash arguments.");
     78             }
     79             RubyHash hash = args[0].convertToHash();
     80             hash.visitAll(new RubyHash.Visitor() {
     81                 @Override
     82                 public void visit(IRubyObject key, IRubyObject value) {
     83                     if (!(key instanceof RubySymbol))
     84                         throw runtime.newTypeError("Expected symbols as hash keys in initialization map.");
     85                     final Descriptors.FieldDescriptor fieldDescriptor = findField(context, key);
     86 
     87                     if (Utils.isMapEntry(fieldDescriptor)) {
     88                         if (!(value instanceof RubyHash))
     89                             throw runtime.newArgumentError("Expected Hash object as initializer value for map field '" +  key.asJavaString() + "'.");
     90 
     91                         final RubyMap map = newMapForField(context, fieldDescriptor);
     92                         map.mergeIntoSelf(context, value);
     93                         maps.put(fieldDescriptor, map);
     94                     } else if (fieldDescriptor.isRepeated()) {
     95                         if (!(value instanceof RubyArray))
     96                             throw runtime.newArgumentError("Expected array as initializer value for repeated field '" +  key.asJavaString() + "'.");
     97                         RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, value);
     98                         addRepeatedField(fieldDescriptor, repeatedField);
     99                     } else {
    100                         Descriptors.OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
    101                         if (oneof != null) {
    102                             oneofCases.put(oneof, fieldDescriptor);
    103                         }
    104                         fields.put(fieldDescriptor, value);
    105                     }
    106 
    107                 }
    108             });
    109         }
    110         return this;
    111     }
    112 
    113     /*
    114      * call-seq:
    115      *     Message.[]=(index, value)
    116      *
    117      * Sets a field's value by field name. The provided field name should be a
    118      * string.
    119      */
    120     @JRubyMethod(name = "[]=")
    121     public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
    122         Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
    123         return setField(context, fieldDescriptor, value);
    124     }
    125 
    126     /*
    127      * call-seq:
    128      *     Message.[](index) => value
    129      *
    130      * Accesses a field's value by field name. The provided field name should be a
    131      * string.
    132      */
    133     @JRubyMethod(name = "[]")
    134     public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
    135         Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
    136         return getField(context, fieldDescriptor);
    137     }
    138 
    139     /*
    140      * call-seq:
    141      *     Message.inspect => string
    142      *
    143      * Returns a human-readable string representing this message. It will be
    144      * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
    145      * field's value is represented according to its own #inspect method.
    146      */
    147     @JRubyMethod
    148     public IRubyObject inspect() {
    149         String cname = metaClass.getName();
    150         StringBuilder sb = new StringBuilder("<");
    151         sb.append(cname);
    152         sb.append(": ");
    153         sb.append(this.layoutInspect());
    154         sb.append(">");
    155 
    156         return getRuntime().newString(sb.toString());
    157     }
    158 
    159     /*
    160      * call-seq:
    161      *     Message.hash => hash_value
    162      *
    163      * Returns a hash value that represents this message's field values.
    164      */
    165     @JRubyMethod
    166     public IRubyObject hash(ThreadContext context) {
    167         int hashCode = System.identityHashCode(this);
    168         return context.runtime.newFixnum(hashCode);
    169     }
    170 
    171     /*
    172      * call-seq:
    173      *     Message.==(other) => boolean
    174      *
    175      * Performs a deep comparison of this message with another. Messages are equal
    176      * if they have the same type and if each field is equal according to the :==
    177      * method's semantics (a more efficient comparison may actually be done if the
    178      * field is of a primitive type).
    179      */
    180     @JRubyMethod(name = "==")
    181     public IRubyObject eq(ThreadContext context, IRubyObject other) {
    182         Ruby runtime = context.runtime;
    183         if (!(other instanceof RubyMessage))
    184             return runtime.getFalse();
    185         RubyMessage message = (RubyMessage) other;
    186         if (descriptor != message.descriptor) {
    187             return runtime.getFalse();
    188         }
    189 
    190         for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
    191             IRubyObject thisVal = getField(context, fdef);
    192             IRubyObject thatVal = message.getField(context, fdef);
    193             IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
    194             if (!ret.isTrue()) {
    195                 return runtime.getFalse();
    196             }
    197         }
    198         return runtime.getTrue();
    199     }
    200 
    201     /*
    202      * call-seq:
    203      *     Message.method_missing(*args)
    204      *
    205      * Provides accessors and setters for message fields according to their field
    206      * names. For any field whose name does not conflict with a built-in method, an
    207      * accessor is provided with the same name as the field, and a setter is
    208      * provided with the name of the field plus the '=' suffix. Thus, given a
    209      * message instance 'msg' with field 'foo', the following code is valid:
    210      *
    211      *     msg.foo = 42
    212      *     puts msg.foo
    213      */
    214     @JRubyMethod(name = "method_missing", rest = true)
    215     public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
    216         if (args.length == 1) {
    217             RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
    218             IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
    219             if (oneofDescriptor.isNil()) {
    220                 if (!hasField(args[0])) {
    221                     return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
    222                 }
    223                 return index(context, args[0]);
    224             }
    225             RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
    226             Descriptors.FieldDescriptor fieldDescriptor =
    227                     oneofCases.get(rubyOneofDescriptor.getOneofDescriptor());
    228             if (fieldDescriptor == null)
    229                 return context.runtime.getNil();
    230 
    231             return context.runtime.newSymbol(fieldDescriptor.getName());
    232         } else {
    233             // fieldName is RubySymbol
    234             RubyString field = args[0].asString();
    235             RubyString equalSign = context.runtime.newString(Utils.EQUAL_SIGN);
    236             if (field.end_with_p(context, equalSign).isTrue()) {
    237                 field.chomp_bang(context, equalSign);
    238             }
    239 
    240             if (!hasField(field)) {
    241                 return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
    242             }
    243             return indexSet(context, field, args[1]);
    244         }
    245     }
    246 
    247     /**
    248      * call-seq:
    249      * Message.dup => new_message
    250      * Performs a shallow copy of this message and returns the new copy.
    251      */
    252     @JRubyMethod
    253     public IRubyObject dup(ThreadContext context) {
    254         RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
    255         IRubyObject value;
    256         for (Descriptors.FieldDescriptor fieldDescriptor : this.descriptor.getFields()) {
    257             if (fieldDescriptor.isRepeated()) {
    258                 dup.addRepeatedField(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor));
    259             } else if (fields.containsKey(fieldDescriptor)) {
    260                 dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor));
    261             } else if (this.builder.hasField(fieldDescriptor)) {
    262                 dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor)));
    263             }
    264         }
    265         for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
    266             dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor));
    267         }
    268         return dup;
    269     }
    270 
    271     /*
    272      * call-seq:
    273      *     Message.descriptor => descriptor
    274      *
    275      * Class method that returns the Descriptor instance corresponding to this
    276      * message class's type.
    277      */
    278     @JRubyMethod(name = "descriptor", meta = true)
    279     public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
    280         return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
    281     }
    282 
    283     /*
    284      * call-seq:
    285      *     MessageClass.encode(msg) => bytes
    286      *
    287      * Encodes the given message object to its serialized form in protocol buffers
    288      * wire format.
    289      */
    290     @JRubyMethod(meta = true)
    291     public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject value) {
    292         RubyMessage message = (RubyMessage) value;
    293         return context.runtime.newString(new ByteList(message.build(context).toByteArray()));
    294     }
    295 
    296     /*
    297      * call-seq:
    298      *     MessageClass.decode(data) => message
    299      *
    300      * Decodes the given data (as a string containing bytes in protocol buffers wire
    301      * format) under the interpretration given by this message class's definition
    302      * and returns a message object with the corresponding field values.
    303      */
    304     @JRubyMethod(meta = true)
    305     public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject data) {
    306         byte[] bin = data.convertToString().getBytes();
    307         RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
    308         try {
    309             ret.builder.mergeFrom(bin);
    310         } catch (InvalidProtocolBufferException e) {
    311             throw context.runtime.newRuntimeError(e.getMessage());
    312         }
    313         return ret;
    314     }
    315 
    316     /*
    317      * call-seq:
    318      *     MessageClass.encode_json(msg) => json_string
    319      *
    320      * Encodes the given message object into its serialized JSON representation.
    321      */
    322     @JRubyMethod(name = "encode_json", meta = true)
    323     public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) {
    324         RubyMessage message = (RubyMessage) msgRb;
    325         return Helpers.invoke(context, message.toHash(context), "to_json");
    326     }
    327 
    328     /*
    329      * call-seq:
    330      *     MessageClass.decode_json(data) => message
    331      *
    332      * Decodes the given data (as a string containing bytes in protocol buffers wire
    333      * format) under the interpretration given by this message class's definition
    334      * and returns a message object with the corresponding field values.
    335      */
    336     @JRubyMethod(name = "decode_json", meta = true)
    337     public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) {
    338         Ruby runtime = context.runtime;
    339         RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
    340         RubyModule jsonModule = runtime.getClassFromPath("JSON");
    341         RubyHash opts = RubyHash.newHash(runtime);
    342         opts.fastASet(runtime.newSymbol("symbolize_names"), runtime.getTrue());
    343         IRubyObject[] args = new IRubyObject[] { Helpers.invoke(context, jsonModule, "parse", json, opts) };
    344         ret.initialize(context, args);
    345         return ret;
    346     }
    347 
    348     @JRubyMethod(name = {"to_h", "to_hash"})
    349     public IRubyObject toHash(ThreadContext context) {
    350         Ruby runtime = context.runtime;
    351         RubyHash ret = RubyHash.newHash(runtime);
    352         for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
    353             IRubyObject value = getField(context, fdef);
    354             if (!value.isNil()) {
    355                 if (value.respondsTo("to_h")) {
    356                     value = Helpers.invoke(context, value, "to_h");
    357                 } else if (value.respondsTo("to_a")) {
    358                     value = Helpers.invoke(context, value, "to_a");
    359                 }
    360             }
    361             ret.fastASet(runtime.newSymbol(fdef.getName()), value);
    362         }
    363         return ret;
    364     }
    365 
    366     protected DynamicMessage build(ThreadContext context) {
    367         return build(context, 0);
    368     }
    369 
    370     protected DynamicMessage build(ThreadContext context, int depth) {
    371         if (depth > SINK_MAXIMUM_NESTING) {
    372             throw context.runtime.newRuntimeError("Maximum recursion depth exceeded during encoding.");
    373         }
    374         for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
    375             this.builder.clearField(fieldDescriptor);
    376             RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
    377             for (DynamicMessage kv : maps.get(fieldDescriptor).build(context, mapDescriptor)) {
    378                 this.builder.addRepeatedField(fieldDescriptor, kv);
    379             }
    380         }
    381         for (Descriptors.FieldDescriptor fieldDescriptor : repeatedFields.keySet()) {
    382             RubyRepeatedField repeatedField = repeatedFields.get(fieldDescriptor);
    383             this.builder.clearField(fieldDescriptor);
    384             for (int i = 0; i < repeatedField.size(); i++) {
    385                 Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth);
    386                 this.builder.addRepeatedField(fieldDescriptor, item);
    387             }
    388         }
    389         for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
    390             IRubyObject value = fields.get(fieldDescriptor);
    391             this.builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth));
    392         }
    393         return this.builder.build();
    394     }
    395 
    396     protected Descriptors.Descriptor getDescriptor() {
    397         return this.descriptor;
    398     }
    399 
    400     // Internal use only, called by Google::Protobuf.deep_copy
    401     protected IRubyObject deepCopy(ThreadContext context) {
    402         RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
    403         for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
    404             if (fdef.isRepeated()) {
    405                 copy.addRepeatedField(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
    406             } else if (fields.containsKey(fdef)) {
    407                 copy.fields.put(fdef, fields.get(fdef));
    408             } else if (this.builder.hasField(fdef)) {
    409                 copy.fields.put(fdef, wrapField(context, fdef, this.builder.getField(fdef)));
    410             }
    411         }
    412         return copy;
    413     }
    414 
    415     private RubyRepeatedField getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
    416         if (this.repeatedFields.containsKey(fieldDescriptor)) {
    417             return this.repeatedFields.get(fieldDescriptor);
    418         }
    419         int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
    420         RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
    421         for (int i = 0; i < count; i++) {
    422             ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)));
    423         }
    424         addRepeatedField(fieldDescriptor, ret);
    425         return ret;
    426     }
    427 
    428     private void addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField) {
    429         this.repeatedFields.put(fieldDescriptor, repeatedField);
    430     }
    431 
    432     private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
    433         this.builder.mergeFrom(dynamicMessage);
    434         return this;
    435     }
    436 
    437     private Descriptors.FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
    438         String nameStr = fieldName.asJavaString();
    439         Descriptors.FieldDescriptor ret = this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr));
    440         if (ret == null)
    441             throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
    442         return ret;
    443     }
    444 
    445     private boolean hasField(IRubyObject fieldName) {
    446         String nameStr = fieldName.asJavaString();
    447         return this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr)) != null;
    448     }
    449 
    450     private void checkRepeatedFieldType(ThreadContext context, IRubyObject value,
    451                                         Descriptors.FieldDescriptor fieldDescriptor) {
    452         Ruby runtime = context.runtime;
    453         if (!(value instanceof RubyRepeatedField)) {
    454             throw runtime.newTypeError("Expected repeated field array");
    455         }
    456     }
    457 
    458     // convert a ruby object to protobuf type, with type check
    459     private Object convert(ThreadContext context,
    460                            Descriptors.FieldDescriptor fieldDescriptor,
    461                            IRubyObject value, int depth) {
    462         Ruby runtime = context.runtime;
    463         Object val = null;
    464         switch (fieldDescriptor.getType()) {
    465             case INT32:
    466             case INT64:
    467             case UINT32:
    468             case UINT64:
    469                 if (!Utils.isRubyNum(value)) {
    470                     throw runtime.newTypeError("Expected number type for integral field.");
    471                 }
    472                 Utils.checkIntTypePrecision(context, fieldDescriptor.getType(), value);
    473                 switch (fieldDescriptor.getType()) {
    474                     case INT32:
    475                         val = RubyNumeric.num2int(value);
    476                         break;
    477                     case INT64:
    478                         val = RubyNumeric.num2long(value);
    479                         break;
    480                     case UINT32:
    481                         val = Utils.num2uint(value);
    482                         break;
    483                     case UINT64:
    484                         val = Utils.num2ulong(context.runtime, value);
    485                         break;
    486                     default:
    487                         break;
    488                 }
    489                 break;
    490             case FLOAT:
    491                 if (!Utils.isRubyNum(value))
    492                     throw runtime.newTypeError("Expected number type for float field.");
    493                 val = (float) RubyNumeric.num2dbl(value);
    494                 break;
    495             case DOUBLE:
    496                 if (!Utils.isRubyNum(value))
    497                     throw runtime.newTypeError("Expected number type for double field.");
    498                 val = RubyNumeric.num2dbl(value);
    499                 break;
    500             case BOOL:
    501                 if (!(value instanceof RubyBoolean))
    502                     throw runtime.newTypeError("Invalid argument for boolean field.");
    503                 val = value.isTrue();
    504                 break;
    505             case BYTES:
    506             case STRING:
    507                 Utils.validateStringEncoding(context.runtime, fieldDescriptor.getType(), value);
    508                 RubyString str = (RubyString) value;
    509                 switch (fieldDescriptor.getType()) {
    510                     case BYTES:
    511                         val = ByteString.copyFrom(str.getBytes());
    512                         break;
    513                     case STRING:
    514                         val = str.asJavaString();
    515                         break;
    516                     default:
    517                         break;
    518                 }
    519                 break;
    520             case MESSAGE:
    521                 RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
    522                 if (!value.getMetaClass().equals(typeClass))
    523                     throw runtime.newTypeError(value, "Invalid type to assign to submessage field.");
    524                 val = ((RubyMessage) value).build(context, depth + 1);
    525                 break;
    526             case ENUM:
    527                 Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
    528 
    529                 if (Utils.isRubyNum(value)) {
    530                     val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
    531                 } else if (value instanceof RubySymbol) {
    532                     val = enumDescriptor.findValueByName(value.asJavaString());
    533                 } else {
    534                     throw runtime.newTypeError("Expected number or symbol type for enum field.");
    535                 }
    536                 if (val == null) {
    537                     throw runtime.newRangeError("Enum value " + value + " is not found.");
    538                 }
    539                 break;
    540             default:
    541                 break;
    542         }
    543         return val;
    544     }
    545 
    546     private IRubyObject wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value) {
    547         if (value == null) {
    548             return context.runtime.getNil();
    549         }
    550         Ruby runtime = context.runtime;
    551         switch (fieldDescriptor.getType()) {
    552             case INT32:
    553             case INT64:
    554             case UINT32:
    555             case UINT64:
    556             case FLOAT:
    557             case DOUBLE:
    558             case BOOL:
    559             case BYTES:
    560             case STRING:
    561                 return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value);
    562             case MESSAGE:
    563                 RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
    564                 RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
    565                 return msg.buildFrom(context, (DynamicMessage) value);
    566             case ENUM:
    567                 Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor) value;
    568                 if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
    569                     return runtime.newFixnum(enumValueDescriptor.getNumber());
    570                 }
    571                 return runtime.newSymbol(enumValueDescriptor.getName());
    572             default:
    573                 return runtime.newString(value.toString());
    574         }
    575     }
    576 
    577     private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context,
    578                                                               Descriptors.FieldDescriptor fieldDescriptor) {
    579         IRubyObject typeClass = context.runtime.getNilClass();
    580 
    581         IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
    582         Descriptors.FieldDescriptor.Type type = fieldDescriptor.getType();
    583         if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
    584             typeClass = ((RubyDescriptor) descriptor).msgclass(context);
    585 
    586         } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
    587             typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
    588         }
    589         return new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
    590     }
    591 
    592     protected IRubyObject getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
    593         Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
    594         if (oneofDescriptor != null) {
    595             if (oneofCases.containsKey(oneofDescriptor)) {
    596                 if (oneofCases.get(oneofDescriptor) != fieldDescriptor)
    597                     return context.runtime.getNil();
    598                 return fields.get(fieldDescriptor);
    599             } else {
    600                 Descriptors.FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
    601                 if (oneofCase != fieldDescriptor) return context.runtime.getNil();
    602                 IRubyObject value = wrapField(context, oneofCase, builder.getField(oneofCase));
    603                 fields.put(fieldDescriptor, value);
    604                 return value;
    605             }
    606         }
    607 
    608         if (Utils.isMapEntry(fieldDescriptor)) {
    609             RubyMap map = maps.get(fieldDescriptor);
    610             if (map == null) {
    611                 map = newMapForField(context, fieldDescriptor);
    612                 int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
    613                 Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
    614                 Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
    615                 RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
    616                 RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
    617                 for (int i = 0; i < mapSize; i++) {
    618                     RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
    619                     DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
    620                     kvMessage.buildFrom(context, message);
    621                     map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField));
    622                 }
    623                 maps.put(fieldDescriptor, map);
    624             }
    625             return map;
    626         }
    627         if (fieldDescriptor.isRepeated()) {
    628             return getRepeatedField(context, fieldDescriptor);
    629         }
    630         if (fieldDescriptor.getType() != Descriptors.FieldDescriptor.Type.MESSAGE ||
    631                 this.builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) {
    632             if (fields.containsKey(fieldDescriptor)) {
    633                 return fields.get(fieldDescriptor);
    634             } else {
    635                 IRubyObject value = wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor));
    636                 if (this.builder.hasField(fieldDescriptor)) {
    637                     fields.put(fieldDescriptor, value);
    638                 }
    639                 return value;
    640             }
    641         }
    642         return context.runtime.getNil();
    643     }
    644 
    645     protected IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
    646         if (Utils.isMapEntry(fieldDescriptor)) {
    647             if (!(value instanceof RubyMap)) {
    648                 throw context.runtime.newTypeError("Expected Map instance");
    649             }
    650             RubyMap thisMap = (RubyMap) getField(context, fieldDescriptor);
    651             thisMap.mergeIntoSelf(context, value);
    652         } else if (fieldDescriptor.isRepeated()) {
    653             checkRepeatedFieldType(context, value, fieldDescriptor);
    654             if (value instanceof RubyRepeatedField) {
    655                 addRepeatedField(fieldDescriptor, (RubyRepeatedField) value);
    656             } else {
    657                 RubyArray ary = value.convertToArray();
    658                 RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, ary);
    659                 addRepeatedField(fieldDescriptor, repeatedField);
    660             }
    661         } else {
    662             Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
    663             if (oneofDescriptor != null) {
    664                 Descriptors.FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
    665                 if (oneofCase != null && oneofCase != fieldDescriptor) {
    666                     fields.remove(oneofCase);
    667                 }
    668                 if (value.isNil()) {
    669                     oneofCases.remove(oneofDescriptor);
    670                     fields.remove(fieldDescriptor);
    671                 } else {
    672                     oneofCases.put(oneofDescriptor, fieldDescriptor);
    673                     fields.put(fieldDescriptor, value);
    674                 }
    675             } else {
    676                 Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType();
    677                 IRubyObject typeClass = context.runtime.getObject();
    678                 boolean addValue = true;
    679                 if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
    680                     typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
    681                     if (value.isNil()){
    682                         addValue = false;
    683                     }
    684                 } else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
    685                     typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context);
    686                     Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
    687                     if (Utils.isRubyNum(value)) {
    688                         Descriptors.EnumValueDescriptor val =
    689                                 enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
    690                         if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName());
    691                     }
    692                 }
    693                 if (addValue) {
    694                     Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
    695                     this.fields.put(fieldDescriptor, value);
    696                 } else {
    697                     this.fields.remove(fieldDescriptor);
    698                 }
    699             }
    700         }
    701         return context.runtime.getNil();
    702     }
    703 
    704     private String layoutInspect() {
    705         ThreadContext context = getRuntime().getCurrentContext();
    706         StringBuilder sb = new StringBuilder();
    707         for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
    708             sb.append(Utils.unescapeIdentifier(fdef.getName()));
    709             sb.append(": ");
    710             sb.append(getField(context, fdef).inspect());
    711             sb.append(", ");
    712         }
    713         return sb.substring(0, sb.length() - 2);
    714     }
    715 
    716     private IRubyObject getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
    717         RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
    718         return thisRbDescriptor.lookup(fieldDescriptor.getName()).getSubType(context);
    719     }
    720 
    721     private RubyRepeatedField rubyToRepeatedField(ThreadContext context,
    722                                                   Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
    723         RubyArray arr = value.convertToArray();
    724         RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
    725         for (int i = 0; i < arr.size(); i++) {
    726             repeatedField.push(context, arr.eltInternal(i));
    727         }
    728         return repeatedField;
    729     }
    730 
    731     private RubyMap newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
    732         RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
    733         Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
    734         Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
    735         IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
    736         IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
    737         if (valueField.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
    738             RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
    739                     context.runtime.newString("value"));
    740             RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubType(context);
    741             return (RubyMap) cMap.newInstance(context, keyType, valueType,
    742                     rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
    743         } else {
    744             return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
    745         }
    746     }
    747 
    748     private Descriptors.FieldDescriptor getOneofCase(Descriptors.OneofDescriptor oneof) {
    749         if (oneofCases.containsKey(oneof)) {
    750             return oneofCases.get(oneof);
    751         }
    752         return builder.getOneofFieldDescriptor(oneof);
    753     }
    754 
    755     private Descriptors.Descriptor descriptor;
    756     private DynamicMessage.Builder builder;
    757     private RubyClass cRepeatedField;
    758     private RubyClass cMap;
    759     private Map<Descriptors.FieldDescriptor, RubyRepeatedField> repeatedFields;
    760     private Map<Descriptors.FieldDescriptor, RubyMap> maps;
    761     private Map<Descriptors.FieldDescriptor, IRubyObject> fields;
    762     private Map<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor> oneofCases;
    763 
    764     private static final int SINK_MAXIMUM_NESTING = 64;
    765 }
    766