33 package com.google.protobuf.jruby;
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;
44 import java.security.MessageDigest;
45 import java.security.NoSuchAlgorithmException;
46 import java.util.HashMap;
67 @JRubyMethod(optional = 1)
68 public IRubyObject
initialize(final ThreadContext context, IRubyObject[]
args) {
69 final Ruby runtime = context.runtime;
70 this.
cRepeatedField = (RubyClass) runtime.getClassFromPath(
"Google::Protobuf::RepeatedField");
71 this.
cMap = (RubyClass) runtime.getClassFromPath(
"Google::Protobuf::Map");
77 if (
args.length == 1) {
78 if (!(
args[0] instanceof RubyHash)) {
79 throw runtime.newArgumentError(
"expected Hash arguments.");
81 RubyHash
hash =
args[0].convertToHash();
82 hash.visitAll(
new RubyHash.Visitor() {
85 if (!(
key instanceof RubySymbol) && !(
key instanceof RubyString))
86 throw runtime.newTypeError(
"Expected string or symbols as hash keys in initialization map.");
89 if (
value.isNil())
return;
92 if (!(
value instanceof RubyHash))
93 throw runtime.newArgumentError(
"Expected Hash object as initializer value for map field '" +
key.asJavaString() +
"'.");
98 }
else if (fieldDescriptor.isRepeated()) {
99 if (!(
value instanceof RubyArray))
100 throw runtime.newArgumentError(
"Expected array as initializer value for repeated field '" +
key.asJavaString() +
"'.");
111 RubyClass typeClass = (RubyClass)
descriptor.msgclass(context);
112 value = (IRubyObject) typeClass.newInstance(context,
value, Block.NULL_BLOCK);
130 @JRubyMethod(
name =
"[]=")
131 public IRubyObject
indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject
value) {
143 @JRubyMethod(
name =
"[]")
144 public IRubyObject
index(ThreadContext context, IRubyObject fieldName) {
146 return getField(context, fieldDescriptor);
159 String cname = metaClass.getName();
160 StringBuilder
sb =
new StringBuilder(
"<");
166 return getRuntime().newString(
sb.toString());
176 public IRubyObject
hash(ThreadContext context) {
178 MessageDigest digest = MessageDigest.getInstance(
"SHA-256");
180 digest.update((
byte)
map.hashCode());
186 digest.update((
byte)
field.hashCode());
188 return context.runtime.newString(
new ByteList(digest.digest()));
189 }
catch (NoSuchAlgorithmException ignore) {
190 return context.runtime.newFixnum(System.identityHashCode(
this));
203 @JRubyMethod(
name =
"==")
204 public IRubyObject
eq(ThreadContext context, IRubyObject other) {
205 Ruby runtime = context.runtime;
207 return runtime.getFalse();
210 return runtime.getFalse();
214 IRubyObject thisVal =
getField(context, fdef);
215 IRubyObject thatVal =
message.getField(context, fdef);
216 IRubyObject ret = thisVal.callMethod(context,
"==", thatVal);
218 return runtime.getFalse();
221 return runtime.getTrue();
237 @JRubyMethod(
name =
"method_missing", rest =
true)
239 if (
args.length == 1) {
241 IRubyObject oneofDescriptor = rubyDescriptor.
lookupOneof(context,
args[0]);
242 if (oneofDescriptor.isNil()) {
244 return Helpers.invokeSuper(context,
this, metaClass,
"method_missing",
args, Block.NULL_BLOCK);
251 if (fieldDescriptor ==
null)
252 return context.runtime.getNil();
254 return context.runtime.newSymbol(fieldDescriptor.getName());
259 if (
field.end_with_p(context, equalSign).isTrue()) {
260 field.chomp_bang(context, equalSign);
264 return Helpers.invokeSuper(context,
this, metaClass,
"method_missing",
args, Block.NULL_BLOCK);
276 public IRubyObject
dup(ThreadContext context) {
280 if (fieldDescriptor.isRepeated()) {
282 }
else if (
fields.containsKey(fieldDescriptor)) {
283 dup.fields.put(fieldDescriptor,
fields.get(fieldDescriptor));
289 dup.maps.put(fieldDescriptor,
maps.get(fieldDescriptor));
301 @JRubyMethod(
name =
"descriptor", meta =
true)
302 public static IRubyObject
getDescriptor(ThreadContext context, IRubyObject recv) {
313 @JRubyMethod(meta =
true)
314 public static IRubyObject
encode(ThreadContext context, IRubyObject recv, IRubyObject
value) {
316 return context.runtime.newString(
new ByteList(
message.build(context).toByteArray()));
327 @JRubyMethod(meta =
true)
328 public static IRubyObject
decode(ThreadContext context, IRubyObject recv, IRubyObject
data) {
329 byte[] bin =
data.convertToString().getBytes();
334 throw context.runtime.newRuntimeError(e.getMessage());
345 @JRubyMethod(
name =
"encode_json", meta =
true)
346 public static IRubyObject
encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) {
348 return Helpers.invoke(context,
message.toHash(context),
"to_json");
359 @JRubyMethod(
name =
"decode_json", meta =
true)
360 public static IRubyObject
decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) {
361 Ruby runtime = context.runtime;
363 RubyModule jsonModule = runtime.getClassFromPath(
"JSON");
364 RubyHash opts = RubyHash.newHash(runtime);
365 opts.fastASet(runtime.newSymbol(
"symbolize_names"), runtime.getTrue());
366 IRubyObject[]
args =
new IRubyObject[] { Helpers.invoke(context, jsonModule,
"parse", json, opts) };
371 @JRubyMethod(
name =
"to_h")
372 public IRubyObject
toHash(ThreadContext context) {
373 Ruby runtime = context.runtime;
374 RubyHash ret = RubyHash.newHash(runtime);
377 if (!
value.isNil()) {
378 if (fdef.isRepeated() && !fdef.isMapField()) {
380 value = Helpers.invoke(context,
value,
"to_a");
382 RubyArray ary =
value.convertToArray();
383 for (
int i = 0;
i < ary.size();
i++) {
384 IRubyObject submsg = Helpers.invoke(context, ary.eltInternal(
i),
"to_h");
385 ary.eltInternalSet(
i, submsg);
388 value = ary.to_ary();
390 }
else if (
value.respondsTo(
"to_h")) {
391 value = Helpers.invoke(context,
value,
"to_h");
392 }
else if (
value.respondsTo(
"to_a")) {
393 value = Helpers.invoke(context,
value,
"to_a");
396 ret.fastASet(runtime.newSymbol(fdef.getName()),
value);
402 return build(context, 0);
407 throw context.runtime.newRuntimeError(
"Maximum recursion depth exceeded during encoding.");
410 this.
builder.clearField(fieldDescriptor);
413 this.
builder.addRepeatedField(fieldDescriptor, kv);
418 this.
builder.clearField(fieldDescriptor);
419 for (
int i = 0;
i < repeatedField.
size();
i++) {
421 this.
builder.addRepeatedField(fieldDescriptor,
item);
436 protected IRubyObject
deepCopy(ThreadContext context) {
439 if (fdef.isRepeated()) {
441 }
else if (
fields.containsKey(fdef)) {
468 this.
builder.mergeFrom(dynamicMessage);
473 String nameStr = fieldName.asJavaString();
476 throw context.runtime.newArgumentError(
"field " + fieldName.asJavaString() +
" is not found");
481 String nameStr = fieldName.asJavaString();
487 Ruby runtime = context.runtime;
489 throw runtime.newTypeError(
"Expected repeated field array");
497 Ruby runtime = context.runtime;
499 switch (fieldDescriptor.getType()) {
505 throw runtime.newTypeError(
"Expected number type for integral field.");
508 switch (fieldDescriptor.getType()) {
527 throw runtime.newTypeError(
"Expected number type for float field.");
528 val = (float) RubyNumeric.num2dbl(
value);
532 throw runtime.newTypeError(
"Expected number type for double field.");
536 if (!(
value instanceof RubyBoolean))
537 throw runtime.newTypeError(
"Invalid argument for boolean field.");
546 val = ((RubyString)
value).asJavaString();
550 if (!
value.getMetaClass().equals(typeClass))
551 throw runtime.newTypeError(
value,
"Invalid type to assign to submessage field.");
559 }
else if (
value instanceof RubySymbol ||
value instanceof RubyString) {
560 val = enumDescriptor.findValueByName(
value.asJavaString());
562 throw runtime.newTypeError(
"Expected number or symbol type for enum field.");
565 throw runtime.newRangeError(
"Enum value " +
value +
" is not found.");
576 return context.runtime.getNil();
578 Ruby runtime = context.runtime;
579 switch (fieldDescriptor.getType()) {
596 if (enumValueDescriptor.getIndex() == -1) {
597 return runtime.newFixnum(enumValueDescriptor.getNumber());
599 return runtime.newSymbol(enumValueDescriptor.getName());
601 return runtime.newString(
value.toString());
607 IRubyObject typeClass = context.runtime.getNilClass();
622 if (oneofDescriptor !=
null) {
623 if (
oneofCases.get(oneofDescriptor) == fieldDescriptor) {
624 return fields.get(fieldDescriptor);
627 if (oneofCase != fieldDescriptor) {
629 return context.runtime.getNil();
631 return wrapField(context, fieldDescriptor, fieldDescriptor.getDefaultValue());
648 RubyClass kvClass = (RubyClass) kvDescriptor.
msgclass(context);
649 for (
int i = 0;
i < mapSize;
i++) {
653 map.indexSet(context, kvMessage.
getField(context, keyField), kvMessage.
getField(context, valueField));
655 maps.put(fieldDescriptor,
map);
659 if (fieldDescriptor.isRepeated()) {
663 this.builder.hasField(fieldDescriptor) ||
fields.containsKey(fieldDescriptor)) {
664 if (
fields.containsKey(fieldDescriptor)) {
665 return fields.get(fieldDescriptor);
674 return context.runtime.getNil();
680 throw context.runtime.newTypeError(
"Expected Map instance");
684 }
else if (fieldDescriptor.isRepeated()) {
689 RubyArray ary =
value.convertToArray();
695 if (oneofDescriptor !=
null) {
697 if (oneofCase !=
null && oneofCase != fieldDescriptor) {
702 fields.remove(fieldDescriptor);
704 oneofCases.put(oneofDescriptor, fieldDescriptor);
709 IRubyObject typeClass = context.runtime.getObject();
710 boolean addValue =
true;
721 enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(
value));
722 if (
val.getIndex() != -1)
value = context.runtime.newSymbol(
val.getName());
729 this.
fields.remove(fieldDescriptor);
733 return context.runtime.getNil();
737 ThreadContext context = getRuntime().getCurrentContext();
738 StringBuilder
sb =
new StringBuilder();
745 return sb.substring(0,
sb.length() - 2);
750 return thisRbDescriptor.
lookup(fieldDescriptor.getName()).getSubType(context);
755 RubyArray arr =
value.convertToArray();
758 RubyClass typeClass =
null;
761 typeClass = (RubyClass)
descriptor.msgclass(context);
764 for (
int i = 0;
i < arr.size();
i++) {
765 IRubyObject row = arr.eltInternal(
i);
766 if (row instanceof RubyHash && typeClass !=
null) {
767 row = (IRubyObject) typeClass.newInstance(context, row, Block.NULL_BLOCK);
770 repeatedField.
push(context, row);
772 return repeatedField;
779 IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
780 IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
783 context.runtime.newString(
"value"));
785 return (
RubyMap)
cMap.newInstance(context, keyType, valueType,
786 rubyDescriptor.
msgclass(context), Block.NULL_BLOCK);
788 return (
RubyMap)
cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);