• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Protocol Buffers - Google's data interchange format
3  * Copyright 2014 Google Inc.  All rights reserved.
4  * https://developers.google.com/protocol-buffers/
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.google.protobuf.jruby;
34 
35 import com.google.protobuf.DescriptorProtos;
36 import com.google.protobuf.Descriptors;
37 import org.jruby.*;
38 import org.jruby.anno.JRubyClass;
39 import org.jruby.anno.JRubyMethod;
40 import org.jruby.runtime.*;
41 import org.jruby.runtime.builtin.IRubyObject;
42 
43 import java.util.HashMap;
44 import java.util.Map;
45 
46 @JRubyClass(name = "DescriptorPool")
47 public class RubyDescriptorPool extends RubyObject {
createRubyDescriptorPool(Ruby runtime)48     public static void createRubyDescriptorPool(Ruby runtime) {
49         RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
50         RubyClass cDescriptorPool = protobuf.defineClassUnder("DescriptorPool", runtime.getObject(), new ObjectAllocator() {
51             @Override
52             public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
53                 return new RubyDescriptorPool(runtime, klazz);
54             }
55         });
56 
57         cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class);
58         descriptorPool = (RubyDescriptorPool) cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK);
59     }
60 
RubyDescriptorPool(Ruby ruby, RubyClass klazz)61     public RubyDescriptorPool(Ruby ruby, RubyClass klazz) {
62         super(ruby, klazz);
63     }
64 
65     @JRubyMethod
initialize(ThreadContext context)66     public IRubyObject initialize(ThreadContext context) {
67         this.symtab = new HashMap<IRubyObject, IRubyObject>();
68         this.cBuilder = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Builder");
69         this.builder = DescriptorProtos.FileDescriptorProto.newBuilder();
70         return this;
71     }
72 
73     @JRubyMethod
build(ThreadContext context, Block block)74     public IRubyObject build(ThreadContext context, Block block) {
75         RubyBuilder ctx = (RubyBuilder) cBuilder.newInstance(context, Block.NULL_BLOCK);
76         if (block.arity() == Arity.ONE_ARGUMENT) {
77             block.yield(context, ctx);
78         } else {
79             Binding binding = block.getBinding();
80             binding.setSelf(ctx);
81             block.yieldSpecific(context);
82         }
83         ctx.finalizeToPool(context, this);
84         buildFileDescriptor(context);
85         return context.runtime.getNil();
86     }
87 
88     @JRubyMethod
lookup(ThreadContext context, IRubyObject name)89     public IRubyObject lookup(ThreadContext context, IRubyObject name) {
90         IRubyObject descriptor = this.symtab.get(name);
91         if (descriptor == null) {
92             return context.runtime.getNil();
93         }
94         return descriptor;
95     }
96 
97     /*
98      * call-seq:
99      *     DescriptorPool.generated_pool => descriptor_pool
100      *
101      * Class method that returns the global DescriptorPool. This is a singleton into
102      * which generated-code message and enum types are registered. The user may also
103      * register types in this pool for convenience so that they do not have to hold
104      * a reference to a private pool instance.
105      */
106     @JRubyMethod(meta = true, name = "generated_pool")
generatedPool(ThreadContext context, IRubyObject recv)107     public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) {
108         return descriptorPool;
109     }
110 
addToSymtab(ThreadContext context, RubyDescriptor def)111     protected void addToSymtab(ThreadContext context, RubyDescriptor def) {
112         symtab.put(def.getName(context), def);
113         this.builder.addMessageType(def.getBuilder());
114     }
115 
addToSymtab(ThreadContext context, RubyEnumDescriptor def)116     protected void addToSymtab(ThreadContext context, RubyEnumDescriptor def) {
117         symtab.put(def.getName(context), def);
118         this.builder.addEnumType(def.getBuilder());
119     }
120 
buildFileDescriptor(ThreadContext context)121     private void buildFileDescriptor(ThreadContext context) {
122         Ruby runtime = context.runtime;
123         try {
124             this.builder.setSyntax("proto3");
125             final Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(
126                     this.builder.build(), new Descriptors.FileDescriptor[]{});
127 
128             for (Descriptors.EnumDescriptor enumDescriptor : fileDescriptor.getEnumTypes()) {
129                 String enumName = Utils.unescapeIdentifier(enumDescriptor.getName());
130                 if (enumDescriptor.findValueByNumber(0) == null) {
131                     throw runtime.newTypeError("Enum definition " + enumName
132                             + " does not contain a value for '0'");
133                 }
134                 ((RubyEnumDescriptor) symtab.get(runtime.newString(enumName)))
135                         .setDescriptor(enumDescriptor);
136             }
137             for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
138                 RubyDescriptor rubyDescriptor = ((RubyDescriptor)
139                         symtab.get(runtime.newString(Utils.unescapeIdentifier(descriptor.getName()))));
140                 for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
141                     if (fieldDescriptor.isRequired()) {
142                         throw runtime.newTypeError("Required fields are unsupported in proto3");
143                     }
144                     RubyFieldDescriptor rubyFieldDescriptor = rubyDescriptor.lookup(fieldDescriptor.getName());
145                     rubyFieldDescriptor.setFieldDef(fieldDescriptor);
146                     if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
147                         RubyDescriptor subType = (RubyDescriptor) lookup(context,
148                                 runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getMessageType().getName())));
149                         rubyFieldDescriptor.setSubType(subType);
150                     }
151                     if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
152                         RubyEnumDescriptor subType = (RubyEnumDescriptor) lookup(context,
153                                 runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getEnumType().getName())));
154                         rubyFieldDescriptor.setSubType(subType);
155                     }
156                 }
157                 rubyDescriptor.setDescriptor(descriptor);
158             }
159         } catch (Descriptors.DescriptorValidationException e) {
160             throw runtime.newRuntimeError(e.getMessage());
161         }
162     }
163 
164     private static RubyDescriptorPool descriptorPool;
165 
166     private RubyClass cBuilder;
167     private Map<IRubyObject, IRubyObject> symtab;
168     private DescriptorProtos.FileDescriptorProto.Builder builder;
169 }
170