/* * 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.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; @JRubyClass(name = "FieldDescriptor") public class RubyFieldDescriptor extends RubyObject { public static void createRubyFileDescriptor(Ruby runtime) { RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf"); RubyClass cFieldDescriptor = mProtobuf.defineClassUnder("FieldDescriptor", runtime.getObject(), new ObjectAllocator() { @Override public IRubyObject allocate(Ruby runtime, RubyClass klazz) { return new RubyFieldDescriptor(runtime, klazz); } }); cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class); } public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) { super(runtime, klazz); } /* * call-seq: * FieldDescriptor.new => field * * Returns a new field descriptor. Its name, type, etc. must be set before it is * added to a message type. */ @JRubyMethod public IRubyObject initialize(ThreadContext context) { builder = DescriptorProtos.FieldDescriptorProto.newBuilder(); 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 * * Sets the label on this field. Cannot be called if field is part of a message * type already in a pool. */ @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_" + labelName.toUpperCase())); return context.runtime.getNil(); } /* * call-seq: * FieldDescriptor.name => name * * 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; } /* * call-seq: * FieldDescriptor.name = name * * Sets the name of this field. Cannot be called once the containing message * type, if any, is added to a pool. */ @JRubyMethod(name = "name=") public IRubyObject setName(ThreadContext context, IRubyObject value) { String nameStr = value.asJavaString(); this.name = context.runtime.newString(nameStr); this.builder.setName(Utils.escapeIdentifier(nameStr)); return context.runtime.getNil(); } @JRubyMethod(name = "subtype") public IRubyObject getSubType(ThreadContext context) { return subType; } /* * call-seq: * FieldDescriptor.type => type * * Returns this field's type, as a Ruby symbol, or nil if not yet set. * * Valid field types are: * :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string, * :bytes, :message. */ @JRubyMethod(name = "type") public IRubyObject getType(ThreadContext context) { return Utils.fieldTypeToRuby(context, this.builder.getType()); } /* * call-seq: * FieldDescriptor.type = type * * Sets this field's type. Cannot be called if field is part of a message type * already in a pool. */ @JRubyMethod(name = "type=") public IRubyObject setType(ThreadContext context, IRubyObject value) { this.builder.setType(DescriptorProtos.FieldDescriptorProto.Type.valueOf("TYPE_" + value.asJavaString().toUpperCase())); 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 * * Sets the tag number for this field. Cannot be called if field is part of a * message type already in a pool. */ @JRubyMethod(name = "number=") public IRubyObject setNumber(ThreadContext context, IRubyObject value) { this.number = value; this.builder.setNumber(RubyNumeric.num2int(value)); return context.runtime.getNil(); } /* * call-seq: * FieldDescriptor.submsg_name = submsg_name * * Sets the name of the message or enum type corresponding to this field, if it * is a message or enum field (respectively). This type name will be resolved * within the context of the pool to which the containing message type is added. * Cannot be called on field that are not of message or enum type, or on fields * that are part of a message type already added to a pool. */ @JRubyMethod(name = "submsg_name=") public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) { this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString())); return context.runtime.getNil(); } /* * call-seq: * FieldDescriptor.get(message) => value * * Returns the value set for this field on the given message. Raises an * exception if message is of the wrong type. */ @JRubyMethod(name = "get") public IRubyObject getValue(ThreadContext context, IRubyObject msgRb) { RubyMessage message = (RubyMessage) msgRb; if (message.getDescriptor() != fieldDef.getContainingType()) { throw context.runtime.newTypeError("set method called on wrong message type"); } return message.getField(context, fieldDef); } /* * call-seq: * FieldDescriptor.set(message, value) * * Sets the value corresponding to this field to the given value on the given * message. Raises an exception if message is of the wrong type. Performs the * ordinary type-checks for field setting. */ @JRubyMethod(name = "set") public IRubyObject setValue(ThreadContext context, IRubyObject msgRb, IRubyObject value) { RubyMessage message = (RubyMessage) msgRb; if (message.getDescriptor() != fieldDef.getContainingType()) { throw context.runtime.newTypeError("set method called on wrong message type"); } message.setField(context, fieldDef, value); return context.runtime.getNil(); } protected void setSubType(IRubyObject rubyDescriptor) { this.subType = rubyDescriptor; } protected void setFieldDef(Descriptors.FieldDescriptor fieldDescriptor) { this.fieldDef = fieldDescriptor; } protected void setOneofName(IRubyObject name) { oneofName = name; } protected void setOneofIndex(int index) { hasOneofIndex = true; oneofIndex = index; } protected IRubyObject getOneofName() { return oneofName; } protected Descriptors.FieldDescriptor getFieldDef() { return fieldDef; } protected DescriptorProtos.FieldDescriptorProto build() { if (hasOneofIndex) builder.setOneofIndex(oneofIndex); return this.builder.build(); } private DescriptorProtos.FieldDescriptorProto.Builder builder; private IRubyObject name; private IRubyObject label; private IRubyObject number; private IRubyObject subType; private IRubyObject oneofName; private Descriptors.FieldDescriptor fieldDef; private int oneofIndex; private boolean hasOneofIndex = false; }