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 { createRubyFileDescriptor(Ruby runtime)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 RubyFieldDescriptor(Ruby runtime, RubyClass klazz)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 initialize(ThreadContext context)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") getLabel(ThreadContext context)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=") setLabel(ThreadContext context, IRubyObject value)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") getName(ThreadContext context)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=") setName(ThreadContext context, IRubyObject value)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") getSubType(ThreadContext context)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") getType(ThreadContext context)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=") setType(ThreadContext context, IRubyObject value)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") getnumber(ThreadContext context)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=") setNumber(ThreadContext context, IRubyObject value)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=") setSubmsgName(ThreadContext context, IRubyObject 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") getValue(ThreadContext context, IRubyObject msgRb)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") setValue(ThreadContext context, IRubyObject msgRb, IRubyObject value)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 setSubType(IRubyObject rubyDescriptor)237 protected void setSubType(IRubyObject rubyDescriptor) { 238 this.subType = rubyDescriptor; 239 } 240 setFieldDef(Descriptors.FieldDescriptor fieldDescriptor)241 protected void setFieldDef(Descriptors.FieldDescriptor fieldDescriptor) { 242 this.fieldDef = fieldDescriptor; 243 } 244 setOneofName(IRubyObject name)245 protected void setOneofName(IRubyObject name) { 246 oneofName = name; 247 } 248 setOneofIndex(int index)249 protected void setOneofIndex(int index) { 250 hasOneofIndex = true; 251 oneofIndex = index; 252 } 253 getOneofName()254 protected IRubyObject getOneofName() { 255 return oneofName; 256 } 257 getFieldDef()258 protected Descriptors.FieldDescriptor getFieldDef() { 259 return fieldDef; 260 } 261 build()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