add jruby support by protobuf-java reflection API

changes/89/218689/1
Isaiah Peng 10 years ago
parent a5f7bb8ebb
commit 27e2b57830
  1. 6
      ruby/.gitignore
  2. 3
      ruby/Gemfile
  3. 25
      ruby/Gemfile.lock
  4. 17
      ruby/README.md
  5. 26
      ruby/Rakefile
  6. 27
      ruby/google-protobuf.gemspec
  7. 7
      ruby/lib/google/protobuf.rb
  8. 84
      ruby/pom.xml
  9. 167
      ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java
  10. 267
      ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
  11. 169
      ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
  12. 86
      ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
  13. 82
      ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java
  14. 185
      ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
  15. 248
      ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
  16. 434
      ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
  17. 744
      ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
  18. 217
      ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java
  19. 84
      ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java
  20. 124
      ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
  21. 118
      ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
  22. 391
      ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
  23. 776
      ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
  24. 300
      ruby/src/main/java/com/google/protobuf/jruby/Utils.java
  25. 60
      ruby/src/main/java/google/ProtobufJavaService.java
  26. 15
      ruby/src/main/sentinel.proto
  27. 2
      ruby/tests/basic.rb
  28. 2
      ruby/tests/stress.rb

6
ruby/.gitignore vendored

@ -0,0 +1,6 @@
*.bundle
tags
.idea/
lib/google/protobuf_java.jar
protobuf-jruby.iml
target/

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

@ -0,0 +1,25 @@
PATH
remote: .
specs:
google-protobuf (3.0.0.alpha.2)
GEM
remote: https://rubygems.org/
specs:
power_assert (0.2.2)
rake (10.4.2)
rake-compiler (0.9.5)
rake
rubygems-tasks (0.2.4)
test-unit (3.0.9)
power_assert
PLATFORMS
java
ruby
DEPENDENCIES
google-protobuf!
rake-compiler
rubygems-tasks
test-unit

@ -60,15 +60,28 @@ To build this Ruby extension, you will need:
* Ruby development headers * Ruby development headers
* a C compiler * a C compiler
First, install the required Ruby gems: To Build the JRuby extension, you will need:
$ sudo gem install bundler rake rake-compiler rspec rubygems-tasks * Maven
* The latest version of the protobuf java library
* Install JRuby via rbenv or RVM
First switch to the desired platform with rbenv or RVM.
Then install the required Ruby gems:
$ gem install bundler
$ bundle
Then build the Gem: Then build the Gem:
$ rake gem $ rake gem
$ gem install pkg/protobuf-$VERSION.gem $ gem install pkg/protobuf-$VERSION.gem
To run the specs:
$ rake test
This gem includes the upb parsing and serialization library as a single-file This gem includes the upb parsing and serialization library as a single-file
amalgamation. It is up-to-date with upb git commit amalgamation. It is up-to-date with upb git commit
`535bc2fe2f2b467f59347ffc9449e11e47791257`. `535bc2fe2f2b467f59347ffc9449e11e47791257`.

@ -1,20 +1,32 @@
require "rake/extensiontask" require "rubygems"
require "rubygems/package_task"
require "rake/extensiontask" unless RUBY_PLATFORM == "java"
require "rake/testtask" require "rake/testtask"
spec = Gem::Specification.load("google-protobuf.gemspec") spec = Gem::Specification.load("google-protobuf.gemspec")
Rake::ExtensionTask.new("protobuf_c", spec) do |ext| if RUBY_PLATFORM == "java"
ext.ext_dir = "ext/google/protobuf_c" task :clean do
ext.lib_dir = "lib/google" system("mvn clean")
end end
Rake::TestTask.new(:test => :build) do |t| task :compile do
t.test_files = FileList["tests/*.rb"] system("mvn package")
end
else
Rake::ExtensionTask.new("protobuf_c", spec) do |ext|
ext.ext_dir = "ext/google/protobuf_c"
ext.lib_dir = "lib/google"
end
end end
Gem::PackageTask.new(spec) do |pkg| Gem::PackageTask.new(spec) do |pkg|
end end
Rake::TestTask.new(:test => :build) do |t|
t.test_files = FileList["tests/*.rb"]
end
task :build => [:clean, :compile] task :build => [:clean, :compile]
task :default => [:build] task :default => [:build]

@ -1,10 +1,3 @@
class << Gem::Specification
def find_c_source(dir)
`cd #{dir}; git ls-files "*.c" "*.h" extconf.rb Makefile`.split
.map{|f| "#{dir}/#{f}"}
end
end
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "google-protobuf" s.name = "google-protobuf"
s.version = "3.0.0.alpha.3.0.pre" s.version = "3.0.0.alpha.3.0.pre"
@ -14,11 +7,17 @@ Gem::Specification.new do |s|
s.authors = ["Protobuf Authors"] s.authors = ["Protobuf Authors"]
s.email = "protobuf@googlegroups.com" s.email = "protobuf@googlegroups.com"
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.extensions = ["ext/google/protobuf_c/extconf.rb"] s.files = ["lib/google/protobuf.rb"]
s.files = ["lib/google/protobuf.rb"] + unless RUBY_PLATFORM == "java"
# extension C source s.files += `git ls-files "*.c" "*.h" extconf.rb Makefile`.split
find_c_source("ext/google/protobuf_c") s.extensions= ["ext/google/protobuf_c/extconf.rb"]
s.test_files = ["tests/basic.rb", else
"tests/stress.rb", s.files += ["lib/google/protobuf_java.jar"]
"tests/generated_code_test.rb"] end
s.test_files = ["tests/basic.rb",
"tests/stress.rb",
"tests/generated_code_test.rb"]
s.add_development_dependency "rake-compiler"
s.add_development_dependency "test-unit"
s.add_development_dependency "rubygems-tasks"
end end

@ -28,4 +28,9 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'google/protobuf_c' if RUBY_PLATFORM == "java"
require 'json'
require 'google/protobuf_java'
else
require 'google/protobuf_c'
end

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
<version>1</version>
</parent>
<groupId>com.google.protobuf.jruby</groupId>
<artifactId>protobuf-jruby</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Protocol Buffer JRuby native extension</name>
<description>
Protocol Buffers are a way of encoding structured data in an efficient yet
extensible format.
</description>
<inceptionYear>2014</inceptionYear>
<url>https://developers.google.com/protocol-buffers/</url>
<licenses>
<license>
<name>New BSD license</name>
<url>http://www.opensource.org/licenses/bsd-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/google/protobuf</url>
<connection>
scm:git:https://github.com/google/protobuf.git
</connection>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<ruby.sources>lib/google</ruby.sources>
<jar.finalName>protobuf_java</jar.finalName>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>${jar.finalName}</finalName>
<outputDirectory>${ruby.sources}</outputDirectory>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.7.13</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.0.0-pre</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,167 @@
/*
* 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 org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name = "Builder")
public class RubyBuilder extends RubyObject {
public static void createRubyBuilder(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cBuilder = protobuf.defineClassUnder("Builder", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyBuilder(runtime, klazz);
}
});
cBuilder.defineAnnotatedMethods(RubyBuilder.class);
}
public RubyBuilder(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
this.cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor");
this.cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor");
this.cMessageBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MessageBuilderContext");
this.cEnumBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumBuilderContext");
}
/*
* call-seq:
* Builder.new => builder
*
* Creates a new Builder. A Builder can accumulate a set of new message and enum
* descriptors and atomically register them into a pool in a way that allows for
* (co)recursive type references.
*/
@JRubyMethod
public IRubyObject initialize(ThreadContext context) {
Ruby runtime = context.runtime;
this.pendingList = runtime.newArray();
return this;
}
/*
* call-seq:
* Builder.add_message(name, &block)
*
* Creates a new, empty descriptor with the given name, and invokes the block in
* the context of a MessageBuilderContext on that descriptor. The block can then
* call, e.g., MessageBuilderContext#optional and MessageBuilderContext#repeated
* methods to define the message fields.
*
* This is the recommended, idiomatic way to build message definitions.
*/
@JRubyMethod(name = "add_message")
public IRubyObject addMessage(ThreadContext context, IRubyObject name, Block block) {
RubyDescriptor msgdef = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
IRubyObject ctx = cMessageBuilderContext.newInstance(context, msgdef, this, Block.NULL_BLOCK);
msgdef.setName(context, name);
if (block.isGiven()) {
if (block.arity() == Arity.ONE_ARGUMENT) {
block.yield(context, ctx);
} else {
Binding binding = block.getBinding();
binding.setSelf(ctx);
block.yieldSpecific(context);
}
}
this.pendingList.add(msgdef);
return context.runtime.getNil();
}
/*
* call-seq:
* Builder.add_enum(name, &block)
*
* Creates a new, empty enum descriptor with the given name, and invokes the block in
* the context of an EnumBuilderContext on that descriptor. The block can then
* call EnumBuilderContext#add_value to define the enum values.
*
* This is the recommended, idiomatic way to build enum definitions.
*/
@JRubyMethod(name = "add_enum")
public IRubyObject addEnum(ThreadContext context, IRubyObject name, Block block) {
RubyEnumDescriptor enumDef = (RubyEnumDescriptor) cEnumDescriptor.newInstance(context, Block.NULL_BLOCK);
IRubyObject ctx = cEnumBuilderContext.newInstance(context, enumDef, Block.NULL_BLOCK);
enumDef.setName(context, name);
if (block.isGiven()) {
if (block.arity() == Arity.ONE_ARGUMENT) {
block.yield(context, ctx);
} else {
Binding binding = block.getBinding();
binding.setSelf(ctx);
block.yieldSpecific(context);
}
}
this.pendingList.add(enumDef);
return context.runtime.getNil();
}
/*
* call-seq:
* Builder.finalize_to_pool(pool)
*
* Adds all accumulated message and enum descriptors created in this builder
* context to the given pool. The operation occurs atomically, and all
* descriptors can refer to each other (including in cycles). This is the only
* way to build (co)recursive message definitions.
*
* This method is usually called automatically by DescriptorPool#build after it
* invokes the given user block in the context of the builder. The user should
* not normally need to call this manually because a Builder is not normally
* created manually.
*/
@JRubyMethod(name = "finalize_to_pool")
public IRubyObject finalizeToPool(ThreadContext context, IRubyObject rbPool) {
RubyDescriptorPool pool = (RubyDescriptorPool) rbPool;
for (int i = 0; i < this.pendingList.size(); i++) {
IRubyObject defRb = this.pendingList.entry(i);
if (defRb instanceof RubyDescriptor) {
pool.addToSymtab(context, (RubyDescriptor) defRb);
} else {
pool.addToSymtab(context, (RubyEnumDescriptor) defRb);
}
}
this.pendingList = context.runtime.newArray();
return context.runtime.getNil();
}
protected RubyArray pendingList;
private RubyClass cDescriptor, cEnumDescriptor, cMessageBuilderContext, cEnumBuilderContext;
}

@ -0,0 +1,267 @@
/*
* 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.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.HashMap;
import java.util.Map;
@JRubyClass(name = "Descriptor", include = "Enumerable")
public class RubyDescriptor extends RubyObject {
public static void createRubyDescriptor(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cDescriptor = protobuf.defineClassUnder("Descriptor", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyDescriptor(runtime, klazz);
}
});
cDescriptor.includeModule(runtime.getEnumerable());
cDescriptor.defineAnnotatedMethods(RubyDescriptor.class);
}
public RubyDescriptor(Ruby runtime, RubyClass klazz) {
super(runtime, klazz);
}
/*
* call-seq:
* Descriptor.new => descriptor
*
* Creates a new, empty, message type descriptor. At a minimum, its name must be
* set before it is added to a pool. It cannot be used to create messages until
* it is added to a pool, after which it becomes immutable (as part of a
* finalization process).
*/
@JRubyMethod
public IRubyObject initialize(ThreadContext context) {
this.builder = DescriptorProtos.DescriptorProto.newBuilder();
this.fieldDefMap = new HashMap<String, RubyFieldDescriptor>();
this.oneofDefs = new HashMap<IRubyObject, RubyOneofDescriptor>();
return this;
}
/*
* call-seq:
* Descriptor.name => name
*
* Returns the name of this message type as a fully-qualfied string (e.g.,
* My.Package.MessageType).
*/
@JRubyMethod(name = "name")
public IRubyObject getName(ThreadContext context) {
return this.name;
}
/*
* call-seq:
* Descriptor.name = name
*
* Assigns a name to this message type. The descriptor must not have been added
* to a pool yet.
*/
@JRubyMethod(name = "name=")
public IRubyObject setName(ThreadContext context, IRubyObject name) {
this.name = name;
this.builder.setName(Utils.escapeIdentifier(this.name.asJavaString()));
return context.runtime.getNil();
}
/*
* call-seq:
* Descriptor.add_field(field) => nil
*
* Adds the given FieldDescriptor to this message type. The descriptor must not
* have been added to a pool yet. Raises an exception if a field with the same
* name or number already exists. Sub-type references (e.g. for fields of type
* message) are not resolved at this point.
*/
@JRubyMethod(name = "add_field")
public IRubyObject addField(ThreadContext context, IRubyObject obj) {
RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) obj;
this.fieldDefMap.put(fieldDef.getName(context).asJavaString(), fieldDef);
this.builder.addField(fieldDef.build());
return context.runtime.getNil();
}
/*
* call-seq:
* Descriptor.lookup(name) => FieldDescriptor
*
* Returns the field descriptor for the field with the given name, if present,
* or nil if none.
*/
@JRubyMethod
public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) {
return this.fieldDefMap.get(fieldName.asJavaString());
}
/*
* call-seq:
* Descriptor.msgclass => message_klass
*
* Returns the Ruby class created for this message type. Valid only once the
* message type has been added to a pool.
*/
@JRubyMethod
public IRubyObject msgclass(ThreadContext context) {
if (this.klazz == null) {
this.klazz = buildClassFromDescriptor(context);
}
return this.klazz;
}
/*
* call-seq:
* Descriptor.each(&block)
*
* Iterates over fields in this message type, yielding to the block on each one.
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
for (Map.Entry<String, RubyFieldDescriptor> entry : fieldDefMap.entrySet()) {
block.yield(context, entry.getValue());
}
return context.runtime.getNil();
}
/*
* call-seq:
* Descriptor.add_oneof(oneof) => nil
*
* Adds the given OneofDescriptor to this message type. This descriptor must not
* have been added to a pool yet. Raises an exception if a oneof with the same
* name already exists, or if any of the oneof's fields' names or numbers
* conflict with an existing field in this message type. All fields in the oneof
* are added to the message descriptor. Sub-type references (e.g. for fields of
* type message) are not resolved at this point.
*/
@JRubyMethod(name = "add_oneof")
public IRubyObject addOneof(ThreadContext context, IRubyObject obj) {
RubyOneofDescriptor def = (RubyOneofDescriptor) obj;
builder.addOneofDecl(def.build(builder.getOneofDeclCount()));
for (RubyFieldDescriptor fieldDescriptor : def.getFields()) {
addField(context, fieldDescriptor);
}
oneofDefs.put(def.getName(context), def);
return context.runtime.getNil();
}
/*
* call-seq:
* Descriptor.each_oneof(&block) => nil
*
* Invokes the given block for each oneof in this message type, passing the
* corresponding OneofDescriptor.
*/
@JRubyMethod(name = "each_oneof")
public IRubyObject eachOneof(ThreadContext context, Block block) {
for (RubyOneofDescriptor oneofDescriptor : oneofDefs.values()) {
block.yieldSpecific(context, oneofDescriptor);
}
return context.runtime.getNil();
}
/*
* call-seq:
* Descriptor.lookup_oneof(name) => OneofDescriptor
*
* Returns the oneof descriptor for the oneof with the given name, if present,
* or nil if none.
*/
@JRubyMethod(name = "lookup_oneof")
public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) {
if (name instanceof RubySymbol) {
name = ((RubySymbol) name).id2name();
}
return oneofDefs.containsKey(name) ? oneofDefs.get(name) : context.runtime.getNil();
}
public void setDescriptor(Descriptors.Descriptor descriptor) {
this.descriptor = descriptor;
}
public Descriptors.Descriptor getDescriptor() {
return this.descriptor;
}
public DescriptorProtos.DescriptorProto.Builder getBuilder() {
return builder;
}
public void setMapEntry(boolean isMapEntry) {
this.builder.setOptions(DescriptorProtos.MessageOptions.newBuilder().setMapEntry(isMapEntry));
}
private RubyModule buildClassFromDescriptor(ThreadContext context) {
Ruby runtime = context.runtime;
ObjectAllocator allocator = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyMessage(runtime, klazz, descriptor);
}
};
// rb_define_class_id
RubyClass klass = RubyClass.newClass(runtime, runtime.getObject());
klass.setAllocator(allocator);
klass.makeMetaClass(runtime.getObject().getMetaClass());
klass.inherit(runtime.getObject());
klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
klass.defineAnnotatedMethods(RubyMessage.class);
return klass;
}
protected RubyFieldDescriptor lookup(String fieldName) {
return fieldDefMap.get(Utils.unescapeIdentifier(fieldName));
}
private IRubyObject name;
private RubyModule klazz;
private DescriptorProtos.DescriptorProto.Builder builder;
private Descriptors.Descriptor descriptor;
private Map<String, RubyFieldDescriptor> fieldDefMap;
private Map<IRubyObject, RubyOneofDescriptor> oneofDefs;
}

@ -0,0 +1,169 @@
/*
* 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.*;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.HashMap;
import java.util.Map;
@JRubyClass(name = "DescriptorPool")
public class RubyDescriptorPool extends RubyObject {
public static void createRubyDescriptorPool(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cDescriptorPool = protobuf.defineClassUnder("DescriptorPool", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyDescriptorPool(runtime, klazz);
}
});
cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class);
descriptorPool = (RubyDescriptorPool) cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK);
}
public RubyDescriptorPool(Ruby ruby, RubyClass klazz) {
super(ruby, klazz);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext context) {
this.symtab = new HashMap<IRubyObject, IRubyObject>();
this.cBuilder = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Builder");
this.builder = DescriptorProtos.FileDescriptorProto.newBuilder();
return this;
}
@JRubyMethod
public IRubyObject build(ThreadContext context, Block block) {
RubyBuilder ctx = (RubyBuilder) cBuilder.newInstance(context, Block.NULL_BLOCK);
if (block.arity() == Arity.ONE_ARGUMENT) {
block.yield(context, ctx);
} else {
Binding binding = block.getBinding();
binding.setSelf(ctx);
block.yieldSpecific(context);
}
ctx.finalizeToPool(context, this);
buildFileDescriptor(context);
return context.runtime.getNil();
}
@JRubyMethod
public IRubyObject lookup(ThreadContext context, IRubyObject name) {
IRubyObject descriptor = this.symtab.get(name);
if (descriptor == null) {
return context.runtime.getNil();
}
return descriptor;
}
/*
* call-seq:
* DescriptorPool.generated_pool => descriptor_pool
*
* Class method that returns the global DescriptorPool. This is a singleton into
* which generated-code message and enum types are registered. The user may also
* register types in this pool for convenience so that they do not have to hold
* a reference to a private pool instance.
*/
@JRubyMethod(meta = true, name = "generated_pool")
public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) {
return descriptorPool;
}
protected void addToSymtab(ThreadContext context, RubyDescriptor def) {
symtab.put(def.getName(context), def);
this.builder.addMessageType(def.getBuilder());
}
protected void addToSymtab(ThreadContext context, RubyEnumDescriptor def) {
symtab.put(def.getName(context), def);
this.builder.addEnumType(def.getBuilder());
}
private void buildFileDescriptor(ThreadContext context) {
Ruby runtime = context.runtime;
try {
this.builder.setSyntax("proto3");
final Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(
this.builder.build(), new Descriptors.FileDescriptor[]{});
for (Descriptors.EnumDescriptor enumDescriptor : fileDescriptor.getEnumTypes()) {
String enumName = Utils.unescapeIdentifier(enumDescriptor.getName());
if (enumDescriptor.findValueByNumber(0) == null) {
throw runtime.newTypeError("Enum definition " + enumName
+ " does not contain a value for '0'");
}
((RubyEnumDescriptor) symtab.get(runtime.newString(enumName)))
.setDescriptor(enumDescriptor);
}
for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
RubyDescriptor rubyDescriptor = ((RubyDescriptor)
symtab.get(runtime.newString(Utils.unescapeIdentifier(descriptor.getName()))));
for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
if (fieldDescriptor.isRequired()) {
throw runtime.newTypeError("Required fields are unsupported in proto3");
}
RubyFieldDescriptor rubyFieldDescriptor = rubyDescriptor.lookup(fieldDescriptor.getName());
rubyFieldDescriptor.setFieldDef(fieldDescriptor);
if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
RubyDescriptor subType = (RubyDescriptor) lookup(context,
runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getMessageType().getName())));
rubyFieldDescriptor.setSubType(subType);
}
if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
RubyEnumDescriptor subType = (RubyEnumDescriptor) lookup(context,
runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getEnumType().getName())));
rubyFieldDescriptor.setSubType(subType);
}
}
rubyDescriptor.setDescriptor(descriptor);
}
} catch (Descriptors.DescriptorValidationException e) {
throw runtime.newRuntimeError(e.getMessage());
}
}
private static RubyDescriptorPool descriptorPool;
private RubyClass cBuilder;
private Map<IRubyObject, IRubyObject> symtab;
private DescriptorProtos.FileDescriptorProto.Builder builder;
}

@ -0,0 +1,86 @@
/*
* 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.Descriptors;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
public class RubyEnum {
/*
* call-seq:
* Enum.lookup(number) => name
*
* This module method, provided on each generated enum module, looks up an enum
* value by number and returns its name as a Ruby symbol, or nil if not found.
*/
@JRubyMethod(meta = true)
public static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number) {
RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor();
Descriptors.EnumValueDescriptor value = descriptor.findValueByNumber(RubyNumeric.num2int(number));
if (value == null) return context.runtime.getNil();
return context.runtime.newSymbol(value.getName());
}
/*
* call-seq:
* Enum.resolve(name) => number
*
* This module method, provided on each generated enum module, looks up an enum
* value by name (as a Ruby symbol) and returns its name, or nil if not found.
*/
@JRubyMethod(meta = true)
public static IRubyObject resolve(ThreadContext context, IRubyObject recv, IRubyObject name) {
RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor();
Descriptors.EnumValueDescriptor value = descriptor.findValueByName(name.asJavaString());
if (value == null) return context.runtime.getNil();
return context.runtime.newFixnum(value.getNumber());
}
/*
* call-seq:
* Enum.descriptor
*
* This module method, provided on each generated enum module, returns the
* EnumDescriptor corresponding to this enum type.
*/
@JRubyMethod(meta = true, name = "descriptor")
public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
return ((RubyModule) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
}
}

@ -0,0 +1,82 @@
/*
* 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 org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
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 = "EnumBuilderContext")
public class RubyEnumBuilderContext extends RubyObject {
public static void createRubyEnumBuilderContext(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cMessageBuilderContext = protobuf.defineClassUnder("EnumBuilderContext", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyEnumBuilderContext(runtime, klazz);
}
});
cMessageBuilderContext.defineAnnotatedMethods(RubyEnumBuilderContext.class);
}
public RubyEnumBuilderContext(Ruby ruby, RubyClass klazz) {
super(ruby, klazz);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext context, IRubyObject enumDescriptor) {
this.enumDescriptor = (RubyEnumDescriptor) enumDescriptor;
return this;
}
/*
* call-seq:
* EnumBuilder.add_value(name, number)
*
* Adds the given name => number mapping to the enum type. Name must be a Ruby
* symbol.
*/
@JRubyMethod
public IRubyObject value(ThreadContext context, IRubyObject name, IRubyObject number) {
this.enumDescriptor.addValue(context, name, number);
return context.runtime.getNil();
}
private RubyEnumDescriptor enumDescriptor;
}

@ -0,0 +1,185 @@
/*
* 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.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyNumeric;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name = "EnumDescriptor", include = "Enumerable")
public class RubyEnumDescriptor extends RubyObject {
public static void createRubyEnumDescriptor(Ruby runtime) {
RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cEnumDescriptor = mProtobuf.defineClassUnder("EnumDescriptor", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyEnumDescriptor(runtime, klazz);
}
});
cEnumDescriptor.includeModule(runtime.getEnumerable());
cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class);
}
public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) {
super(runtime, klazz);
}
/*
* call-seq:
* EnumDescriptor.new => enum_descriptor
*
* Creates a new, empty, enum descriptor. Must be added to a pool before the
* enum type can be used. The enum type may only be modified prior to adding to
* a pool.
*/
@JRubyMethod
public IRubyObject initialize(ThreadContext context) {
this.builder = DescriptorProtos.EnumDescriptorProto.newBuilder();
return this;
}
/*
* call-seq:
* EnumDescriptor.name => name
*
* Returns the name of this enum type.
*/
@JRubyMethod(name = "name")
public IRubyObject getName(ThreadContext context) {
return this.name;
}
/*
* call-seq:
* EnumDescriptor.name = name
*
* Sets the name of this enum type. Cannot be called if the enum type has
* already been added to a pool.
*/
@JRubyMethod(name = "name=")
public IRubyObject setName(ThreadContext context, IRubyObject name) {
this.name = name;
this.builder.setName(Utils.escapeIdentifier(name.asJavaString()));
return context.runtime.getNil();
}
/*
* call-seq:
* EnumDescriptor.add_value(key, value)
*
* Adds a new key => value mapping to this enum type. Key must be given as a
* Ruby symbol. Cannot be called if the enum type has already been added to a
* pool. Will raise an exception if the key or value is already in use.
*/
@JRubyMethod(name = "add_value")
public IRubyObject addValue(ThreadContext context, IRubyObject name, IRubyObject number) {
DescriptorProtos.EnumValueDescriptorProto.Builder valueBuilder = DescriptorProtos.EnumValueDescriptorProto.newBuilder();
valueBuilder.setName(name.asJavaString());
valueBuilder.setNumber(RubyNumeric.num2int(number));
this.builder.addValue(valueBuilder);
return context.runtime.getNil();
}
/*
* call-seq:
* EnumDescriptor.each(&block)
*
* Iterates over key => value mappings in this enum's definition, yielding to
* the block with (key, value) arguments for each one.
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
Ruby runtime = context.runtime;
for (Descriptors.EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) {
block.yield(context, runtime.newArray(runtime.newSymbol(enumValueDescriptor.getName()),
runtime.newFixnum(enumValueDescriptor.getNumber())));
}
return runtime.getNil();
}
/*
* call-seq:
* EnumDescriptor.enummodule => module
*
* Returns the Ruby module corresponding to this enum type. Cannot be called
* until the enum descriptor has been added to a pool.
*/
@JRubyMethod
public IRubyObject enummodule(ThreadContext context) {
if (this.klazz == null) {
this.klazz = buildModuleFromDescriptor(context);
}
return this.klazz;
}
public void setDescriptor(Descriptors.EnumDescriptor descriptor) {
this.descriptor = descriptor;
}
public Descriptors.EnumDescriptor getDescriptor() {
return this.descriptor;
}
public DescriptorProtos.EnumDescriptorProto.Builder getBuilder() {
return this.builder;
}
private RubyModule buildModuleFromDescriptor(ThreadContext context) {
Ruby runtime = context.runtime;
Utils.checkNameAvailability(context, name.asJavaString());
RubyModule enumModule = RubyModule.newModule(runtime);
for (Descriptors.EnumValueDescriptor value : descriptor.getValues()) {
enumModule.defineConstant(value.getName(), runtime.newFixnum(value.getNumber()));
}
enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
enumModule.defineAnnotatedMethods(RubyEnum.class);
return enumModule;
}
private IRubyObject name;
private RubyModule klazz;
private Descriptors.EnumDescriptor descriptor;
private DescriptorProtos.EnumDescriptorProto.Builder builder;
}

@ -0,0 +1,248 @@
/*
* 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 = 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) {
this.builder.setLabel(
DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + value.asJavaString().toUpperCase()));
return context.runtime.getNil();
}
/*
* call-seq:
* FieldDescriptor.name => name
*
* Returns the name of this field.
*/
@JRubyMethod(name = "name")
public IRubyObject getName(ThreadContext context) {
return this.name;
}
@JRubyMethod(name = "subtype")
public IRubyObject getSubType(ThreadContext context) {
return subType;
}
/*
* 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();
}
/*
* 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
*
* 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.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 subType;
private IRubyObject oneofName;
private Descriptors.FieldDescriptor fieldDef;
private int oneofIndex;
private boolean hasOneofIndex = false;
}

@ -0,0 +1,434 @@
/*
* 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.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.MapEntry;
import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@JRubyClass(name = "Map", include = "Enumerable")
public class RubyMap extends RubyObject {
public static void createRubyMap(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cMap = protobuf.defineClassUnder("Map", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
return new RubyMap(ruby, rubyClass);
}
});
cMap.includeModule(runtime.getEnumerable());
cMap.defineAnnotatedMethods(RubyMap.class);
}
public RubyMap(Ruby ruby, RubyClass rubyClass) {
super(ruby, rubyClass);
}
/*
* call-seq:
* Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
* => new map
*
* Allocates a new Map container. This constructor may be called with 2, 3, or 4
* arguments. The first two arguments are always present and are symbols (taking
* on the same values as field-type symbols in message descriptors) that
* indicate the type of the map key and value fields.
*
* The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
* :string, :bytes.
*
* The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
* :string, :bytes, :enum, :message.
*
* The third argument, value_typeclass, must be present if value_type is :enum
* or :message. As in RepeatedField#new, this argument must be a message class
* (for :message) or enum module (for :enum).
*
* The last argument, if present, provides initial content for map. Note that
* this may be an ordinary Ruby hashmap or another Map instance with identical
* key and value types. Also note that this argument may be present whether or
* not value_typeclass is present (and it is unambiguously separate from
* value_typeclass because value_typeclass's presence is strictly determined by
* value_type). The contents of this initial hashmap or Map instance are
* shallow-copied into the new Map: the original map is unmodified, but
* references to underlying objects will be shared if the value type is a
* message type.
*/
@JRubyMethod(required = 2, optional = 2)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
this.table = new HashMap<IRubyObject, IRubyObject>();
this.keyType = Utils.rubyToFieldType(args[0]);
this.valueType = Utils.rubyToFieldType(args[1]);
switch(keyType) {
case INT32:
case INT64:
case UINT32:
case UINT64:
case BOOL:
case STRING:
case BYTES:
// These are OK.
break;
default:
throw context.runtime.newArgumentError("Invalid key type for map.");
}
int initValueArg = 2;
if (needTypeclass(this.valueType) && args.length > 2) {
this.valueTypeClass = args[2];
Utils.validateTypeClass(context, this.valueType, this.valueTypeClass);
initValueArg = 3;
} else {
this.valueTypeClass = context.runtime.getNilClass();
}
// Table value type is always UINT64: this ensures enough space to store the
// native_slot value.
if (args.length > initValueArg) {
mergeIntoSelf(context, args[initValueArg]);
}
return this;
}
/*
* call-seq:
* Map.[]=(key, value) => value
*
* Inserts or overwrites the value at the given key with the given new value.
* Throws an exception if the key type is incorrect. Returns the new value that
* was just inserted.
*/
@JRubyMethod(name = "[]=")
public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) {
Utils.checkType(context, keyType, key, (RubyModule) valueTypeClass);
Utils.checkType(context, valueType, value, (RubyModule) valueTypeClass);
IRubyObject symbol;
if (valueType == Descriptors.FieldDescriptor.Type.ENUM &&
Utils.isRubyNum(value) &&
! (symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) {
value = symbol;
}
this.table.put(key, value);
return value;
}
/*
* call-seq:
* Map.[](key) => value
*
* Accesses the element at the given key. Throws an exception if the key type is
* incorrect. Returns nil when the key is not present in the map.
*/
@JRubyMethod(name = "[]")
public IRubyObject index(ThreadContext context, IRubyObject key) {
if (table.containsKey(key))
return this.table.get(key);
return context.runtime.getNil();
}
/*
* call-seq:
* Map.==(other) => boolean
*
* Compares this map to another. Maps are equal if they have identical key sets,
* and for each key, the values in both maps compare equal. Elements are
* compared as per normal Ruby semantics, by calling their :== methods (or
* performing a more efficient comparison for primitive types).
*
* Maps with dissimilar key types or value types/typeclasses are never equal,
* even if value comparison (for example, between integers and floats) would
* have otherwise indicated that every element has equal value.
*/
@JRubyMethod(name = "==")
public IRubyObject eq(ThreadContext context, IRubyObject _other) {
if (_other instanceof RubyHash)
return toHash(context).op_equal(context, _other);
RubyMap other = (RubyMap) _other;
if (this == other) return context.runtime.getTrue();
if (!typeCompatible(other) || this.table.size() != other.table.size())
return context.runtime.getFalse();
for (IRubyObject key : table.keySet()) {
if (! other.table.containsKey(key))
return context.runtime.getFalse();
if (! other.table.get(key).equals(table.get(key)))
return context.runtime.getFalse();
}
return context.runtime.getTrue();
}
/*
* call-seq:
* Map.inspect => string
*
* Returns a string representing this map's elements. It will be formatted as
* "{key => value, key => value, ...}", with each key and value string
* representation computed by its own #inspect method.
*/
@JRubyMethod
public IRubyObject inspect() {
return toHash(getRuntime().getCurrentContext()).inspect();
}
/*
* call-seq:
* Map.hash => hash_value
*
* Returns a hash value based on this map's contents.
*/
@JRubyMethod
public IRubyObject hash(ThreadContext context) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
for (IRubyObject key : table.keySet()) {
digest.update((byte) key.hashCode());
digest.update((byte) table.get(key).hashCode());
}
return context.runtime.newString(new ByteList(digest.digest()));
} catch (NoSuchAlgorithmException ignore) {
return context.runtime.newFixnum(System.identityHashCode(table));
}
}
/*
* call-seq:
* Map.keys => [list_of_keys]
*
* Returns the list of keys contained in the map, in unspecified order.
*/
@JRubyMethod
public IRubyObject keys(ThreadContext context) {
return RubyArray.newArray(context.runtime, table.keySet());
}
/*
* call-seq:
* Map.values => [list_of_values]
*
* Returns the list of values contained in the map, in unspecified order.
*/
@JRubyMethod
public IRubyObject values(ThreadContext context) {
return RubyArray.newArray(context.runtime, table.values());
}
/*
* call-seq:
* Map.clear
*
* Removes all entries from the map.
*/
@JRubyMethod
public IRubyObject clear(ThreadContext context) {
table.clear();
return context.runtime.getNil();
}
/*
* call-seq:
* Map.each(&block)
*
* Invokes &block on each |key, value| pair in the map, in unspecified order.
* Note that Map also includes Enumerable; map thus acts like a normal Ruby
* sequence.
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
for (IRubyObject key : table.keySet()) {
block.yieldSpecific(context, key, table.get(key));
}
return context.runtime.getNil();
}
/*
* call-seq:
* Map.delete(key) => old_value
*
* Deletes the value at the given key, if any, returning either the old value or
* nil if none was present. Throws an exception if the key is of the wrong type.
*/
@JRubyMethod
public IRubyObject delete(ThreadContext context, IRubyObject key) {
return table.remove(key);
}
/*
* call-seq:
* Map.has_key?(key) => bool
*
* Returns true if the given key is present in the map. Throws an exception if
* the key has the wrong type.
*/
@JRubyMethod(name = "has_key?")
public IRubyObject hasKey(ThreadContext context, IRubyObject key) {
return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
}
/*
* call-seq:
* Map.length
*
* Returns the number of entries (key-value pairs) in the map.
*/
@JRubyMethod
public IRubyObject length(ThreadContext context) {
return context.runtime.newFixnum(this.table.size());
}
/*
* call-seq:
* Map.dup => new_map
*
* Duplicates this map with a shallow copy. References to all non-primitive
* element objects (e.g., submessages) are shared.
*/
@JRubyMethod
public IRubyObject dup(ThreadContext context) {
RubyMap newMap = newThisType(context);
for (Map.Entry<IRubyObject, IRubyObject> entry : table.entrySet()) {
newMap.table.put(entry.getKey(), entry.getValue());
}
return newMap;
}
@JRubyMethod(name = "to_h")
public RubyHash toHash(ThreadContext context) {
return RubyHash.newHash(context.runtime, table, context.runtime.getNil());
}
// Used by Google::Protobuf.deep_copy but not exposed directly.
protected IRubyObject deepCopy(ThreadContext context) {
RubyMap newMap = newThisType(context);
switch (valueType) {
case MESSAGE:
for (IRubyObject key : table.keySet()) {
RubyMessage message = (RubyMessage) table.get(key);
newMap.table.put(key.dup(), message.deepCopy(context));
}
break;
default:
for (IRubyObject key : table.keySet()) {
newMap.table.put(key.dup(), table.get(key).dup());
}
}
return newMap;
}
protected List<DynamicMessage> build(ThreadContext context, RubyDescriptor descriptor) {
List<DynamicMessage> list = new ArrayList<DynamicMessage>();
RubyClass rubyClass = (RubyClass) descriptor.msgclass(context);
Descriptors.FieldDescriptor keyField = descriptor.lookup("key").getFieldDef();
Descriptors.FieldDescriptor valueField = descriptor.lookup("value").getFieldDef();
for (IRubyObject key : table.keySet()) {
RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK);
mapMessage.setField(context, keyField, key);
mapMessage.setField(context, valueField, table.get(key));
list.add(mapMessage.build(context));
}
return list;
}
protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) {
if (hashmap instanceof RubyHash) {
((RubyHash) hashmap).visitAll(new RubyHash.Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject val) {
indexSet(context, key, val);
}
});
} else if (hashmap instanceof RubyMap) {
RubyMap other = (RubyMap) hashmap;
if (!typeCompatible(other)) {
throw context.runtime.newTypeError("Attempt to merge Map with mismatching types");
}
} else {
throw context.runtime.newTypeError("Unknown type merging into Map");
}
return this;
}
protected boolean typeCompatible(RubyMap other) {
return this.keyType == other.keyType &&
this.valueType == other.valueType &&
this.valueTypeClass == other.valueTypeClass;
}
private RubyMap newThisType(ThreadContext context) {
RubyMap newMap;
if (needTypeclass(valueType)) {
newMap = (RubyMap) metaClass.newInstance(context,
Utils.fieldTypeToRuby(context, keyType),
Utils.fieldTypeToRuby(context, valueType),
valueTypeClass, Block.NULL_BLOCK);
} else {
newMap = (RubyMap) metaClass.newInstance(context,
Utils.fieldTypeToRuby(context, keyType),
Utils.fieldTypeToRuby(context, valueType),
Block.NULL_BLOCK);
}
newMap.table = new HashMap<IRubyObject, IRubyObject>();
return newMap;
}
private boolean needTypeclass(Descriptors.FieldDescriptor.Type type) {
switch(type) {
case MESSAGE:
case ENUM:
return true;
default:
return false;
}
}
private Descriptors.FieldDescriptor.Type keyType;
private Descriptors.FieldDescriptor.Type valueType;
private IRubyObject valueTypeClass;
private Map<IRubyObject, IRubyObject> table;
}

@ -0,0 +1,744 @@
/*
* 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.*;
import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import java.util.HashMap;
import java.util.Map;
public class RubyMessage extends RubyObject {
public RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor) {
super(ruby, klazz);
this.descriptor = descriptor;
}
/*
* call-seq:
* Message.new(kwargs) => new_message
*
* Creates a new instance of the given message class. Keyword arguments may be
* provided with keywords corresponding to field names.
*
* Note that no literal Message class exists. Only concrete classes per message
* type exist, as provided by the #msgclass method on Descriptors after they
* have been added to a pool. The method definitions described here on the
* Message class are provided on each concrete message class.
*/
@JRubyMethod(optional = 1)
public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
final Ruby runtime = context.runtime;
this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
this.builder = DynamicMessage.newBuilder(this.descriptor);
this.repeatedFields = new HashMap<Descriptors.FieldDescriptor, RubyRepeatedField>();
this.maps = new HashMap<Descriptors.FieldDescriptor, RubyMap>();
this.fields = new HashMap<Descriptors.FieldDescriptor, IRubyObject>();
this.oneofCases = new HashMap<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor>();
if (args.length == 1) {
if (!(args[0] instanceof RubyHash)) {
throw runtime.newArgumentError("expected Hash arguments.");
}
RubyHash hash = args[0].convertToHash();
hash.visitAll(new RubyHash.Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
if (!(key instanceof RubySymbol))
throw runtime.newTypeError("Expected symbols as hash keys in initialization map.");
final Descriptors.FieldDescriptor fieldDescriptor = findField(context, key);
if (Utils.isMapEntry(fieldDescriptor)) {
if (!(value instanceof RubyHash))
throw runtime.newArgumentError("Expected Hash object as initializer value for map field.");
final RubyMap map = newMapForField(context, fieldDescriptor);
map.mergeIntoSelf(context, value);
maps.put(fieldDescriptor, map);
} else if (fieldDescriptor.isRepeated()) {
if (!(value instanceof RubyArray))
throw runtime.newTypeError("Expected array as initializer var for repeated field.");
RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, value);
addRepeatedField(fieldDescriptor, repeatedField);
} else {
Descriptors.OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
if (oneof != null) {
oneofCases.put(oneof, fieldDescriptor);
}
fields.put(fieldDescriptor, value);
}
}
});
}
return this;
}
/*
* call-seq:
* Message.[]=(index, value)
*
* Sets a field's value by field name. The provided field name should be a
* string.
*/
@JRubyMethod(name = "[]=")
public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
return setField(context, fieldDescriptor, value);
}
/*
* call-seq:
* Message.[](index) => value
*
* Accesses a field's value by field name. The provided field name should be a
* string.
*/
@JRubyMethod(name = "[]")
public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
return getField(context, fieldDescriptor);
}
/*
* call-seq:
* Message.inspect => string
*
* Returns a human-readable string representing this message. It will be
* formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
* field's value is represented according to its own #inspect method.
*/
@JRubyMethod
public IRubyObject inspect() {
String cname = metaClass.getName();
StringBuilder sb = new StringBuilder("<");
sb.append(cname);
sb.append(": ");
sb.append(this.layoutInspect());
sb.append(">");
return getRuntime().newString(sb.toString());
}
/*
* call-seq:
* Message.hash => hash_value
*
* Returns a hash value that represents this message's field values.
*/
@JRubyMethod
public IRubyObject hash(ThreadContext context) {
int hashCode = System.identityHashCode(this);
return context.runtime.newFixnum(hashCode);
}
/*
* call-seq:
* Message.==(other) => boolean
*
* Performs a deep comparison of this message with another. Messages are equal
* if they have the same type and if each field is equal according to the :==
* method's semantics (a more efficient comparison may actually be done if the
* field is of a primitive type).
*/
@JRubyMethod(name = "==")
public IRubyObject eq(ThreadContext context, IRubyObject other) {
Ruby runtime = context.runtime;
if (!(other instanceof RubyMessage))
return runtime.getFalse();
RubyMessage message = (RubyMessage) other;
if (descriptor != message.descriptor) {
return runtime.getFalse();
}
for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
IRubyObject thisVal = getField(context, fdef);
IRubyObject thatVal = message.getField(context, fdef);
IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
if (!ret.isTrue()) {
return runtime.getFalse();
}
}
return runtime.getTrue();
}
/*
* call-seq:
* Message.method_missing(*args)
*
* Provides accessors and setters for message fields according to their field
* names. For any field whose name does not conflict with a built-in method, an
* accessor is provided with the same name as the field, and a setter is
* provided with the name of the field plus the '=' suffix. Thus, given a
* message instance 'msg' with field 'foo', the following code is valid:
*
* msg.foo = 42
* puts msg.foo
*/
@JRubyMethod(name = "method_missing", rest = true)
public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
if (args.length == 1) {
RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
if (oneofDescriptor.isNil()) {
return index(context, args[0]);
}
RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
Descriptors.FieldDescriptor fieldDescriptor =
oneofCases.get(rubyOneofDescriptor.getOneofDescriptor());
if (fieldDescriptor == null)
return context.runtime.getNil();
return context.runtime.newSymbol(fieldDescriptor.getName());
} else {
// fieldName is RubySymbol
RubyString field = args[0].asString();
RubyString equalSign = context.runtime.newString(Utils.EQUAL_SIGN);
if (field.end_with_p(context, equalSign).isTrue()) {
field.chomp_bang(context, equalSign);
}
return indexSet(context, field, args[1]);
}
}
/**
* call-seq:
* Message.dup => new_message
* Performs a shallow copy of this message and returns the new copy.
*/
@JRubyMethod
public IRubyObject dup(ThreadContext context) {
RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
IRubyObject value;
for (Descriptors.FieldDescriptor fieldDescriptor : builder.getAllFields().keySet()) {
if (fieldDescriptor.isRepeated()) {
dup.repeatedFields.put(fieldDescriptor, getRepeatedField(context, fieldDescriptor));
} else if (builder.hasField(fieldDescriptor)) {
dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, builder.getField(fieldDescriptor)));
}
}
for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor));
}
for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor));
}
return dup;
}
/*
* call-seq:
* Message.descriptor => descriptor
*
* Class method that returns the Descriptor instance corresponding to this
* message class's type.
*/
@JRubyMethod(name = "descriptor", meta = true)
public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
}
/*
* call-seq:
* MessageClass.encode(msg) => bytes
*
* Encodes the given message object to its serialized form in protocol buffers
* wire format.
*/
@JRubyMethod(meta = true)
public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject value) {
RubyMessage message = (RubyMessage) value;
return context.runtime.newString(new ByteList(message.build(context).toByteArray()));
}
/*
* call-seq:
* MessageClass.decode(data) => message
*
* Decodes the given data (as a string containing bytes in protocol buffers wire
* format) under the interpretration given by this message class's definition
* and returns a message object with the corresponding field values.
*/
@JRubyMethod(meta = true)
public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject data) {
byte[] bin = data.convertToString().getBytes();
RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
try {
ret.builder.mergeFrom(bin);
} catch (InvalidProtocolBufferException e) {
throw context.runtime.newRuntimeError(e.getMessage());
}
return ret;
}
/*
* call-seq:
* MessageClass.encode_json(msg) => json_string
*
* Encodes the given message object into its serialized JSON representation.
*/
@JRubyMethod(name = "encode_json", meta = true)
public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) {
RubyMessage message = (RubyMessage) msgRb;
return Helpers.invoke(context, message.toHash(context), "to_json");
}
/*
* call-seq:
* MessageClass.decode_json(data) => message
*
* Decodes the given data (as a string containing bytes in protocol buffers wire
* format) under the interpretration given by this message class's definition
* and returns a message object with the corresponding field values.
*/
@JRubyMethod(name = "decode_json", meta = true)
public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) {
Ruby runtime = context.runtime;
RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
RubyModule jsonModule = runtime.getClassFromPath("JSON");
RubyHash opts = RubyHash.newHash(runtime);
opts.fastASet(runtime.newSymbol("symbolize_names"), runtime.getTrue());
IRubyObject[] args = new IRubyObject[] { Helpers.invoke(context, jsonModule, "parse", json, opts) };
ret.initialize(context, args);
return ret;
}
@JRubyMethod(name = "to_h")
public IRubyObject toHash(ThreadContext context) {
Ruby runtime = context.runtime;
RubyHash ret = RubyHash.newHash(runtime);
for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
IRubyObject value = getField(context, fdef);
if (value.respondsTo("to_h")) {
value = Helpers.invoke(context, value, "to_h");
}
ret.fastASet(runtime.newString(fdef.getName()), value);
}
return ret;
}
protected DynamicMessage build(ThreadContext context) {
return build(context, 0);
}
protected DynamicMessage build(ThreadContext context, int depth) {
if (depth > SINK_MAXIMUM_NESTING) {
throw context.runtime.newRuntimeError("Maximum recursion depth exceeded during encoding.");
}
for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
this.builder.clearField(fieldDescriptor);
RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
for (DynamicMessage kv : maps.get(fieldDescriptor).build(context, mapDescriptor)) {
this.builder.addRepeatedField(fieldDescriptor, kv);
}
}
for (Descriptors.FieldDescriptor fieldDescriptor : repeatedFields.keySet()) {
RubyRepeatedField repeatedField = repeatedFields.get(fieldDescriptor);
this.builder.clearField(fieldDescriptor);
for (int i = 0; i < repeatedField.size(); i++) {
Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth);
this.builder.addRepeatedField(fieldDescriptor, item);
}
}
for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
IRubyObject value = fields.get(fieldDescriptor);
this.builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth));
}
return this.builder.build();
}
protected Descriptors.Descriptor getDescriptor() {
return this.descriptor;
}
// Internal use only, called by Google::Protobuf.deep_copy
protected IRubyObject deepCopy(ThreadContext context) {
RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
if (fdef.isRepeated()) {
copy.addRepeatedField(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
} else if (fields.containsKey(fdef)) {
copy.fields.put(fdef, fields.get(fdef));
} else if (this.builder.hasField(fdef)) {
copy.fields.put(fdef, wrapField(context, fdef, this.builder.getField(fdef)));
}
}
return copy;
}
private RubyRepeatedField getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
if (this.repeatedFields.containsKey(fieldDescriptor)) {
return this.repeatedFields.get(fieldDescriptor);
}
int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
for (int i = 0; i < count; i++) {
ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)));
}
return ret;
}
private void addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField) {
this.repeatedFields.put(fieldDescriptor, repeatedField);
}
private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
this.builder.mergeFrom(dynamicMessage);
return this;
}
private Descriptors.FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
String nameStr = fieldName.asJavaString();
Descriptors.FieldDescriptor ret = this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr));
if (ret == null)
throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
return ret;
}
private void checkRepeatedFieldType(ThreadContext context, IRubyObject value,
Descriptors.FieldDescriptor fieldDescriptor) {
Ruby runtime = context.runtime;
if (!(value instanceof RubyRepeatedField)) {
throw runtime.newTypeError("Expected repeated field array");
}
}
// convert a ruby object to protobuf type, with type check
private Object convert(ThreadContext context,
Descriptors.FieldDescriptor fieldDescriptor,
IRubyObject value, int depth) {
Ruby runtime = context.runtime;
Object val = null;
switch (fieldDescriptor.getType()) {
case INT32:
case INT64:
case UINT32:
case UINT64:
if (!Utils.isRubyNum(value)) {
throw runtime.newTypeError("Expected number type for integral field.");
}
Utils.checkIntTypePrecision(context, fieldDescriptor.getType(), value);
switch (fieldDescriptor.getType()) {
case INT32:
val = RubyNumeric.num2int(value);
break;
case INT64:
val = RubyNumeric.num2long(value);
break;
case UINT32:
val = Utils.num2uint(value);
break;
case UINT64:
val = Utils.num2ulong(context.runtime, value);
break;
default:
break;
}
break;
case FLOAT:
if (!Utils.isRubyNum(value))
throw runtime.newTypeError("Expected number type for float field.");
val = (float) RubyNumeric.num2dbl(value);
break;
case DOUBLE:
if (!Utils.isRubyNum(value))
throw runtime.newTypeError("Expected number type for double field.");
val = RubyNumeric.num2dbl(value);
break;
case BOOL:
if (!(value instanceof RubyBoolean))
throw runtime.newTypeError("Invalid argument for boolean field.");
val = value.isTrue();
break;
case BYTES:
case STRING:
Utils.validateStringEncoding(context.runtime, fieldDescriptor.getType(), value);
RubyString str = (RubyString) value;
switch (fieldDescriptor.getType()) {
case BYTES:
val = ByteString.copyFrom(str.getBytes());
break;
case STRING:
val = str.asJavaString();
break;
default:
break;
}
break;
case MESSAGE:
RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
if (!value.getMetaClass().equals(typeClass))
throw runtime.newTypeError(value, "Invalid type to assign to submessage field.");
val = ((RubyMessage) value).build(context, depth + 1);
break;
case ENUM:
Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
if (Utils.isRubyNum(value)) {
val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
} else if (value instanceof RubySymbol) {
val = enumDescriptor.findValueByName(value.asJavaString());
} else {
throw runtime.newTypeError("Expected number or symbol type for enum field.");
}
if (val == null) {
throw runtime.newRangeError("Enum value " + value + " is not found.");
}
break;
default:
break;
}
return val;
}
private IRubyObject wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value) {
if (value == null) {
return context.runtime.getNil();
}
Ruby runtime = context.runtime;
switch (fieldDescriptor.getType()) {
case INT32:
case INT64:
case UINT32:
case UINT64:
case FLOAT:
case DOUBLE:
case BOOL:
case BYTES:
case STRING:
return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value);
case MESSAGE:
RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
return msg.buildFrom(context, (DynamicMessage) value);
case ENUM:
Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor) value;
if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
return runtime.newFixnum(enumValueDescriptor.getNumber());
}
return runtime.newSymbol(enumValueDescriptor.getName());
default:
return runtime.newString(value.toString());
}
}
private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context,
Descriptors.FieldDescriptor fieldDescriptor) {
IRubyObject typeClass = context.runtime.getNilClass();
IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
Descriptors.FieldDescriptor.Type type = fieldDescriptor.getType();
if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
typeClass = ((RubyDescriptor) descriptor).msgclass(context);
} else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
}
return new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
}
protected IRubyObject getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
if (oneofDescriptor != null) {
if (oneofCases.containsKey(oneofDescriptor)) {
if (oneofCases.get(oneofDescriptor) != fieldDescriptor)
return context.runtime.getNil();
return fields.get(fieldDescriptor);
} else {
Descriptors.FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
if (oneofCase != fieldDescriptor) return context.runtime.getNil();
IRubyObject value = wrapField(context, oneofCase, builder.getField(oneofCase));
fields.put(fieldDescriptor, value);
return value;
}
}
if (Utils.isMapEntry(fieldDescriptor)) {
RubyMap map = maps.get(fieldDescriptor);
if (map == null) {
map = newMapForField(context, fieldDescriptor);
int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
for (int i = 0; i < mapSize; i++) {
RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
kvMessage.buildFrom(context, message);
map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField));
}
maps.put(fieldDescriptor, map);
}
return map;
}
if (fieldDescriptor.isRepeated()) {
return getRepeatedField(context, fieldDescriptor);
}
if (fieldDescriptor.getType() != Descriptors.FieldDescriptor.Type.MESSAGE ||
this.builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) {
if (fields.containsKey(fieldDescriptor)) {
return fields.get(fieldDescriptor);
} else {
IRubyObject value = wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor));
if (this.builder.hasField(fieldDescriptor)) {
fields.put(fieldDescriptor, value);
}
return value;
}
}
return context.runtime.getNil();
}
protected IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
if (Utils.isMapEntry(fieldDescriptor)) {
if (!(value instanceof RubyMap)) {
throw context.runtime.newTypeError("Expected Map instance");
}
RubyMap thisMap = (RubyMap) getField(context, fieldDescriptor);
thisMap.mergeIntoSelf(context, value);
} else if (fieldDescriptor.isRepeated()) {
checkRepeatedFieldType(context, value, fieldDescriptor);
if (value instanceof RubyRepeatedField) {
addRepeatedField(fieldDescriptor, (RubyRepeatedField) value);
} else {
RubyArray ary = value.convertToArray();
RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, ary);
addRepeatedField(fieldDescriptor, repeatedField);
}
} else {
Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
if (oneofDescriptor != null) {
Descriptors.FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
if (oneofCase != null && oneofCase != fieldDescriptor) {
fields.remove(oneofCase);
}
if (value.isNil()) {
oneofCases.remove(oneofDescriptor);
fields.remove(fieldDescriptor);
} else {
oneofCases.put(oneofDescriptor, fieldDescriptor);
fields.put(fieldDescriptor, value);
}
} else {
Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType();
IRubyObject typeClass = context.runtime.getObject();
if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
} else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context);
}
Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
// Convert integer enum to symbol
if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
if (Utils.isRubyNum(value)) {
Descriptors.EnumValueDescriptor val =
enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName());
}
}
this.fields.put(fieldDescriptor, value);
}
}
return context.runtime.getNil();
}
private String layoutInspect() {
ThreadContext context = getRuntime().getCurrentContext();
StringBuilder sb = new StringBuilder();
for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
sb.append(Utils.unescapeIdentifier(fdef.getName()));
sb.append(": ");
sb.append(getField(context, fdef).inspect());
sb.append(", ");
}
return sb.substring(0, sb.length() - 2);
}
private IRubyObject getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
return thisRbDescriptor.lookup(fieldDescriptor.getName()).getSubType(context);
}
private RubyRepeatedField rubyToRepeatedField(ThreadContext context,
Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
RubyArray arr = value.convertToArray();
RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
for (int i = 0; i < arr.size(); i++) {
repeatedField.push(context, arr.eltInternal(i));
}
return repeatedField;
}
private RubyMap newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
if (valueField.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
context.runtime.newString("value"));
RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubType(context);
return (RubyMap) cMap.newInstance(context, keyType, valueType,
rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
} else {
return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
}
}
private Descriptors.FieldDescriptor getOneofCase(Descriptors.OneofDescriptor oneof) {
if (oneofCases.containsKey(oneof)) {
return oneofCases.get(oneof);
}
return builder.getOneofFieldDescriptor(oneof);
}
private Descriptors.Descriptor descriptor;
private DynamicMessage.Builder builder;
private RubyClass cRepeatedField;
private RubyClass cMap;
private Map<Descriptors.FieldDescriptor, RubyRepeatedField> repeatedFields;
private Map<Descriptors.FieldDescriptor, RubyMap> maps;
private Map<Descriptors.FieldDescriptor, IRubyObject> fields;
private Map<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor> oneofCases;
private static final int SINK_MAXIMUM_NESTING = 64;
}

@ -0,0 +1,217 @@
/*
* 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.Descriptors;
import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name = "MessageBuilderContext")
public class RubyMessageBuilderContext extends RubyObject {
public static void createRubyMessageBuilderContext(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cMessageBuilderContext = protobuf.defineClassUnder("MessageBuilderContext", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyMessageBuilderContext(runtime, klazz);
}
});
cMessageBuilderContext.defineAnnotatedMethods(RubyMessageBuilderContext.class);
}
public RubyMessageBuilderContext(Ruby ruby, RubyClass klazz) {
super(ruby, klazz);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext context, IRubyObject descriptor, IRubyObject rubyBuilder) {
this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
this.cDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Descriptor");
this.cOneofDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::OneofDescriptor");
this.cOneofBuilderContext = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Internal::OneofBuilderContext");
this.descriptor = (RubyDescriptor) descriptor;
this.builder = (RubyBuilder) rubyBuilder;
return this;
}
/*
* call-seq:
* MessageBuilderContext.optional(name, type, number, type_class = nil)
*
* Defines a new optional field on this message type with the given type, tag
* number, and type class (for message and enum fields). The type must be a Ruby
* symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
* string, if present (as accepted by FieldDescriptor#submsg_name=).
*/
@JRubyMethod(required = 3, optional = 1)
public IRubyObject optional(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
IRubyObject typeClass = runtime.getNil();
if (args.length > 3) typeClass = args[3];
msgdefAddField(context, "optional", args[0], args[1], args[2], typeClass);
return context.runtime.getNil();
}
/*
* call-seq:
* MessageBuilderContext.required(name, type, number, type_class = nil)
*
* Defines a new required field on this message type with the given type, tag
* number, and type class (for message and enum fields). The type must be a Ruby
* symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
* string, if present (as accepted by FieldDescriptor#submsg_name=).
*
* Proto3 does not have required fields, but this method exists for
* completeness. Any attempt to add a message type with required fields to a
* pool will currently result in an error.
*/
@JRubyMethod(required = 3, optional = 1)
public IRubyObject required(ThreadContext context, IRubyObject[] args) {
IRubyObject typeClass = context.runtime.getNil();
if (args.length > 3) typeClass = args[3];
msgdefAddField(context, "required", args[0], args[1], args[2], typeClass);
return context.runtime.getNil();
}
/*
* call-seq:
* MessageBuilderContext.repeated(name, type, number, type_class = nil)
*
* Defines a new repeated field on this message type with the given type, tag
* number, and type class (for message and enum fields). The type must be a Ruby
* symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
* string, if present (as accepted by FieldDescriptor#submsg_name=).
*/
@JRubyMethod(required = 3, optional = 1)
public IRubyObject repeated(ThreadContext context, IRubyObject[] args) {
IRubyObject typeClass = context.runtime.getNil();
if (args.length > 3) typeClass = args[3];
msgdefAddField(context, "repeated", args[0], args[1], args[2], typeClass);
return context.runtime.getNil();
}
/*
* call-seq:
* MessageBuilderContext.map(name, key_type, value_type, number,
* value_type_class = nil)
*
* Defines a new map field on this message type with the given key and value
* types, tag number, and type class (for message and enum value types). The key
* type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type
* type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the
* type_class must be a string, if present (as accepted by
* FieldDescriptor#submsg_name=).
*/
@JRubyMethod(required = 4, optional = 1)
public IRubyObject map(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
IRubyObject name = args[0];
IRubyObject keyType = args[1];
IRubyObject valueType = args[2];
IRubyObject number = args[3];
IRubyObject typeClass = args.length > 4 ? args[4] : context.runtime.getNil();
// Validate the key type. We can't accept enums, messages, or floats/doubles
// as map keys. (We exclude these explicitly, and the field-descriptor setter
// below then ensures that the type is one of the remaining valid options.)
if (keyType.equals(RubySymbol.newSymbol(runtime, "float")) ||
keyType.equals(RubySymbol.newSymbol(runtime, "double")) ||
keyType.equals(RubySymbol.newSymbol(runtime, "enum")) ||
keyType.equals(RubySymbol.newSymbol(runtime, "message")))
throw runtime.newArgumentError("Cannot add a map field with a float, double, enum, or message type.");
// Create a new message descriptor for the map entry message, and create a
// repeated submessage field here with that type.
RubyDescriptor mapentryDesc = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
IRubyObject mapentryDescName = RubySymbol.newSymbol(runtime, name).id2name(context);
mapentryDesc.setName(context, mapentryDescName);
mapentryDesc.setMapEntry(true);
//optional <type> key = 1;
RubyFieldDescriptor keyField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
keyField.setName(context, runtime.newString("key"));
keyField.setLabel(context, RubySymbol.newSymbol(runtime, "optional"));
keyField.setNumber(context, runtime.newFixnum(1));
keyField.setType(context, keyType);
mapentryDesc.addField(context, keyField);
//optional <type> value = 2;
RubyFieldDescriptor valueField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
valueField.setName(context, runtime.newString("value"));
valueField.setLabel(context, RubySymbol.newSymbol(runtime, "optional"));
valueField.setNumber(context, runtime.newFixnum(2));
valueField.setType(context, valueType);
if (! typeClass.isNil()) valueField.setSubmsgName(context, typeClass);
mapentryDesc.addField(context, valueField);
// Add the map-entry message type to the current builder, and use the type to
// create the map field itself.
this.builder.pendingList.add(mapentryDesc);
msgdefAddField(context, "repeated", name, runtime.newSymbol("message"), number, mapentryDescName);
return runtime.getNil();
}
@JRubyMethod
public IRubyObject oneof(ThreadContext context, IRubyObject name, Block block) {
RubyOneofDescriptor oneofdef = (RubyOneofDescriptor)
cOneofDescriptor.newInstance(context, Block.NULL_BLOCK);
RubyOneofBuilderContext ctx = (RubyOneofBuilderContext)
cOneofBuilderContext.newInstance(context, oneofdef, Block.NULL_BLOCK);
oneofdef.setName(context, name);
Binding binding = block.getBinding();
binding.setSelf(ctx);
block.yieldSpecific(context);
descriptor.addOneof(context, oneofdef);
return context.runtime.getNil();
}
private void msgdefAddField(ThreadContext context, String label, IRubyObject name,
IRubyObject type, IRubyObject number, IRubyObject typeClass) {
descriptor.addField(context,
Utils.msgdefCreateField(context, label, name, type, number, typeClass, cFieldDescriptor));
}
private RubyDescriptor descriptor;
private RubyBuilder builder;
private RubyClass cFieldDescriptor;
private RubyClass cOneofDescriptor;
private RubyClass cOneofBuilderContext;
private RubyClass cDescriptor;
}

@ -0,0 +1,84 @@
/*
* 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 org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
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 = "OneofBuilderContext")
public class RubyOneofBuilderContext extends RubyObject {
public static void createRubyOneofBuilderContext(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyModule internal = protobuf.defineModuleUnder("Internal");
RubyClass cRubyOneofBuidlerContext = internal.defineClassUnder("OneofBuilderContext", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
return new RubyOneofBuilderContext(ruby, rubyClass);
}
});
cRubyOneofBuidlerContext.defineAnnotatedMethods(RubyOneofBuilderContext.class);
}
public RubyOneofBuilderContext(Ruby ruby, RubyClass rubyClass) {
super(ruby, rubyClass);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext context, IRubyObject oneofdef) {
this.descriptor = (RubyOneofDescriptor) oneofdef;
this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
return this;
}
@JRubyMethod(required = 3, optional = 1)
public IRubyObject optional(ThreadContext context, IRubyObject[] args) {
IRubyObject name = args[0];
IRubyObject type = args[1];
IRubyObject number = args[2];
IRubyObject typeClass = args.length > 3 ? args[3] : context.runtime.getNil();
RubyFieldDescriptor fieldDescriptor = Utils.msgdefCreateField(context, "optional",
name, type, number, typeClass, cFieldDescriptor);
descriptor.addField(context, fieldDescriptor);
return this;
}
private RubyOneofDescriptor descriptor;
private RubyClass cFieldDescriptor;
}

@ -0,0 +1,124 @@
package com.google.protobuf.jruby;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.Descriptors;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.*;
@JRubyClass(name = "OneofDescriptor", include = "Enumerable")
public class RubyOneofDescriptor extends RubyObject {
public static void createRubyOneofDescriptor(Ruby runtime) {
RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cRubyOneofDescriptor = protobuf.defineClassUnder("OneofDescriptor", runtime.getObject(), new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
return new RubyOneofDescriptor(ruby, rubyClass);
}
});
cRubyOneofDescriptor.defineAnnotatedMethods(RubyOneofDescriptor.class);
cRubyOneofDescriptor.includeModule(runtime.getEnumerable());
}
public RubyOneofDescriptor(Ruby ruby, RubyClass rubyClass) {
super(ruby, rubyClass);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext context) {
builder = DescriptorProtos.OneofDescriptorProto.newBuilder();
fields = new ArrayList<RubyFieldDescriptor>();
return this;
}
/*
* call-seq:
* OneofDescriptor.name => name
*
* Returns the name of this oneof.
*/
@JRubyMethod(name = "name")
public IRubyObject getName(ThreadContext context) {
return name;
}
/*
* call-seq:
* OneofDescriptor.name = name
*
* Sets a new name for this oneof. The oneof must not have been added to a
* message descriptor yet.
*/
@JRubyMethod(name = "name=")
public IRubyObject setName(ThreadContext context, IRubyObject name) {
this.name = context.runtime.newString(name.asJavaString());
this.builder.setName(name.asJavaString());
return context.runtime.getNil();
}
/*
* call-seq:
* OneofDescriptor.add_field(field) => nil
*
* Adds a field to this oneof. The field may have been added to this oneof in
* the past, or the message to which this oneof belongs (if any), but may not
* have already been added to any other oneof or message. Otherwise, an
* exception is raised.
*
* All fields added to the oneof via this method will be automatically added to
* the message to which this oneof belongs, if it belongs to one currently, or
* else will be added to any message to which the oneof is later added at the
* time that it is added.
*/
@JRubyMethod(name = "add_field")
public IRubyObject addField(ThreadContext context, IRubyObject obj) {
RubyFieldDescriptor fieldDescriptor = (RubyFieldDescriptor) obj;
fieldDescriptor.setOneofName(this.name);
fields.add(fieldDescriptor);
return context.runtime.getNil();
}
/*
* call-seq:
* OneofDescriptor.each(&block) => nil
*
* Iterates through fields in this oneof, yielding to the block on each one.
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
for (RubyFieldDescriptor field : fields) {
block.yieldSpecific(context, field);
}
return context.runtime.getNil();
}
public DescriptorProtos.OneofDescriptorProto build(int index) {
for (RubyFieldDescriptor field: fields) {
field.setOneofIndex(index);
}
return this.builder.build();
}
protected Collection<RubyFieldDescriptor> getFields() {
return fields;
}
protected Descriptors.OneofDescriptor getOneofDescriptor() {
RubyFieldDescriptor fieldDescriptor = fields.get(0);
return fieldDescriptor.getFieldDef().getContainingOneof();
}
private IRubyObject name;
private DescriptorProtos.OneofDescriptorProto.Builder builder;
private List<RubyFieldDescriptor> fields;
}

@ -0,0 +1,118 @@
/*
* 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 org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyModule(name = "Protobuf")
public class RubyProtobuf {
public static void createProtobuf(Ruby runtime) {
RubyModule mGoogle = runtime.getModule("Google");
RubyModule mProtobuf = mGoogle.defineModuleUnder("Protobuf");
mProtobuf.defineAnnotatedMethods(RubyProtobuf.class);
}
/*
* call-seq:
* Google::Protobuf.encode(msg) => bytes
*
* Encodes the given message object to protocol buffers wire format. This is an
* alternative to the #encode method on msg's class.
*/
@JRubyMethod(meta = true)
public static IRubyObject encode(ThreadContext context, IRubyObject self, IRubyObject message) {
return RubyMessage.encode(context, message.getMetaClass(), message);
}
/*
* call-seq:
* Google::Protobuf.decode(class, bytes) => msg
*
* Decodes the given bytes as protocol buffers wire format under the
* interpretation given by the given class's message definition. This is an
* alternative to the #decode method on the given class.
*/
@JRubyMethod(meta = true)
public static IRubyObject decode(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) {
return RubyMessage.decode(context, klazz, message);
}
/*
* call-seq:
* Google::Protobuf.encode_json(msg) => json_string
*
* Encodes the given message object to its JSON representation. This is an
* alternative to the #encode_json method on msg's class.
*/
@JRubyMethod(name = "encode_json", meta = true)
public static IRubyObject encodeJson(ThreadContext context, IRubyObject self, IRubyObject message) {
return RubyMessage.encodeJson(context, message.getMetaClass(), message);
}
/*
* call-seq:
* Google::Protobuf.decode_json(class, json_string) => msg
*
* Decodes the given JSON string under the interpretation given by the given
* class's message definition. This is an alternative to the #decode_json method
* on the given class.
*/
@JRubyMethod(name = "decode_json", meta = true)
public static IRubyObject decodeJson(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) {
return RubyMessage.decodeJson(context, klazz, message);
}
/*
* call-seq:
* Google::Protobuf.deep_copy(obj) => copy_of_obj
*
* Performs a deep copy of either a RepeatedField instance or a message object,
* recursively copying its members.
*/
@JRubyMethod(name = "deep_copy", meta = true)
public static IRubyObject deepCopy(ThreadContext context, IRubyObject self, IRubyObject message) {
if (message instanceof RubyMessage) {
return ((RubyMessage) message).deepCopy(context);
} else if (message instanceof RubyRepeatedField) {
return ((RubyRepeatedField) message).deepCopy(context);
} else {
return ((RubyMap) message).deepCopy(context);
}
}
}

@ -0,0 +1,391 @@
/*
* 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.Descriptors;
import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name = "RepeatedClass", include = "Enumerable")
public class RubyRepeatedField extends RubyObject {
public static void createRubyRepeatedField(Ruby runtime) {
RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
RubyClass cRepeatedField = mProtobuf.defineClassUnder("RepeatedField", runtime.getObject(),
new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new RubyRepeatedField(runtime, klazz);
}
});
cRepeatedField.defineAnnotatedMethods(RubyRepeatedField.class);
cRepeatedField.includeModule(runtime.getEnumerable());
}
public RubyRepeatedField(Ruby runtime, RubyClass klazz) {
super(runtime, klazz);
}
public RubyRepeatedField(Ruby runtime, RubyClass klazz, Descriptors.FieldDescriptor.Type fieldType, IRubyObject typeClass) {
this(runtime, klazz);
this.fieldType = fieldType;
this.storage = runtime.newArray();
this.typeClass = typeClass;
}
@JRubyMethod(required = 1, optional = 2)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
this.storage = runtime.newArray();
IRubyObject ary = null;
if (!(args[0] instanceof RubySymbol)) {
throw runtime.newArgumentError("Expected Symbol for type name");
}
this.fieldType = Utils.rubyToFieldType(args[0]);
if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE
|| fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
if (args.length < 2)
throw runtime.newArgumentError("Expected at least 2 arguments for message/enum");
typeClass = args[1];
if (args.length > 2)
ary = args[2];
Utils.validateTypeClass(context, fieldType, typeClass);
} else {
if (args.length > 2)
throw runtime.newArgumentError("Too many arguments: expected 1 or 2");
if (args.length > 1)
ary = args[1];
}
if (ary != null) {
RubyArray arr = ary.convertToArray();
for (int i = 0; i < arr.size(); i++) {
this.storage.add(arr.eltInternal(i));
}
}
return this;
}
/*
* call-seq:
* RepeatedField.[]=(index, value)
*
* Sets the element at the given index. On out-of-bounds assignments, extends
* the array and fills the hole (if any) with default values.
*/
@JRubyMethod(name = "[]=")
public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) {
Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
this.storage.set(RubyNumeric.num2int(index), value);
return context.runtime.getNil();
}
/*
* call-seq:
* RepeatedField.[](index) => value
*
* Accesses the element at the given index. Throws an exception on out-of-bounds
* errors.
*/
@JRubyMethod(name = "[]")
public IRubyObject index(ThreadContext context, IRubyObject index) {
return this.storage.eltInternal(RubyNumeric.num2int(index));
}
/*
* call-seq:
* RepeatedField.insert(*args)
*
* Pushes each arg in turn onto the end of the repeated field.
*/
@JRubyMethod(rest = true)
public IRubyObject insert(ThreadContext context, IRubyObject[] args) {
for (int i = 0; i < args.length; i++) {
Utils.checkType(context, fieldType, args[i], (RubyModule) typeClass);
this.storage.add(args[i]);
}
return context.runtime.getNil();
}
/*
* call-seq:
* RepeatedField.push(value)
*
* Adds a new element to the repeated field.
*/
@JRubyMethod(name = {"push", "<<"})
public IRubyObject push(ThreadContext context, IRubyObject value) {
Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
this.storage.add(value);
return this;
}
/*
* call-seq:
* RepeatedField.pop => value
*
* Removes the last element and returns it. Throws an exception if the repeated
* field is empty.
*/
@JRubyMethod
public IRubyObject pop(ThreadContext context) {
IRubyObject ret = this.storage.last();
this.storage.remove(ret);
return ret;
}
/*
* call-seq:
* RepeatedField.replace(list)
*
* Replaces the contents of the repeated field with the given list of elements.
*/
@JRubyMethod
public IRubyObject replace(ThreadContext context, IRubyObject list) {
RubyArray arr = (RubyArray) list;
checkArrayElementType(context, arr);
this.storage = arr;
return context.runtime.getNil();
}
/*
* call-seq:
* RepeatedField.clear
*
* Clears (removes all elements from) this repeated field.
*/
@JRubyMethod
public IRubyObject clear(ThreadContext context) {
this.storage.clear();
return context.runtime.getNil();
}
/*
* call-seq:
* RepeatedField.length
*
* Returns the length of this repeated field.
*/
@JRubyMethod(name = {"count", "length"})
public IRubyObject length(ThreadContext context) {
return context.runtime.newFixnum(this.storage.size());
}
/*
* call-seq:
* RepeatedField.+(other) => repeated field
*
* Returns a new repeated field that contains the concatenated list of this
* repeated field's elements and other's elements. The other (second) list may
* be either another repeated field or a Ruby array.
*/
@JRubyMethod(name = "+")
public IRubyObject plus(ThreadContext context, IRubyObject list) {
RubyRepeatedField dup = (RubyRepeatedField) dup(context);
if (list instanceof RubyArray) {
checkArrayElementType(context, (RubyArray) list);
dup.storage.addAll((RubyArray) list);
} else {
RubyRepeatedField repeatedField = (RubyRepeatedField) list;
if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && !
typeClass.equals(repeatedField.typeClass)))
throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type.");
dup.storage.addAll((RubyArray) repeatedField.toArray(context));
}
return dup;
}
/*
* call-seq:
* RepeatedField.hash => hash_value
*
* Returns a hash value computed from this repeated field's elements.
*/
@JRubyMethod
public IRubyObject hash(ThreadContext context) {
int hashCode = System.identityHashCode(this.storage);
return context.runtime.newFixnum(hashCode);
}
/*
* call-seq:
* RepeatedField.==(other) => boolean
*
* Compares this repeated field to another. Repeated fields are equal if their
* element types are equal, their lengths are equal, and each element is equal.
* Elements are compared as per normal Ruby semantics, by calling their :==
* methods (or performing a more efficient comparison for primitive types).
*/
@JRubyMethod(name = "==")
public IRubyObject eq(ThreadContext context, IRubyObject other) {
return this.toArray(context).op_equal(context, other);
}
/*
* call-seq:
* RepeatedField.each(&block)
*
* Invokes the block once for each element of the repeated field. RepeatedField
* also includes Enumerable; combined with this method, the repeated field thus
* acts like an ordinary Ruby sequence.
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
this.storage.each(context, block);
return context.runtime.getNil();
}
@JRubyMethod(name = {"to_ary", "to_a"})
public IRubyObject toArray(ThreadContext context) {
for (int i = 0; i < this.storage.size(); i++) {
IRubyObject defaultValue = defaultValue(context);
if (storage.eltInternal(i).isNil()) {
storage.set(i, defaultValue);
}
}
return this.storage;
}
/*
* call-seq:
* RepeatedField.dup => repeated_field
*
* Duplicates this repeated field with a shallow copy. References to all
* non-primitive element objects (e.g., submessages) are shared.
*/
@JRubyMethod
public IRubyObject dup(ThreadContext context) {
RubyRepeatedField dup = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
for (int i = 0; i < this.storage.size(); i++) {
dup.push(context, this.storage.eltInternal(i));
}
return dup;
}
/*
* call-seq:
* RepeatedField.inspect => string
*
* Returns a string representing this repeated field's elements. It will be
* formated as "[<element>, <element>, ...]", with each element's string
* representation computed by its own #inspect method.
*/
@JRubyMethod
public IRubyObject inspect() {
StringBuilder str = new StringBuilder("[");
for (int i = 0; i < this.storage.size(); i++) {
str.append(storage.eltInternal(i).inspect());
str.append(", ");
}
if (str.length() > 1) {
str.replace(str.length() - 2, str.length(), "]");
} else {
str.append("]");
}
return getRuntime().newString(str.toString());
}
// Java API
protected IRubyObject get(int index) {
return this.storage.eltInternal(index);
}
protected RubyRepeatedField deepCopy(ThreadContext context) {
RubyRepeatedField copy = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
for (int i = 0; i < size(); i++) {
IRubyObject value = storage.eltInternal(i);
if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
copy.storage.add(((RubyMessage) value).deepCopy(context));
} else {
copy.storage.add(value);
}
}
return copy;
}
protected int size() {
return this.storage.size();
}
private IRubyObject defaultValue(ThreadContext context) {
SentinelOuterClass.Sentinel sentinel = SentinelOuterClass.Sentinel.getDefaultInstance();
Object value;
switch (fieldType) {
case INT32:
value = sentinel.getDefaultInt32();
break;
case INT64:
value = sentinel.getDefaultInt64();
break;
case UINT32:
value = sentinel.getDefaultUnit32();
break;
case UINT64:
value = sentinel.getDefaultUint64();
break;
case FLOAT:
value = sentinel.getDefaultFloat();
break;
case DOUBLE:
value = sentinel.getDefaultDouble();
break;
case BOOL:
value = sentinel.getDefaultBool();
break;
case BYTES:
value = sentinel.getDefaultBytes();
break;
case STRING:
value = sentinel.getDefaultString();
break;
default:
return context.runtime.getNil();
}
return Utils.wrapPrimaryValue(context, fieldType, value);
}
private void checkArrayElementType(ThreadContext context, RubyArray arr) {
for (int i = 0; i < arr.getLength(); i++) {
Utils.checkType(context, fieldType, arr.eltInternal(i), (RubyModule) typeClass);
}
}
private RubyArray storage;
private Descriptors.FieldDescriptor.Type fieldType;
private IRubyObject typeClass;
}

@ -0,0 +1,776 @@
/*
* 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.
*/
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: sentinel.proto
package com.google.protobuf.jruby;
public final class SentinelOuterClass {
private SentinelOuterClass() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
}
public interface SentinelOrBuilder extends
// @@protoc_insertion_point(interface_extends:com.google.protobuf.jruby.Sentinel)
com.google.protobuf.MessageOrBuilder {
/**
* <code>optional int32 default_int32 = 1;</code>
*/
int getDefaultInt32();
/**
* <code>optional int64 default_int64 = 2;</code>
*/
long getDefaultInt64();
/**
* <code>optional uint32 default_unit32 = 3;</code>
*/
int getDefaultUnit32();
/**
* <code>optional uint64 default_uint64 = 4;</code>
*/
long getDefaultUint64();
/**
* <code>optional string default_string = 5;</code>
*/
java.lang.String getDefaultString();
/**
* <code>optional string default_string = 5;</code>
*/
com.google.protobuf.ByteString
getDefaultStringBytes();
/**
* <code>optional bool default_bool = 6;</code>
*/
boolean getDefaultBool();
/**
* <code>optional float default_float = 7;</code>
*/
float getDefaultFloat();
/**
* <code>optional double default_double = 8;</code>
*/
double getDefaultDouble();
/**
* <code>optional bytes default_bytes = 9;</code>
*/
com.google.protobuf.ByteString getDefaultBytes();
}
/**
* Protobuf type {@code com.google.protobuf.jruby.Sentinel}
*/
public static final class Sentinel extends
com.google.protobuf.GeneratedMessage implements
// @@protoc_insertion_point(message_implements:com.google.protobuf.jruby.Sentinel)
SentinelOrBuilder {
// Use Sentinel.newBuilder() to construct.
private Sentinel(com.google.protobuf.GeneratedMessage.Builder builder) {
super(builder);
}
private Sentinel() {
defaultInt32_ = 0;
defaultInt64_ = 0L;
defaultUnit32_ = 0;
defaultUint64_ = 0L;
defaultString_ = "";
defaultBool_ = false;
defaultFloat_ = 0F;
defaultDouble_ = 0D;
defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return com.google.protobuf.UnknownFieldSet.getDefaultInstance();
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
}
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
}
public static final com.google.protobuf.Parser<Sentinel> PARSER =
new com.google.protobuf.AbstractParser<Sentinel>() {
public Sentinel parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
Builder builder = newBuilder();
try {
builder.mergeFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(
e.getMessage()).setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
}
};
@java.lang.Override
public com.google.protobuf.Parser<Sentinel> getParserForType() {
return PARSER;
}
public static final int DEFAULT_INT32_FIELD_NUMBER = 1;
private int defaultInt32_;
/**
* <code>optional int32 default_int32 = 1;</code>
*/
public int getDefaultInt32() {
return defaultInt32_;
}
public static final int DEFAULT_INT64_FIELD_NUMBER = 2;
private long defaultInt64_;
/**
* <code>optional int64 default_int64 = 2;</code>
*/
public long getDefaultInt64() {
return defaultInt64_;
}
public static final int DEFAULT_UNIT32_FIELD_NUMBER = 3;
private int defaultUnit32_;
/**
* <code>optional uint32 default_unit32 = 3;</code>
*/
public int getDefaultUnit32() {
return defaultUnit32_;
}
public static final int DEFAULT_UINT64_FIELD_NUMBER = 4;
private long defaultUint64_;
/**
* <code>optional uint64 default_uint64 = 4;</code>
*/
public long getDefaultUint64() {
return defaultUint64_;
}
public static final int DEFAULT_STRING_FIELD_NUMBER = 5;
private java.lang.Object defaultString_;
/**
* <code>optional string default_string = 5;</code>
*/
public java.lang.String getDefaultString() {
java.lang.Object ref = defaultString_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
if (bs.isValidUtf8()) {
defaultString_ = s;
}
return s;
}
}
/**
* <code>optional string default_string = 5;</code>
*/
public com.google.protobuf.ByteString
getDefaultStringBytes() {
java.lang.Object ref = defaultString_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
defaultString_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
public static final int DEFAULT_BOOL_FIELD_NUMBER = 6;
private boolean defaultBool_;
/**
* <code>optional bool default_bool = 6;</code>
*/
public boolean getDefaultBool() {
return defaultBool_;
}
public static final int DEFAULT_FLOAT_FIELD_NUMBER = 7;
private float defaultFloat_;
/**
* <code>optional float default_float = 7;</code>
*/
public float getDefaultFloat() {
return defaultFloat_;
}
public static final int DEFAULT_DOUBLE_FIELD_NUMBER = 8;
private double defaultDouble_;
/**
* <code>optional double default_double = 8;</code>
*/
public double getDefaultDouble() {
return defaultDouble_;
}
public static final int DEFAULT_BYTES_FIELD_NUMBER = 9;
private com.google.protobuf.ByteString defaultBytes_;
/**
* <code>optional bytes default_bytes = 9;</code>
*/
public com.google.protobuf.ByteString getDefaultBytes() {
return defaultBytes_;
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(java.io.InputStream input)
throws java.io.IOException {
return PARSER.parseFrom(input);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseFrom(input, extensionRegistry);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return PARSER.parseDelimitedFrom(input);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseDelimitedFrom(input, extensionRegistry);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return PARSER.parseFrom(input);
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseFrom(input, extensionRegistry);
}
public static Builder newBuilder() { return new Builder(); }
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder(com.google.protobuf.jruby.SentinelOuterClass.Sentinel prototype) {
return newBuilder().mergeFrom(prototype);
}
public Builder toBuilder() { return newBuilder(this); }
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code com.google.protobuf.jruby.Sentinel}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:com.google.protobuf.jruby.Sentinel)
com.google.protobuf.jruby.SentinelOuterClass.SentinelOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
}
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
}
// Construct using com.google.protobuf.jruby.SentinelOuterClass.Sentinel.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
}
}
public Builder clear() {
super.clear();
defaultInt32_ = 0;
defaultInt64_ = 0L;
defaultUnit32_ = 0;
defaultUint64_ = 0L;
defaultString_ = "";
defaultBool_ = false;
defaultFloat_ = 0F;
defaultDouble_ = 0D;
defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
return this;
}
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
}
public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
return com.google.protobuf.jruby.SentinelOuterClass.Sentinel.getDefaultInstance();
}
public com.google.protobuf.jruby.SentinelOuterClass.Sentinel build() {
com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
public com.google.protobuf.jruby.SentinelOuterClass.Sentinel buildPartial() {
com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(this);
result.defaultInt32_ = defaultInt32_;
result.defaultInt64_ = defaultInt64_;
result.defaultUnit32_ = defaultUnit32_;
result.defaultUint64_ = defaultUint64_;
result.defaultString_ = defaultString_;
result.defaultBool_ = defaultBool_;
result.defaultFloat_ = defaultFloat_;
result.defaultDouble_ = defaultDouble_;
result.defaultBytes_ = defaultBytes_;
onBuilt();
return result;
}
private int defaultInt32_ ;
/**
* <code>optional int32 default_int32 = 1;</code>
*/
public int getDefaultInt32() {
return defaultInt32_;
}
/**
* <code>optional int32 default_int32 = 1;</code>
*/
public Builder setDefaultInt32(int value) {
defaultInt32_ = value;
onChanged();
return this;
}
/**
* <code>optional int32 default_int32 = 1;</code>
*/
public Builder clearDefaultInt32() {
defaultInt32_ = 0;
onChanged();
return this;
}
private long defaultInt64_ ;
/**
* <code>optional int64 default_int64 = 2;</code>
*/
public long getDefaultInt64() {
return defaultInt64_;
}
/**
* <code>optional int64 default_int64 = 2;</code>
*/
public Builder setDefaultInt64(long value) {
defaultInt64_ = value;
onChanged();
return this;
}
/**
* <code>optional int64 default_int64 = 2;</code>
*/
public Builder clearDefaultInt64() {
defaultInt64_ = 0L;
onChanged();
return this;
}
private int defaultUnit32_ ;
/**
* <code>optional uint32 default_unit32 = 3;</code>
*/
public int getDefaultUnit32() {
return defaultUnit32_;
}
/**
* <code>optional uint32 default_unit32 = 3;</code>
*/
public Builder setDefaultUnit32(int value) {
defaultUnit32_ = value;
onChanged();
return this;
}
/**
* <code>optional uint32 default_unit32 = 3;</code>
*/
public Builder clearDefaultUnit32() {
defaultUnit32_ = 0;
onChanged();
return this;
}
private long defaultUint64_ ;
/**
* <code>optional uint64 default_uint64 = 4;</code>
*/
public long getDefaultUint64() {
return defaultUint64_;
}
/**
* <code>optional uint64 default_uint64 = 4;</code>
*/
public Builder setDefaultUint64(long value) {
defaultUint64_ = value;
onChanged();
return this;
}
/**
* <code>optional uint64 default_uint64 = 4;</code>
*/
public Builder clearDefaultUint64() {
defaultUint64_ = 0L;
onChanged();
return this;
}
private java.lang.Object defaultString_ = "";
/**
* <code>optional string default_string = 5;</code>
*/
public java.lang.String getDefaultString() {
java.lang.Object ref = defaultString_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
if (bs.isValidUtf8()) {
defaultString_ = s;
}
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>optional string default_string = 5;</code>
*/
public com.google.protobuf.ByteString
getDefaultStringBytes() {
java.lang.Object ref = defaultString_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
defaultString_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>optional string default_string = 5;</code>
*/
public Builder setDefaultString(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
defaultString_ = value;
onChanged();
return this;
}
/**
* <code>optional string default_string = 5;</code>
*/
public Builder clearDefaultString() {
defaultString_ = getDefaultInstance().getDefaultString();
onChanged();
return this;
}
/**
* <code>optional string default_string = 5;</code>
*/
public Builder setDefaultStringBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
defaultString_ = value;
onChanged();
return this;
}
private boolean defaultBool_ ;
/**
* <code>optional bool default_bool = 6;</code>
*/
public boolean getDefaultBool() {
return defaultBool_;
}
/**
* <code>optional bool default_bool = 6;</code>
*/
public Builder setDefaultBool(boolean value) {
defaultBool_ = value;
onChanged();
return this;
}
/**
* <code>optional bool default_bool = 6;</code>
*/
public Builder clearDefaultBool() {
defaultBool_ = false;
onChanged();
return this;
}
private float defaultFloat_ ;
/**
* <code>optional float default_float = 7;</code>
*/
public float getDefaultFloat() {
return defaultFloat_;
}
/**
* <code>optional float default_float = 7;</code>
*/
public Builder setDefaultFloat(float value) {
defaultFloat_ = value;
onChanged();
return this;
}
/**
* <code>optional float default_float = 7;</code>
*/
public Builder clearDefaultFloat() {
defaultFloat_ = 0F;
onChanged();
return this;
}
private double defaultDouble_ ;
/**
* <code>optional double default_double = 8;</code>
*/
public double getDefaultDouble() {
return defaultDouble_;
}
/**
* <code>optional double default_double = 8;</code>
*/
public Builder setDefaultDouble(double value) {
defaultDouble_ = value;
onChanged();
return this;
}
/**
* <code>optional double default_double = 8;</code>
*/
public Builder clearDefaultDouble() {
defaultDouble_ = 0D;
onChanged();
return this;
}
private com.google.protobuf.ByteString defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
/**
* <code>optional bytes default_bytes = 9;</code>
*/
public com.google.protobuf.ByteString getDefaultBytes() {
return defaultBytes_;
}
/**
* <code>optional bytes default_bytes = 9;</code>
*/
public Builder setDefaultBytes(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
defaultBytes_ = value;
onChanged();
return this;
}
/**
* <code>optional bytes default_bytes = 9;</code>
*/
public Builder clearDefaultBytes() {
defaultBytes_ = getDefaultInstance().getDefaultBytes();
onChanged();
return this;
}
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return this;
}
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return this;
}
// @@protoc_insertion_point(builder_scope:com.google.protobuf.jruby.Sentinel)
}
// @@protoc_insertion_point(class_scope:com.google.protobuf.jruby.Sentinel)
private static final com.google.protobuf.jruby.SentinelOuterClass.Sentinel defaultInstance;static {
defaultInstance = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel();
}
public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstance() {
return defaultInstance;
}
public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
return defaultInstance;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
private static
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\016sentinel.proto\022\031com.google.protobuf.jr" +
"uby\"\334\001\n\010Sentinel\022\025\n\rdefault_int32\030\001 \001(\005\022" +
"\025\n\rdefault_int64\030\002 \001(\003\022\026\n\016default_unit32" +
"\030\003 \001(\r\022\026\n\016default_uint64\030\004 \001(\004\022\026\n\016defaul" +
"t_string\030\005 \001(\t\022\024\n\014default_bool\030\006 \001(\010\022\025\n\r" +
"default_float\030\007 \001(\002\022\026\n\016default_double\030\010 " +
"\001(\001\022\025\n\rdefault_bytes\030\t \001(\014B\002H\002b\006proto3"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
}, assigner);
internal_static_com_google_protobuf_jruby_Sentinel_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_com_google_protobuf_jruby_Sentinel_descriptor,
new java.lang.String[] { "DefaultInt32", "DefaultInt64", "DefaultUnit32", "DefaultUint64", "DefaultString", "DefaultBool", "DefaultFloat", "DefaultDouble", "DefaultBytes", });
}
// @@protoc_insertion_point(outer_class_scope)
}

@ -0,0 +1,300 @@
/*
* 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.ByteString;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.Descriptors;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.*;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import java.math.BigInteger;
public class Utils {
public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
}
public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) {
return fieldTypeToRuby(context, type.name());
}
public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) {
return fieldTypeToRuby(context, type.name());
}
private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
}
public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType,
IRubyObject value, RubyModule typeClass) {
Ruby runtime = context.runtime;
Object val;
switch(fieldType) {
case INT32:
case INT64:
case UINT32:
case UINT64:
if (!isRubyNum(value)) {
throw runtime.newTypeError("Expected number type for integral field.");
}
switch(fieldType) {
case INT32:
RubyNumeric.num2int(value);
break;
case INT64:
RubyNumeric.num2long(value);
break;
case UINT32:
num2uint(value);
break;
default:
num2ulong(context.runtime, value);
break;
}
checkIntTypePrecision(context, fieldType, value);
break;
case FLOAT:
if (!isRubyNum(value))
throw runtime.newTypeError("Expected number type for float field.");
break;
case DOUBLE:
if (!isRubyNum(value))
throw runtime.newTypeError("Expected number type for double field.");
break;
case BOOL:
if (!(value instanceof RubyBoolean))
throw runtime.newTypeError("Invalid argument for boolean field.");
break;
case BYTES:
case STRING:
validateStringEncoding(context.runtime, fieldType, value);
break;
case MESSAGE:
if (value.getMetaClass() != typeClass) {
throw runtime.newTypeError(value, typeClass);
}
break;
case ENUM:
if (value instanceof RubySymbol) {
Descriptors.EnumDescriptor enumDescriptor =
((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor();
val = enumDescriptor.findValueByName(value.asJavaString());
if (val == null)
throw runtime.newRangeError("Enum value " + value + " is not found.");
} else if(!isRubyNum(value)) {
throw runtime.newTypeError("Expected number or symbol type for enum field.");
}
break;
default:
break;
}
}
public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) {
Ruby runtime = context.runtime;
switch (fieldType) {
case INT32:
return runtime.newFixnum((Integer) value);
case INT64:
return runtime.newFixnum((Long) value);
case UINT32:
return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
case UINT64:
long ret = (Long) value;
return ret >= 0 ? runtime.newFixnum(ret) :
RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
case FLOAT:
return runtime.newFloat((Float) value);
case DOUBLE:
return runtime.newFloat((Double) value);
case BOOL:
return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
case BYTES:
return runtime.newString(((ByteString) value).toStringUtf8());
case STRING:
return runtime.newString(value.toString());
default:
return runtime.getNil();
}
}
public static int num2uint(IRubyObject value) {
long longVal = RubyNumeric.num2long(value);
if (longVal > UINT_MAX)
throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
long num = longVal;
if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
// encode to UINT32
num = (-longVal ^ (-1l >>> 32) ) + 1;
RubyNumeric.checkInt(value, num);
return (int) num;
}
public static long num2ulong(Ruby runtime, IRubyObject value) {
if (value instanceof RubyFloat) {
RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
return RubyBignum.big2ulong(bignum);
} else if (value instanceof RubyBignum) {
return RubyBignum.big2ulong((RubyBignum) value);
} else {
return RubyNumeric.num2long(value);
}
}
public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
if (!(value instanceof RubyString))
throw runtime.newTypeError("Invalid argument for string field.");
Encoding encoding = ((RubyString) value).getEncoding();
switch(type) {
case BYTES:
if (encoding != ASCIIEncoding.INSTANCE)
throw runtime.newTypeError("Encoding for bytes fields" +
" must be \"ASCII-8BIT\", but was " + encoding);
break;
case STRING:
if (encoding != UTF8Encoding.INSTANCE
&& encoding != USASCIIEncoding.INSTANCE)
throw runtime.newTypeError("Encoding for string fields" +
" must be \"UTF-8\" or \"ASCII\", but was " + encoding);
break;
default:
break;
}
}
public static void checkNameAvailability(ThreadContext context, String name) {
if (context.runtime.getObject().getConstantAt(name) != null)
throw context.runtime.newNameError(name + " is already defined", name);
}
/**
* Replace invalid "." in descriptor with __DOT__
* @param name
* @return
*/
public static String escapeIdentifier(String name) {
return name.replace(".", BADNAME_REPLACEMENT);
}
/**
* Replace __DOT__ in descriptor name with "."
* @param name
* @return
*/
public static String unescapeIdentifier(String name) {
return name.replace(BADNAME_REPLACEMENT, ".");
}
public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) {
return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
fieldDescriptor.isRepeated() &&
fieldDescriptor.getMessageType().getOptions().getMapEntry();
}
public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name,
IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) {
Ruby runtime = context.runtime;
RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
fieldDef.setLabel(context, runtime.newString(label));
fieldDef.setName(context, name);
fieldDef.setType(context, type);
fieldDef.setNumber(context, number);
if (!typeClass.isNil()) {
if (!(typeClass instanceof RubyString)) {
throw runtime.newArgumentError("expected string for type class");
}
fieldDef.setSubmsgName(context, typeClass);
}
return fieldDef;
}
protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
if (value instanceof RubyFloat) {
double doubleVal = RubyNumeric.num2dbl(value);
if (Math.floor(doubleVal) != doubleVal) {
throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field.");
}
}
if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) {
if (RubyNumeric.num2dbl(value) < 0) {
throw context.runtime.newRangeError("Assigning negative value to unsigned integer field.");
}
}
}
protected static boolean isRubyNum(Object value) {
return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
}
protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
Ruby runtime = context.runtime;
if (!(value instanceof RubyModule)) {
throw runtime.newArgumentError("TypeClass has incorrect type");
}
RubyModule klass = (RubyModule) value;
IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
if (descriptor.isNil()) {
throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
"class or enum as returned by the DescriptorPool.");
}
if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
if (! (descriptor instanceof RubyDescriptor)) {
throw runtime.newArgumentError("Descriptor has an incorrect type");
}
} else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
if (! (descriptor instanceof RubyEnumDescriptor)) {
throw runtime.newArgumentError("Descriptor has an incorrect type");
}
}
}
public static String BADNAME_REPLACEMENT = "__DOT__";
public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
public static String EQUAL_SIGN = "=";
private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
private static long UINT_MAX = 0xffffffffl;
}

@ -0,0 +1,60 @@
/*
* 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 google;
import com.google.protobuf.jruby.*;
import org.jruby.Ruby;
import org.jruby.runtime.load.BasicLibraryService;
import java.io.IOException;
public class ProtobufJavaService implements BasicLibraryService {
@Override
public boolean basicLoad(Ruby ruby) throws IOException {
ruby.defineModule("Google");
RubyProtobuf.createProtobuf(ruby);
RubyDescriptor.createRubyDescriptor(ruby);
RubyBuilder.createRubyBuilder(ruby);
RubyFieldDescriptor.createRubyFileDescriptor(ruby);
RubyMessageBuilderContext.createRubyMessageBuilderContext(ruby);
RubyEnumDescriptor.createRubyEnumDescriptor(ruby);
RubyEnumBuilderContext.createRubyEnumBuilderContext(ruby);
RubyDescriptorPool.createRubyDescriptorPool(ruby);
RubyRepeatedField.createRubyRepeatedField(ruby);
RubyFieldDescriptor.createRubyFileDescriptor(ruby);
RubyMap.createRubyMap(ruby);
RubyOneofDescriptor.createRubyOneofDescriptor(ruby);
RubyOneofBuilderContext.createRubyOneofBuilderContext(ruby);
return true;
}
}

@ -0,0 +1,15 @@
syntax = "proto3";
package com.google.protobuf.jruby;
option optimize_for = CODE_SIZE;
message Sentinel {
optional int32 default_int32 = 1;
optional int64 default_int64 = 2;
optional uint32 default_unit32 = 3;
optional uint64 default_uint64 = 4;
optional string default_string = 5;
optional bool default_bool = 6;
optional float default_float = 7;
optional double default_double = 8;
optional bytes default_bytes = 9;
}

@ -972,6 +972,7 @@ module BasicTest
end end
def test_json def test_json
skip("Unimplemented") if RUBY_PLATFORM == "java"
m = TestMessage.new(:optional_int32 => 1234, m = TestMessage.new(:optional_int32 => 1234,
:optional_int64 => -0x1_0000_0000, :optional_int64 => -0x1_0000_0000,
:optional_uint32 => 0x8000_0000, :optional_uint32 => 0x8000_0000,
@ -994,6 +995,7 @@ module BasicTest
end end
def test_json_maps def test_json_maps
skip("Unimplemented") if RUBY_PLATFORM == "java"
m = MapMessage.new(:map_string_int32 => {"a" => 1}) m = MapMessage.new(:map_string_int32 => {"a" => 1})
expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}' expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}'
assert MapMessage.encode_json(m) == expected assert MapMessage.encode_json(m) == expected

@ -30,7 +30,7 @@ module StressTest
100_000.times do 100_000.times do
mnew = TestMessage.decode(data) mnew = TestMessage.decode(data)
mnew = mnew.dup mnew = mnew.dup
assert mnew.inspect == m.inspect assert_equal mnew.inspect, m.inspect
assert TestMessage.encode(mnew) == data assert TestMessage.encode(mnew) == data
end end
end end

Loading…
Cancel
Save