parent
b6993a9060
commit
3581d85ced
21 changed files with 264 additions and 1832 deletions
@ -1,147 +0,0 @@ |
||||
/* |
||||
* 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 internal = runtime.getClassFromPath("Google::Protobuf::Internal"); |
||||
RubyClass cBuilder = internal.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.cFileBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Internal::FileBuilderContext"); |
||||
} |
||||
|
||||
/* |
||||
* 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, IRubyObject descriptorPool) { |
||||
this.descriptorPool = (RubyDescriptorPool) descriptorPool; |
||||
return this; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* Builder.add_message(name, &block) |
||||
* |
||||
* Old and deprecated way to create a new descriptor. |
||||
* See FileBuilderContext.add_message for the recommended way. |
||||
* |
||||
* Exists for backwards compatibility to allow building descriptor pool for |
||||
* files generated by protoc which don't add messages within "add_file" block. |
||||
* Descriptors created this way get assigned to a default empty FileDescriptor. |
||||
*/ |
||||
@JRubyMethod(name = "add_message") |
||||
public IRubyObject addMessage(ThreadContext context, IRubyObject name, Block block) { |
||||
ensureDefaultFileBuilder(context); |
||||
defaultFileBuilder.addMessage(context, name, block); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* Builder.add_enum(name, &block) |
||||
* |
||||
* Old and deprecated way to create a new enum descriptor. |
||||
* See FileBuilderContext.add_enum for the recommended way. |
||||
* |
||||
* Exists for backwards compatibility to allow building descriptor pool for |
||||
* files generated by protoc which don't add enums within "add_file" block. |
||||
* Enum descriptors created this way get assigned to a default empty |
||||
* FileDescriptor. |
||||
*/ |
||||
@JRubyMethod(name = "add_enum") |
||||
public IRubyObject addEnum(ThreadContext context, IRubyObject name, Block block) { |
||||
ensureDefaultFileBuilder(context); |
||||
defaultFileBuilder.addEnum(context, name, block); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* Builder.add_file(name, options = nil, &block) |
||||
* |
||||
* Creates a new, file descriptor with the given name and options and invokes |
||||
* the block in the context of a FileBuilderContext on that descriptor. The |
||||
* block can then call FileBuilderContext#add_message or |
||||
* FileBuilderContext#add_enum to define new messages or enums, respectively. |
||||
* |
||||
* This is the recommended, idiomatic way to build file descriptors. |
||||
*/ |
||||
@JRubyMethod(name = "add_file") |
||||
public IRubyObject addFile(ThreadContext context, IRubyObject name, IRubyObject options, Block block) { |
||||
RubyFileBuilderContext ctx = (RubyFileBuilderContext) cFileBuilderContext.newInstance(context, descriptorPool, name, options, Block.NULL_BLOCK); |
||||
ctx.instance_eval(context, block); |
||||
ctx.build(context); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* Used to trigger the build when using the deprecated syntax |
||||
*/ |
||||
protected void build(ThreadContext context) { |
||||
if (defaultFileBuilder != null) { |
||||
defaultFileBuilder.build(context); |
||||
} |
||||
} |
||||
|
||||
private void ensureDefaultFileBuilder(ThreadContext context) { |
||||
if (defaultFileBuilder == null) { |
||||
this.defaultFileBuilder = (RubyFileBuilderContext) cFileBuilderContext.newInstance(context, descriptorPool, context.runtime.newString("ruby_default_file.proto"), Block.NULL_BLOCK); |
||||
} |
||||
} |
||||
|
||||
private RubyClass cFileBuilderContext; |
||||
private RubyDescriptorPool descriptorPool; |
||||
private RubyFileBuilderContext defaultFileBuilder; |
||||
} |
@ -1,91 +0,0 @@ |
||||
/* |
||||
* 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.EnumDescriptorProto; |
||||
import org.jruby.Ruby; |
||||
import org.jruby.RubyClass; |
||||
import org.jruby.RubyModule; |
||||
import org.jruby.RubyNumeric; |
||||
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 internal = runtime.getClassFromPath("Google::Protobuf::Internal"); |
||||
RubyClass cEnumBuilderContext = internal.defineClassUnder("EnumBuilderContext", runtime.getObject(), new ObjectAllocator() { |
||||
@Override |
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
||||
return new RubyEnumBuilderContext(runtime, klazz); |
||||
} |
||||
}); |
||||
cEnumBuilderContext.defineAnnotatedMethods(RubyEnumBuilderContext.class); |
||||
} |
||||
|
||||
public RubyEnumBuilderContext(Ruby ruby, RubyClass klazz) { |
||||
super(ruby, klazz); |
||||
} |
||||
|
||||
@JRubyMethod |
||||
public IRubyObject initialize(ThreadContext context, IRubyObject fileBuilderContext, IRubyObject name) { |
||||
this.fileBuilderContext = (RubyFileBuilderContext) fileBuilderContext; |
||||
this.builder = this.fileBuilderContext.getNewEnumBuilder(); |
||||
this.builder.setName(name.asJavaString()); |
||||
this.builder.getOptionsBuilder().setAllowAlias(true); |
||||
|
||||
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.builder.addValueBuilder() |
||||
.setName(name.asJavaString()) |
||||
.setNumber(RubyNumeric.num2int(number)); |
||||
return context.nil; |
||||
} |
||||
|
||||
private EnumDescriptorProto.Builder builder; |
||||
private RubyFileBuilderContext fileBuilderContext; |
||||
} |
@ -1,348 +0,0 @@ |
||||
/* |
||||
* 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.DescriptorProto; |
||||
import com.google.protobuf.DescriptorProtos.EnumDescriptorProto; |
||||
import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProtoOrBuilder; |
||||
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; |
||||
import com.google.protobuf.DescriptorProtos.FileDescriptorProto; |
||||
import com.google.protobuf.Descriptors.FieldDescriptor; |
||||
import org.jruby.Ruby; |
||||
import org.jruby.RubyClass; |
||||
import org.jruby.RubyHash; |
||||
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.HashMap; |
||||
import java.util.List; |
||||
import java.util.TreeMap; |
||||
|
||||
@JRubyClass(name = "FileBuilderContext") |
||||
public class RubyFileBuilderContext extends RubyObject { |
||||
public static void createRubyFileBuilderContext(Ruby runtime) { |
||||
RubyModule internal = runtime.getClassFromPath("Google::Protobuf::Internal"); |
||||
RubyClass cFileBuilderContext = internal.defineClassUnder("FileBuilderContext", runtime.getObject(), new ObjectAllocator() { |
||||
@Override |
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
||||
return new RubyFileBuilderContext(runtime, klazz); |
||||
} |
||||
}); |
||||
cFileBuilderContext.defineAnnotatedMethods(RubyFileBuilderContext.class); |
||||
|
||||
cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor"); |
||||
cEnumBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Internal::EnumBuilderContext"); |
||||
cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor"); |
||||
cMessageBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Internal::MessageBuilderContext"); |
||||
} |
||||
|
||||
public RubyFileBuilderContext(Ruby runtime, RubyClass klazz) { |
||||
super(runtime, klazz); |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* FileBuilderContext.new(descriptor_pool, name, options = nil) => context |
||||
* |
||||
* Create a new file builder context for the given file descriptor and |
||||
* builder context. This class is intended to serve as a DSL context to be used |
||||
* with #instance_eval. |
||||
*/ |
||||
@JRubyMethod(required = 2, optional = 1) |
||||
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { |
||||
this.descriptorPool = (RubyDescriptorPool) args[0]; |
||||
this.builder = FileDescriptorProto.newBuilder(); |
||||
this.builder.setName(args[1].asJavaString()); |
||||
this.builder.setSyntax("proto3"); |
||||
|
||||
if (args.length > 2) { |
||||
RubyHash options = (RubyHash) args[2]; |
||||
IRubyObject syntax = options.fastARef(context.runtime.newSymbol("syntax")); |
||||
|
||||
if (syntax != null) { |
||||
String syntaxStr = syntax.asJavaString(); |
||||
this.builder.setSyntax(syntaxStr); |
||||
this.proto3 = syntaxStr.equals("proto3"); |
||||
} |
||||
} |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* FileBuilderContext.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) { |
||||
RubyObject ctx = (RubyObject) cEnumBuilderContext.newInstance(context, this, name, Block.NULL_BLOCK); |
||||
ctx.instance_eval(context, block); |
||||
|
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* FileBuilderContext.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) { |
||||
RubyObject ctx = (RubyObject) cMessageBuilderContext.newInstance(context, this, name, Block.NULL_BLOCK); |
||||
ctx.instance_eval(context, block); |
||||
|
||||
return context.nil; |
||||
} |
||||
|
||||
protected void build(ThreadContext context) { |
||||
Ruby runtime = context.runtime; |
||||
List<DescriptorProto.Builder> messageBuilderList = builder.getMessageTypeBuilderList(); |
||||
List<EnumDescriptorProto.Builder> enumBuilderList = builder.getEnumTypeBuilderList(); |
||||
|
||||
// Get the package name from defined names
|
||||
String packageName = getPackageName(messageBuilderList, enumBuilderList); |
||||
|
||||
if (!packageName.isEmpty()) { |
||||
builder.setPackage(packageName); |
||||
} |
||||
|
||||
// Make an index of the message builders so we can easily nest them
|
||||
TreeMap<String, DescriptorProto.Builder> messageBuilderMap = new TreeMap(); |
||||
for (DescriptorProto.Builder messageBuilder : messageBuilderList) { |
||||
messageBuilderMap.put(messageBuilder.getName(), messageBuilder); |
||||
} |
||||
|
||||
// Make an index of the enum builders so we can easily nest them
|
||||
HashMap<String, EnumDescriptorProto.Builder> enumBuilderMap = new HashMap(); |
||||
for (EnumDescriptorProto.Builder enumBuilder : enumBuilderList) { |
||||
enumBuilderMap.put("." + enumBuilder.getName(), enumBuilder); |
||||
} |
||||
|
||||
// Rename and properly nest messages and create associated ruby objects
|
||||
int packageNameLength = packageName.length(); |
||||
int currentMessageIndex = 0; |
||||
int currentEnumIndex = 0; |
||||
|
||||
// Need to get a static list because we are potentially deleting some of them from the collection
|
||||
DescriptorProto.Builder[] messageBuilders = new DescriptorProto.Builder[messageBuilderList.size()]; |
||||
messageBuilderList.toArray(messageBuilders); |
||||
EnumDescriptorProto.Builder[] enumBuilders = new EnumDescriptorProto.Builder[enumBuilderList.size()]; |
||||
enumBuilderList.toArray(enumBuilders); |
||||
|
||||
for (EnumDescriptorProto.Builder enumBuilder : enumBuilders) { |
||||
String name = enumBuilder.getName(); |
||||
int lastDot = name.lastIndexOf('.'); |
||||
|
||||
if (lastDot > packageNameLength) { |
||||
String parentName = name.substring(0, lastDot); |
||||
String shortName = name.substring(lastDot + 1); |
||||
|
||||
enumBuilder.setName(shortName); |
||||
messageBuilderMap.get(parentName).addEnumType(enumBuilder); |
||||
|
||||
builder.removeEnumType(currentEnumIndex); |
||||
|
||||
} else { |
||||
if (packageNameLength > 0) { |
||||
// Remove the package name
|
||||
String shortName = name.substring(packageNameLength + 1); |
||||
enumBuilder.setName(shortName); |
||||
} |
||||
|
||||
currentEnumIndex++; |
||||
} |
||||
|
||||
// Ensure we have a default value if using proto3 syntax
|
||||
if (proto3) { |
||||
boolean foundDefault = false; |
||||
for (EnumValueDescriptorProtoOrBuilder enumValue : enumBuilder.getValueOrBuilderList()) { |
||||
if (enumValue.getNumber() == 0) { |
||||
foundDefault = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!foundDefault) { |
||||
throw Utils.createTypeError(context, "Enum definition " + enumBuilder.getName() + " does not contain a value for '0'"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Wipe out top level message builders so we can insert only the ones that should be there
|
||||
builder.clearMessageType(); |
||||
|
||||
/* |
||||
* This block is done in this order because calling |
||||
* `addNestedType` and `addMessageType` makes a copy of the builder |
||||
* so the objects that our maps point to are no longer the objects |
||||
* that are being used to build the descriptions. |
||||
*/ |
||||
for (HashMap.Entry<String, DescriptorProto.Builder> entry : messageBuilderMap.descendingMap().entrySet()) { |
||||
DescriptorProto.Builder messageBuilder = entry.getValue(); |
||||
|
||||
// Rewrite any enum defaults needed
|
||||
for(FieldDescriptorProto.Builder field : messageBuilder.getFieldBuilderList()) { |
||||
String typeName = field.getTypeName(); |
||||
|
||||
if (typeName == null || !field.hasDefaultValue()) continue; |
||||
|
||||
EnumDescriptorProto.Builder enumBuilder = enumBuilderMap.get(typeName); |
||||
|
||||
if (enumBuilder == null) continue; |
||||
|
||||
int defaultValue = Integer.parseInt(field.getDefaultValue()); |
||||
|
||||
for (EnumValueDescriptorProtoOrBuilder enumValue : enumBuilder.getValueOrBuilderList()) { |
||||
if (enumValue.getNumber() == defaultValue) { |
||||
field.setDefaultValue(enumValue.getName()); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Turn Foo.Bar.Baz into a correctly nested structure with the correct name
|
||||
String name = messageBuilder.getName(); |
||||
int lastDot = name.lastIndexOf('.'); |
||||
|
||||
if (lastDot > packageNameLength) { |
||||
String parentName = name.substring(0, lastDot); |
||||
String shortName = name.substring(lastDot + 1); |
||||
messageBuilder.setName(shortName); |
||||
messageBuilderMap.get(parentName).addNestedType(messageBuilder); |
||||
|
||||
} else { |
||||
if (packageNameLength > 0) { |
||||
// Remove the package name
|
||||
messageBuilder.setName(name.substring(packageNameLength + 1)); |
||||
} |
||||
|
||||
// Add back in top level message definitions
|
||||
builder.addMessageType(messageBuilder); |
||||
|
||||
currentMessageIndex++; |
||||
} |
||||
} |
||||
|
||||
descriptorPool.registerFileDescriptor(context, builder); |
||||
} |
||||
|
||||
protected EnumDescriptorProto.Builder getNewEnumBuilder() { |
||||
return builder.addEnumTypeBuilder(); |
||||
} |
||||
|
||||
protected DescriptorProto.Builder getNewMessageBuilder() { |
||||
return builder.addMessageTypeBuilder(); |
||||
} |
||||
|
||||
protected boolean isProto3() { |
||||
return proto3; |
||||
} |
||||
|
||||
private String getPackageName(List<DescriptorProto.Builder> messages, List<EnumDescriptorProto.Builder> enums) { |
||||
String shortest = null; |
||||
String longest = null; |
||||
|
||||
/* |
||||
* The >= in the longest string comparisons below makes it so we replace |
||||
* the name in case all the names are the same length. This makes it so |
||||
* that the shortest and longest aren't the same name to prevent |
||||
* finding a "package" that isn't correct |
||||
*/ |
||||
|
||||
for (DescriptorProto.Builder message : messages) { |
||||
String name = message.getName(); |
||||
int nameLength = name.length(); |
||||
if (shortest == null) { |
||||
shortest = name; |
||||
longest = name; |
||||
} else if (nameLength < shortest.length()) { |
||||
shortest = name; |
||||
} else if (nameLength >= longest.length()) { |
||||
longest = name; |
||||
} |
||||
} |
||||
|
||||
for (EnumDescriptorProto.Builder item : enums) { |
||||
String name = item.getName(); |
||||
int nameLength = name.length(); |
||||
if (shortest == null) { |
||||
shortest = name; |
||||
longest = name; |
||||
} else if (nameLength < shortest.length()) { |
||||
shortest = name; |
||||
} else if (nameLength >= longest.length()) { |
||||
longest = name; |
||||
} |
||||
} |
||||
|
||||
if (shortest == null) { |
||||
return ""; |
||||
} |
||||
|
||||
int lastCommonDot = 0; |
||||
for (int i = 0; i < shortest.length(); i++) { |
||||
char nextChar = shortest.charAt(i); |
||||
if (nextChar != longest.charAt(i)) break; |
||||
if (nextChar == '.') lastCommonDot = i; |
||||
} |
||||
|
||||
return shortest.substring(0, lastCommonDot); |
||||
} |
||||
|
||||
private static RubyClass cDescriptor; |
||||
private static RubyClass cEnumBuilderContext; |
||||
private static RubyClass cEnumDescriptor; |
||||
private static RubyClass cMessageBuilderContext; |
||||
|
||||
private FileDescriptorProto.Builder builder; |
||||
private RubyDescriptorPool descriptorPool; |
||||
private boolean proto3 = true; |
||||
} |
@ -1,255 +0,0 @@ |
||||
/* |
||||
* 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.DescriptorProto; |
||||
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; |
||||
import com.google.protobuf.DescriptorProtos.OneofDescriptorProto; |
||||
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 internal = runtime.getClassFromPath("Google::Protobuf::Internal"); |
||||
RubyClass cMessageBuilderContext = internal.defineClassUnder("MessageBuilderContext", runtime.getObject(), new ObjectAllocator() { |
||||
@Override |
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) { |
||||
return new RubyMessageBuilderContext(runtime, klazz); |
||||
} |
||||
}); |
||||
cMessageBuilderContext.defineAnnotatedMethods(RubyMessageBuilderContext.class); |
||||
|
||||
cFieldDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::FieldDescriptor"); |
||||
cOneofBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Internal::OneofBuilderContext"); |
||||
} |
||||
|
||||
public RubyMessageBuilderContext(Ruby runtime, RubyClass klazz) { |
||||
super(runtime, klazz); |
||||
} |
||||
|
||||
@JRubyMethod |
||||
public IRubyObject initialize(ThreadContext context, IRubyObject fileBuilderContext, IRubyObject name) { |
||||
this.fileBuilderContext = (RubyFileBuilderContext) fileBuilderContext; |
||||
this.builder = this.fileBuilderContext.getNewMessageBuilder(); |
||||
this.builder.setName(name.asJavaString()); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* MessageBuilderContext.optional(name, type, number, type_class = nil, |
||||
* options = 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 = 2) |
||||
public IRubyObject optional(ThreadContext context, IRubyObject[] args) { |
||||
addField(context, OPTIONAL, args, false); |
||||
return context.nil; |
||||
} |
||||
|
||||
@JRubyMethod(required = 3, optional = 2) |
||||
public IRubyObject proto3_optional(ThreadContext context, IRubyObject[] args) { |
||||
addField(context, OPTIONAL, args, true); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* MessageBuilderContext.required(name, type, number, type_class = nil, |
||||
* options = 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 = 2) |
||||
public IRubyObject required(ThreadContext context, IRubyObject[] args) { |
||||
if (fileBuilderContext.isProto3()) throw Utils.createTypeError(context, "Required fields are unsupported in proto3"); |
||||
addField(context, "required", args, false); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* 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) { |
||||
addField(context, "repeated", args, false); |
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* 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; |
||||
if (!fileBuilderContext.isProto3()) throw runtime.newArgumentError("Cannot add a native map field using proto2 syntax."); |
||||
|
||||
RubySymbol messageSym = runtime.newSymbol("message"); |
||||
|
||||
IRubyObject name = args[0]; |
||||
IRubyObject keyType = args[1]; |
||||
IRubyObject valueType = args[2]; |
||||
IRubyObject number = args[3]; |
||||
IRubyObject typeClass = args.length > 4 ? args[4] : context.nil; |
||||
|
||||
// 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(runtime.newSymbol("float")) || |
||||
keyType.equals(runtime.newSymbol("double")) || |
||||
keyType.equals(runtime.newSymbol("enum")) || |
||||
keyType.equals(messageSym)) |
||||
throw runtime.newArgumentError("Cannot add a map field with a float, double, enum, or message type."); |
||||
|
||||
DescriptorProto.Builder mapEntryBuilder = fileBuilderContext.getNewMessageBuilder(); |
||||
mapEntryBuilder.setName(builder.getName() + "_MapEntry_" + name.asJavaString()); |
||||
mapEntryBuilder.getOptionsBuilder().setMapEntry(true); |
||||
|
||||
mapEntryBuilder.addField( |
||||
Utils.createFieldBuilder( |
||||
context, |
||||
OPTIONAL, |
||||
new IRubyObject[] { |
||||
runtime.newString("key"), |
||||
keyType, |
||||
runtime.newFixnum(1) |
||||
} |
||||
) |
||||
); |
||||
|
||||
mapEntryBuilder.addField( |
||||
Utils.createFieldBuilder( |
||||
context, |
||||
OPTIONAL, |
||||
new IRubyObject[] { |
||||
runtime.newString("value"), |
||||
valueType, |
||||
runtime.newFixnum(2), |
||||
typeClass |
||||
} |
||||
) |
||||
); |
||||
|
||||
IRubyObject[] addFieldArgs = { |
||||
name, messageSym, number, runtime.newString(mapEntryBuilder.getName()) |
||||
}; |
||||
|
||||
repeated(context, addFieldArgs); |
||||
|
||||
return context.nil; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* MessageBuilderContext.oneof(name, &block) => nil |
||||
* |
||||
* Creates a new OneofDescriptor with the given name, creates a |
||||
* OneofBuilderContext attached to that OneofDescriptor, evaluates the given |
||||
* block in the context of that OneofBuilderContext with #instance_eval, and |
||||
* then adds the oneof to the message. |
||||
* |
||||
* This is the recommended, idiomatic way to build oneof definitions. |
||||
*/ |
||||
@JRubyMethod |
||||
public IRubyObject oneof(ThreadContext context, IRubyObject name, Block block) { |
||||
RubyOneofBuilderContext ctx = (RubyOneofBuilderContext) |
||||
cOneofBuilderContext.newInstance( |
||||
context, |
||||
context.runtime.newFixnum(builder.getOneofDeclCount()), |
||||
this, |
||||
Block.NULL_BLOCK |
||||
); |
||||
|
||||
builder.addOneofDeclBuilder().setName(name.asJavaString()); |
||||
ctx.instance_eval(context, block); |
||||
|
||||
return context.nil; |
||||
} |
||||
|
||||
protected void addFieldBuilder(FieldDescriptorProto.Builder fieldBuilder) { |
||||
builder.addField(fieldBuilder); |
||||
} |
||||
|
||||
private FieldDescriptorProto.Builder addField(ThreadContext context, String label, IRubyObject[] args, boolean proto3Optional) { |
||||
FieldDescriptorProto.Builder fieldBuilder = |
||||
Utils.createFieldBuilder(context, label, args); |
||||
|
||||
fieldBuilder.setProto3Optional(proto3Optional); |
||||
builder.addField(fieldBuilder); |
||||
|
||||
return fieldBuilder; |
||||
} |
||||
|
||||
private static RubyClass cFieldDescriptor; |
||||
private static RubyClass cOneofBuilderContext; |
||||
|
||||
private static final String OPTIONAL = "optional"; |
||||
|
||||
private DescriptorProto.Builder builder; |
||||
private RubyClass cDescriptor; |
||||
private RubyFileBuilderContext fileBuilderContext; |
||||
} |
@ -1,103 +0,0 @@ |
||||
/* |
||||
* 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.FieldDescriptorProto; |
||||
import org.jruby.Ruby; |
||||
import org.jruby.RubyClass; |
||||
import org.jruby.RubyHash; |
||||
import org.jruby.RubyModule; |
||||
import org.jruby.RubyNumeric; |
||||
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 internal = runtime.getClassFromPath("Google::Protobuf::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); |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* OneofBuilderContext.new(oneof_index, message_builder) => context |
||||
* |
||||
* Create a new oneof builder context around the given oneof descriptor and |
||||
* builder context. This class is intended to serve as a DSL context to be used |
||||
* with #instance_eval. |
||||
*/ |
||||
@JRubyMethod |
||||
public IRubyObject initialize(ThreadContext context, IRubyObject index, IRubyObject messageBuilder) { |
||||
this.builder = (RubyMessageBuilderContext) messageBuilder; |
||||
this.index = RubyNumeric.num2int(index); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/* |
||||
* call-seq: |
||||
* OneofBuilderContext.optional(name, type, number, type_class = nil, |
||||
* options = nil) |
||||
* |
||||
* Defines a new optional field in this oneof 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 = 2) |
||||
public IRubyObject optional(ThreadContext context, IRubyObject[] args) { |
||||
FieldDescriptorProto.Builder fieldBuilder = |
||||
Utils.createFieldBuilder(context, "optional", args); |
||||
fieldBuilder.setOneofIndex(index); |
||||
builder.addFieldBuilder(fieldBuilder); |
||||
|
||||
return context.nil; |
||||
} |
||||
|
||||
private RubyMessageBuilderContext builder; |
||||
private int index; |
||||
} |
Loading…
Reference in new issue