1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 // Author: xiaofeng@google.com (Feng Xiao)
32
33 #include <google/protobuf/compiler/java/java_shared_code_generator.h>
34
35 #include <memory>
36
37 #include <google/protobuf/compiler/java/java_helpers.h>
38 #include <google/protobuf/compiler/java/java_name_resolver.h>
39 #include <google/protobuf/compiler/code_generator.h>
40 #include <google/protobuf/descriptor.pb.h>
41 #include <google/protobuf/io/printer.h>
42 #include <google/protobuf/io/zero_copy_stream.h>
43 #include <google/protobuf/descriptor.h>
44 #include <google/protobuf/stubs/strutil.h>
45
46 namespace google {
47 namespace protobuf {
48 namespace compiler {
49 namespace java {
50
SharedCodeGenerator(const FileDescriptor * file,const Options & options)51 SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file,
52 const Options& options)
53 : name_resolver_(new ClassNameResolver), file_(file), options_(options) {}
54
~SharedCodeGenerator()55 SharedCodeGenerator::~SharedCodeGenerator() {}
56
Generate(GeneratorContext * context,std::vector<std::string> * file_list,std::vector<std::string> * annotation_file_list)57 void SharedCodeGenerator::Generate(
58 GeneratorContext* context, std::vector<std::string>* file_list,
59 std::vector<std::string>* annotation_file_list) {
60 std::string java_package = FileJavaPackage(file_);
61 std::string package_dir = JavaPackageToDir(java_package);
62
63 if (HasDescriptorMethods(file_, options_.enforce_lite)) {
64 // Generate descriptors.
65 std::string classname = name_resolver_->GetDescriptorClassName(file_);
66 std::string filename = package_dir + classname + ".java";
67 file_list->push_back(filename);
68 std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
69 GeneratedCodeInfo annotations;
70 io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
71 &annotations);
72 std::unique_ptr<io::Printer> printer(
73 new io::Printer(output.get(), '$',
74 options_.annotate_code ? &annotation_collector : NULL));
75 std::string info_relative_path = classname + ".java.pb.meta";
76 std::string info_full_path = filename + ".pb.meta";
77 printer->Print(
78 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
79 "// source: $filename$\n"
80 "\n",
81 "filename", file_->name());
82 if (!java_package.empty()) {
83 printer->Print(
84 "package $package$;\n"
85 "\n",
86 "package", java_package);
87 }
88 PrintGeneratedAnnotation(printer.get(), '$',
89 options_.annotate_code ? info_relative_path : "");
90 printer->Print(
91 "public final class $classname$ {\n"
92 " public static com.google.protobuf.Descriptors.FileDescriptor\n"
93 " descriptor;\n"
94 " static {\n",
95 "classname", classname);
96 printer->Annotate("classname", file_->name());
97 printer->Indent();
98 printer->Indent();
99 GenerateDescriptors(printer.get());
100 printer->Outdent();
101 printer->Outdent();
102 printer->Print(
103 " }\n"
104 "}\n");
105
106 if (options_.annotate_code) {
107 std::unique_ptr<io::ZeroCopyOutputStream> info_output(
108 context->Open(info_full_path));
109 annotations.SerializeToZeroCopyStream(info_output.get());
110 annotation_file_list->push_back(info_full_path);
111 }
112
113 printer.reset();
114 output.reset();
115 }
116 }
117
GenerateDescriptors(io::Printer * printer)118 void SharedCodeGenerator::GenerateDescriptors(io::Printer* printer) {
119 // Embed the descriptor. We simply serialize the entire FileDescriptorProto
120 // and embed it as a string literal, which is parsed and built into real
121 // descriptors at initialization time. We unfortunately have to put it in
122 // a string literal, not a byte array, because apparently using a literal
123 // byte array causes the Java compiler to generate *instructions* to
124 // initialize each and every byte of the array, e.g. as if you typed:
125 // b[0] = 123; b[1] = 456; b[2] = 789;
126 // This makes huge bytecode files and can easily hit the compiler's internal
127 // code size limits (error "code to large"). String literals are apparently
128 // embedded raw, which is what we want.
129 FileDescriptorProto file_proto;
130 file_->CopyTo(&file_proto);
131
132 std::string file_data;
133 file_proto.SerializeToString(&file_data);
134
135 printer->Print("java.lang.String[] descriptorData = {\n");
136 printer->Indent();
137
138 // Limit the number of bytes per line.
139 static const int kBytesPerLine = 40;
140 // Limit the number of lines per string part.
141 static const int kLinesPerPart = 400;
142 // Every block of bytes, start a new string literal, in order to avoid the
143 // 64k length limit. Note that this value needs to be <64k.
144 static const int kBytesPerPart = kBytesPerLine * kLinesPerPart;
145 for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
146 if (i > 0) {
147 if (i % kBytesPerPart == 0) {
148 printer->Print(",\n");
149 } else {
150 printer->Print(" +\n");
151 }
152 }
153 printer->Print("\"$data$\"", "data",
154 CEscape(file_data.substr(i, kBytesPerLine)));
155 }
156
157 printer->Outdent();
158 printer->Print("\n};\n");
159
160 // -----------------------------------------------------------------
161 // Find out all dependencies.
162 std::vector<std::pair<std::string, std::string> > dependencies;
163 for (int i = 0; i < file_->dependency_count(); i++) {
164 std::string filename = file_->dependency(i)->name();
165 std::string package = FileJavaPackage(file_->dependency(i));
166 std::string classname =
167 name_resolver_->GetDescriptorClassName(file_->dependency(i));
168 std::string full_name;
169 if (package.empty()) {
170 full_name = classname;
171 } else {
172 full_name = package + "." + classname;
173 }
174 dependencies.push_back(std::make_pair(filename, full_name));
175 }
176
177 // -----------------------------------------------------------------
178 // Invoke internalBuildGeneratedFileFrom() to build the file.
179 printer->Print(
180 "descriptor = com.google.protobuf.Descriptors.FileDescriptor\n"
181 " .internalBuildGeneratedFileFrom(descriptorData,\n");
182 printer->Print(
183 " new com.google.protobuf.Descriptors.FileDescriptor[] {\n");
184
185 for (int i = 0; i < dependencies.size(); i++) {
186 const std::string& dependency = dependencies[i].second;
187 printer->Print(" $dependency$.getDescriptor(),\n", "dependency",
188 dependency);
189 }
190
191 printer->Print(" });\n");
192 }
193
194 } // namespace java
195 } // namespace compiler
196 } // namespace protobuf
197 } // namespace google
198