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 <iostream> 36 37#include <google/protobuf/compiler/javanano/javanano_file.h> 38#include <google/protobuf/compiler/javanano/javanano_enum.h> 39#include <google/protobuf/compiler/javanano/javanano_extension.h> 40#include <google/protobuf/compiler/javanano/javanano_helpers.h> 41#include <google/protobuf/compiler/javanano/javanano_message.h> 42#include <google/protobuf/compiler/code_generator.h> 43#include <google/protobuf/io/printer.h> 44#include <google/protobuf/io/zero_copy_stream.h> 45#include <google/protobuf/descriptor.pb.h> 46#include <google/protobuf/stubs/strutil.h> 47 48namespace google { 49namespace protobuf { 50namespace compiler { 51namespace javanano { 52 53namespace { 54 55// Recursively searches the given message to see if it contains any extensions. 56bool UsesExtensions(const Message& message) { 57 const Reflection* reflection = message.GetReflection(); 58 59 // We conservatively assume that unknown fields are extensions. 60 if (reflection->GetUnknownFields(message).field_count() > 0) return true; 61 62 vector<const FieldDescriptor*> fields; 63 reflection->ListFields(message, &fields); 64 65 for (int i = 0; i < fields.size(); i++) { 66 if (fields[i]->is_extension()) return true; 67 68 if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 69 if (fields[i]->is_repeated()) { 70 int size = reflection->FieldSize(message, fields[i]); 71 for (int j = 0; j < size; j++) { 72 const Message& sub_message = 73 reflection->GetRepeatedMessage(message, fields[i], j); 74 if (UsesExtensions(sub_message)) return true; 75 } 76 } else { 77 const Message& sub_message = reflection->GetMessage(message, fields[i]); 78 if (UsesExtensions(sub_message)) return true; 79 } 80 } 81 } 82 83 return false; 84} 85 86} // namespace 87 88FileGenerator::FileGenerator(const FileDescriptor* file, const Params& params) 89 : file_(file), 90 params_(params), 91 java_package_(FileJavaPackage(params, file)), 92 classname_(FileClassName(params, file)) {} 93 94FileGenerator::~FileGenerator() {} 95 96bool FileGenerator::Validate(string* error) { 97 // Check for extensions 98 FileDescriptorProto file_proto; 99 file_->CopyTo(&file_proto); 100 if (UsesExtensions(file_proto) && !params_.store_unknown_fields()) { 101 error->assign(file_->name()); 102 error->append( 103 ": Java NANO_RUNTIME only supports extensions when the " 104 "'store_unknown_fields' generator option is 'true'."); 105 return false; 106 } 107 108 if (file_->service_count() != 0 && !params_.ignore_services()) { 109 error->assign(file_->name()); 110 error->append( 111 ": Java NANO_RUNTIME does not support services\""); 112 return false; 113 } 114 115 if (!IsOuterClassNeeded(params_, file_)) { 116 return true; 117 } 118 119 // Check whether legacy javanano generator would omit the outer class. 120 if (!params_.has_java_outer_classname(file_->name()) 121 && file_->message_type_count() == 1 122 && file_->enum_type_count() == 0 && file_->extension_count() == 0) { 123 cout << "INFO: " << file_->name() << ":" << endl; 124 cout << "Javanano generator has changed to align with java generator. " 125 "An outer class will be created for this file and the single message " 126 "in the file will become a nested class. Use java_multiple_files to " 127 "skip generating the outer class, or set an explicit " 128 "java_outer_classname to suppress this message." << endl; 129 } 130 131 // Check that no class name matches the file's class name. This is a common 132 // problem that leads to Java compile errors that can be hard to understand. 133 // It's especially bad when using the java_multiple_files, since we would 134 // end up overwriting the outer class with one of the inner ones. 135 bool found_conflict = false; 136 for (int i = 0; !found_conflict && i < file_->message_type_count(); i++) { 137 if (file_->message_type(i)->name() == classname_) { 138 found_conflict = true; 139 } 140 } 141 if (params_.java_enum_style()) { 142 for (int i = 0; !found_conflict && i < file_->enum_type_count(); i++) { 143 if (file_->enum_type(i)->name() == classname_) { 144 found_conflict = true; 145 } 146 } 147 } 148 if (found_conflict) { 149 error->assign(file_->name()); 150 error->append( 151 ": Cannot generate Java output because the file's outer class name, \""); 152 error->append(classname_); 153 error->append( 154 "\", matches the name of one of the types declared inside it. " 155 "Please either rename the type or use the java_outer_classname " 156 "option to specify a different outer class name for the .proto file."); 157 return false; 158 } 159 return true; 160} 161 162void FileGenerator::Generate(io::Printer* printer) { 163 // We don't import anything because we refer to all classes by their 164 // fully-qualified names in the generated source. 165 printer->Print( 166 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); 167 if (!java_package_.empty()) { 168 printer->Print( 169 "\n" 170 "package $package$;\n", 171 "package", java_package_); 172 } 173 174 // Note: constants (from enums, emitted in the loop below) may have the same names as constants 175 // in the nested classes. This causes Java warnings, but is not fatal, so we suppress those 176 // warnings here in the top-most class declaration. 177 printer->Print( 178 "\n" 179 "@SuppressWarnings(\"hiding\")\n" 180 "public interface $classname$ {\n", 181 "classname", classname_); 182 printer->Indent(); 183 184 // ----------------------------------------------------------------- 185 186 // Extensions. 187 for (int i = 0; i < file_->extension_count(); i++) { 188 ExtensionGenerator(file_->extension(i), params_).Generate(printer); 189 } 190 191 // Enums. 192 for (int i = 0; i < file_->enum_type_count(); i++) { 193 EnumGenerator(file_->enum_type(i), params_).Generate(printer); 194 } 195 196 // Messages. 197 if (!params_.java_multiple_files(file_->name())) { 198 for (int i = 0; i < file_->message_type_count(); i++) { 199 MessageGenerator(file_->message_type(i), params_).Generate(printer); 200 } 201 } 202 203 // Static variables. 204 for (int i = 0; i < file_->message_type_count(); i++) { 205 // TODO(kenton): Reuse MessageGenerator objects? 206 MessageGenerator(file_->message_type(i), params_).GenerateStaticVariables(printer); 207 } 208 209 printer->Outdent(); 210 printer->Print( 211 "}\n"); 212} 213 214template<typename GeneratorClass, typename DescriptorClass> 215static void GenerateSibling(const string& package_dir, 216 const string& java_package, 217 const DescriptorClass* descriptor, 218 OutputDirectory* output_directory, 219 vector<string>* file_list, 220 const Params& params) { 221 string filename = package_dir + descriptor->name() + ".java"; 222 file_list->push_back(filename); 223 224 scoped_ptr<io::ZeroCopyOutputStream> output( 225 output_directory->Open(filename)); 226 io::Printer printer(output.get(), '$'); 227 228 printer.Print( 229 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); 230 if (!java_package.empty()) { 231 printer.Print( 232 "\n" 233 "package $package$;\n", 234 "package", java_package); 235 } 236 237 GeneratorClass(descriptor, params).Generate(&printer); 238} 239 240void FileGenerator::GenerateSiblings(const string& package_dir, 241 OutputDirectory* output_directory, 242 vector<string>* file_list) { 243 if (params_.java_multiple_files(file_->name())) { 244 for (int i = 0; i < file_->message_type_count(); i++) { 245 GenerateSibling<MessageGenerator>(package_dir, java_package_, 246 file_->message_type(i), 247 output_directory, file_list, params_); 248 } 249 250 if (params_.java_enum_style()) { 251 for (int i = 0; i < file_->enum_type_count(); i++) { 252 GenerateSibling<EnumGenerator>(package_dir, java_package_, 253 file_->enum_type(i), 254 output_directory, file_list, params_); 255 } 256 } 257 } 258} 259 260} // namespace javanano 261} // namespace compiler 262} // namespace protobuf 263} // namespace google 264