javanano_file.cc revision 64d8d8f89050c5ada85341f967af391f4716a7cb
1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// http://code.google.com/p/protobuf/ 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: kenton@google.com (Kenton Varda) 32// Based on original Protocol Buffers design by 33// Sanjay Ghemawat, Jeff Dean, and others. 34 35#include <google/protobuf/compiler/javanano/javanano_file.h> 36#include <google/protobuf/compiler/javanano/javanano_enum.h> 37#include <google/protobuf/compiler/javanano/javanano_helpers.h> 38#include <google/protobuf/compiler/javanano/javanano_message.h> 39#include <google/protobuf/compiler/code_generator.h> 40#include <google/protobuf/io/printer.h> 41#include <google/protobuf/io/zero_copy_stream.h> 42#include <google/protobuf/descriptor.pb.h> 43#include <google/protobuf/stubs/strutil.h> 44 45namespace google { 46namespace protobuf { 47namespace compiler { 48namespace javanano { 49 50namespace { 51 52// Recursively searches the given message to see if it contains any extensions. 53bool UsesExtensions(const Message& message) { 54 const Reflection* reflection = message.GetReflection(); 55 56 // We conservatively assume that unknown fields are extensions. 57 if (reflection->GetUnknownFields(message).field_count() > 0) return true; 58 59 vector<const FieldDescriptor*> fields; 60 reflection->ListFields(message, &fields); 61 62 for (int i = 0; i < fields.size(); i++) { 63 if (fields[i]->is_extension()) return true; 64 65 if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 66 if (fields[i]->is_repeated()) { 67 int size = reflection->FieldSize(message, fields[i]); 68 for (int j = 0; j < size; j++) { 69 const Message& sub_message = 70 reflection->GetRepeatedMessage(message, fields[i], j); 71 if (UsesExtensions(sub_message)) return true; 72 } 73 } else { 74 const Message& sub_message = reflection->GetMessage(message, fields[i]); 75 if (UsesExtensions(sub_message)) return true; 76 } 77 } 78 } 79 80 return false; 81} 82 83} // namespace 84 85FileGenerator::FileGenerator(const FileDescriptor* file, const Params& params) 86 : file_(file), 87 params_(params), 88 java_package_(FileJavaPackage(params, file)), 89 classname_(FileClassName(params, file)) {} 90 91FileGenerator::~FileGenerator() {} 92 93bool FileGenerator::Validate(string* error) { 94 // Check for extensions 95 FileDescriptorProto file_proto; 96 file_->CopyTo(&file_proto); 97 if (UsesExtensions(file_proto)) { 98 error->assign(file_->name()); 99 error->append( 100 ": Java NANO_RUNTIME does not support extensions\""); 101 return false; 102 } 103 104 // If there is no outer class name then there must be only 105 // message and no enums defined in the file scope. 106 if (!params_.has_java_outer_classname(file_->name())) { 107 if (file_->message_type_count() != 1) { 108 error->assign(file_->name()); 109 error->append( 110 ": Java NANO_RUNTIME may only have 1 message if there is no 'option java_outer_classname'\""); 111 return false; 112 } 113 114 if (file_->enum_type_count() != 0) { 115 error->assign(file_->name()); 116 error->append( 117 ": Java NANO_RUNTIME must have an 'option java_outer_classname' if file scope enums are present\""); 118 return false; 119 } 120 } 121 122 // Check that no class name matches the file's class name. This is a common 123 // problem that leads to Java compile errors that can be hard to understand. 124 // It's especially bad when using the java_multiple_files, since we would 125 // end up overwriting the outer class with one of the inner ones. 126 int found_fileName = 0; 127 for (int i = 0; i < file_->enum_type_count(); i++) { 128 if (file_->enum_type(i)->name() == classname_) { 129 found_fileName += 1; 130 } 131 } 132 for (int i = 0; i < file_->message_type_count(); i++) { 133 if (file_->message_type(i)->name() == classname_) { 134 found_fileName += 1; 135 } 136 } 137 if (file_->service_count() != 0) { 138 error->assign(file_->name()); 139 error->append( 140 ": Java NANO_RUNTIME does not support services\""); 141 return false; 142 } 143 144 if (found_fileName > 1) { 145 error->assign(file_->name()); 146 error->append( 147 ": Cannot generate Java output because there is more than one class name, \""); 148 error->append(classname_); 149 error->append( 150 "\", matches the name of one of the types declared inside it. " 151 "Please either rename the type or use the java_outer_classname " 152 "option to specify a different outer class name for the .proto file." 153 " -- FIX THIS MESSAGE"); 154 return false; 155 } 156 return true; 157} 158 159void FileGenerator::Generate(io::Printer* printer) { 160 // We don't import anything because we refer to all classes by their 161 // fully-qualified names in the generated source. 162 printer->Print( 163 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" 164 "\n"); 165 if (!java_package_.empty()) { 166 printer->Print( 167 "package $package$;\n" 168 "\n", 169 "package", java_package_); 170 } 171 172 if (params_.has_java_outer_classname(file_->name())) { 173 printer->Print( 174 "public final class $classname$ {\n" 175 " private $classname$() {}\n", 176 "classname", classname_); 177 printer->Indent(); 178 } 179 180 // ----------------------------------------------------------------- 181 182 if (!params_.java_multiple_files()) { 183 for (int i = 0; i < file_->enum_type_count(); i++) { 184 EnumGenerator(file_->enum_type(i), params_).Generate(printer); 185 } 186 for (int i = 0; i < file_->message_type_count(); i++) { 187 MessageGenerator(file_->message_type(i), params_).Generate(printer); 188 } 189 } 190 191 // Static variables. 192 for (int i = 0; i < file_->message_type_count(); i++) { 193 // TODO(kenton): Reuse MessageGenerator objects? 194 MessageGenerator(file_->message_type(i), params_).GenerateStaticVariables(printer); 195 } 196 197 if (params_.has_java_outer_classname(file_->name())) { 198 printer->Outdent(); 199 printer->Print( 200 "}\n"); 201 } 202} 203 204template<typename GeneratorClass, typename DescriptorClass> 205static void GenerateSibling(const string& package_dir, 206 const string& java_package, 207 const DescriptorClass* descriptor, 208 OutputDirectory* output_directory, 209 vector<string>* file_list, 210 const Params& params) { 211 string filename = package_dir + descriptor->name() + ".java"; 212 file_list->push_back(filename); 213 214 scoped_ptr<io::ZeroCopyOutputStream> output( 215 output_directory->Open(filename)); 216 io::Printer printer(output.get(), '$'); 217 218 printer.Print( 219 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" 220 "\n"); 221 if (!java_package.empty()) { 222 printer.Print( 223 "package $package$;\n" 224 "\n", 225 "package", java_package); 226 } 227 228 GeneratorClass(descriptor, params).Generate(&printer); 229} 230 231void FileGenerator::GenerateSiblings(const string& package_dir, 232 OutputDirectory* output_directory, 233 vector<string>* file_list) { 234 if (params_.java_multiple_files()) { 235 for (int i = 0; i < file_->enum_type_count(); i++) { 236 GenerateSibling<EnumGenerator>(package_dir, java_package_, 237 file_->enum_type(i), 238 output_directory, file_list, params_); 239 } 240 for (int i = 0; i < file_->message_type_count(); i++) { 241 GenerateSibling<MessageGenerator>(package_dir, java_package_, 242 file_->message_type(i), 243 output_directory, file_list, params_); 244 } 245 } 246} 247 248} // namespace javanano 249} // namespace compiler 250} // namespace protobuf 251} // namespace google 252