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: kenton@google.com (Kenton Varda) 32// Based on original Protocol Buffers design by 33// Sanjay Ghemawat, Jeff Dean, and others. 34 35#include <map> 36#include <string> 37 38#include <google/protobuf/compiler/java/java_context.h> 39#include <google/protobuf/compiler/java/java_enum.h> 40#include <google/protobuf/compiler/java/java_doc_comment.h> 41#include <google/protobuf/compiler/java/java_helpers.h> 42#include <google/protobuf/compiler/java/java_name_resolver.h> 43#include <google/protobuf/io/printer.h> 44#include <google/protobuf/descriptor.pb.h> 45#include <google/protobuf/stubs/strutil.h> 46 47namespace google { 48namespace protobuf { 49namespace compiler { 50namespace java { 51 52namespace { 53bool EnumHasCustomOptions(const EnumDescriptor* descriptor) { 54 if (descriptor->options().unknown_fields().field_count() > 0) return true; 55 for (int i = 0; i < descriptor->value_count(); ++i) { 56 const EnumValueDescriptor* value = descriptor->value(i); 57 if (value->options().unknown_fields().field_count() > 0) return true; 58 } 59 return false; 60} 61} // namespace 62 63EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, 64 bool immutable_api, 65 Context* context) 66 : descriptor_(descriptor), immutable_api_(immutable_api), 67 name_resolver_(context->GetNameResolver()) { 68 for (int i = 0; i < descriptor_->value_count(); i++) { 69 const EnumValueDescriptor* value = descriptor_->value(i); 70 const EnumValueDescriptor* canonical_value = 71 descriptor_->FindValueByNumber(value->number()); 72 73 if (value == canonical_value) { 74 canonical_values_.push_back(value); 75 } else { 76 Alias alias; 77 alias.value = value; 78 alias.canonical_value = canonical_value; 79 aliases_.push_back(alias); 80 } 81 } 82} 83 84EnumGenerator::~EnumGenerator() {} 85 86void EnumGenerator::Generate(io::Printer* printer) { 87 WriteEnumDocComment(printer, descriptor_); 88 if (HasDescriptorMethods(descriptor_)) { 89 printer->Print( 90 "public enum $classname$\n" 91 " implements com.google.protobuf.ProtocolMessageEnum {\n", 92 "classname", descriptor_->name()); 93 } else { 94 printer->Print( 95 "public enum $classname$\n" 96 " implements com.google.protobuf.Internal.EnumLite {\n", 97 "classname", descriptor_->name()); 98 } 99 printer->Indent(); 100 101 for (int i = 0; i < canonical_values_.size(); i++) { 102 map<string, string> vars; 103 vars["name"] = canonical_values_[i]->name(); 104 vars["index"] = SimpleItoa(canonical_values_[i]->index()); 105 vars["number"] = SimpleItoa(canonical_values_[i]->number()); 106 WriteEnumValueDocComment(printer, canonical_values_[i]); 107 if (canonical_values_[i]->options().deprecated()) { 108 printer->Print("@java.lang.Deprecated\n"); 109 } 110 printer->Print(vars, 111 "$name$($index$, $number$),\n"); 112 } 113 114 printer->Print( 115 ";\n" 116 "\n"); 117 118 // ----------------------------------------------------------------- 119 120 for (int i = 0; i < aliases_.size(); i++) { 121 map<string, string> vars; 122 vars["classname"] = descriptor_->name(); 123 vars["name"] = aliases_[i].value->name(); 124 vars["canonical_name"] = aliases_[i].canonical_value->name(); 125 WriteEnumValueDocComment(printer, aliases_[i].value); 126 printer->Print(vars, 127 "public static final $classname$ $name$ = $canonical_name$;\n"); 128 } 129 130 for (int i = 0; i < descriptor_->value_count(); i++) { 131 map<string, string> vars; 132 vars["name"] = descriptor_->value(i)->name(); 133 vars["number"] = SimpleItoa(descriptor_->value(i)->number()); 134 WriteEnumValueDocComment(printer, descriptor_->value(i)); 135 printer->Print(vars, 136 "public static final int $name$_VALUE = $number$;\n"); 137 } 138 printer->Print("\n"); 139 140 // ----------------------------------------------------------------- 141 142 printer->Print( 143 "\n" 144 "public final int getNumber() { return value; }\n" 145 "\n" 146 "public static $classname$ valueOf(int value) {\n" 147 " switch (value) {\n", 148 "classname", descriptor_->name()); 149 printer->Indent(); 150 printer->Indent(); 151 152 for (int i = 0; i < canonical_values_.size(); i++) { 153 printer->Print( 154 "case $number$: return $name$;\n", 155 "name", canonical_values_[i]->name(), 156 "number", SimpleItoa(canonical_values_[i]->number())); 157 } 158 159 printer->Outdent(); 160 printer->Outdent(); 161 printer->Print( 162 " default: return null;\n" 163 " }\n" 164 "}\n" 165 "\n" 166 "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n" 167 " internalGetValueMap() {\n" 168 " return internalValueMap;\n" 169 "}\n" 170 "private static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n" 171 " internalValueMap =\n" 172 " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" 173 " public $classname$ findValueByNumber(int number) {\n" 174 " return $classname$.valueOf(number);\n" 175 " }\n" 176 " };\n" 177 "\n", 178 "classname", descriptor_->name()); 179 180 // ----------------------------------------------------------------- 181 // Reflection 182 183 if (HasDescriptorMethods(descriptor_)) { 184 printer->Print( 185 "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" 186 " getValueDescriptor() {\n" 187 " return getDescriptor().getValues().get(index);\n" 188 "}\n" 189 "public final com.google.protobuf.Descriptors.EnumDescriptor\n" 190 " getDescriptorForType() {\n" 191 " return getDescriptor();\n" 192 "}\n" 193 "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" 194 " getDescriptor() {\n"); 195 196 // TODO(kenton): Cache statically? Note that we can't access descriptors 197 // at module init time because it wouldn't work with descriptor.proto, but 198 // we can cache the value the first time getDescriptor() is called. 199 if (descriptor_->containing_type() == NULL) { 200 if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) { 201 printer->Print( 202 " return $file$.getDescriptor().getEnumTypes().get($index$);\n", 203 "file", name_resolver_->GetClassName(descriptor_->file(), 204 immutable_api_), 205 "index", SimpleItoa(descriptor_->index())); 206 } else { 207 printer->Indent(); 208 if (EnumHasCustomOptions(descriptor_)) { 209 // We need to load the immutable classes in order to parse custom 210 // options. However, since file level enums (no outer class) are 211 // shared by immutable code and mutable code, the immutable classes 212 // may not exist. So we try to use Java reflection to retrieve the 213 // descriptor from immutable classes. 214 printer->Print( 215 "try {\n" 216 " java.lang.Class immutableFileClass =\n" 217 " java.lang.Class.forName(\"$immutable_file_class_name$\");\n" 218 " @java.lang.SuppressWarnings(\"unchecked\")\n" 219 " java.lang.reflect.Method m =\n" 220 " immutableFileClass.getMethod(\"getDescriptor\");\n" 221 " com.google.protobuf.Descriptors.FileDescriptor file =\n" 222 " (com.google.protobuf.Descriptors.FileDescriptor)\n" 223 " m.invoke(immutableFileClass);\n" 224 " return file.getEnumTypes().get($index$);\n" 225 "} catch (Exception e) {\n" 226 // Immutable classes cannot be found. Proceed as if custom options 227 // don't exist. 228 "}\n", 229 "immutable_file_class_name", 230 name_resolver_->GetImmutableClassName(descriptor_->file()), 231 "index", SimpleItoa(descriptor_->index())); 232 } 233 printer->Print( 234 "return $immutable_package$.$descriptor_class$.getDescriptor()\n" 235 " .getEnumTypes().get($index$);\n", 236 "immutable_package", FileJavaPackage(descriptor_->file(), true), 237 "descriptor_class", 238 name_resolver_->GetDescriptorClassName(descriptor_->file()), 239 "index", SimpleItoa(descriptor_->index())); 240 printer->Outdent(); 241 } 242 } else { 243 printer->Print( 244 " return $parent$.$descriptor$.getEnumTypes().get($index$);\n", 245 "parent", name_resolver_->GetClassName(descriptor_->containing_type(), 246 immutable_api_), 247 "descriptor", descriptor_->containing_type()->options() 248 .no_standard_descriptor_accessor() 249 ? "getDefaultInstance().getDescriptorForType()" 250 : "getDescriptor()", 251 "index", SimpleItoa(descriptor_->index())); 252 } 253 254 printer->Print( 255 "}\n" 256 "\n" 257 "private static final $classname$[] VALUES = ", 258 "classname", descriptor_->name()); 259 260 if (CanUseEnumValues()) { 261 // If the constants we are going to output are exactly the ones we 262 // have declared in the Java enum in the same order, then we can use 263 // the values() method that the Java compiler automatically generates 264 // for every enum. 265 printer->Print("values();\n"); 266 } else { 267 printer->Print( 268 "{\n" 269 " "); 270 for (int i = 0; i < descriptor_->value_count(); i++) { 271 printer->Print("$name$, ", 272 "name", descriptor_->value(i)->name()); 273 } 274 printer->Print( 275 "\n" 276 "};\n"); 277 } 278 279 printer->Print( 280 "\n" 281 "public static $classname$ valueOf(\n" 282 " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" 283 " if (desc.getType() != getDescriptor()) {\n" 284 " throw new java.lang.IllegalArgumentException(\n" 285 " \"EnumValueDescriptor is not for this type.\");\n" 286 " }\n" 287 " return VALUES[desc.getIndex()];\n" 288 "}\n" 289 "\n", 290 "classname", descriptor_->name()); 291 292 // index is only used for reflection; lite implementation does not need it 293 printer->Print("private final int index;\n"); 294 } 295 296 // ----------------------------------------------------------------- 297 298 printer->Print( 299 "private final int value;\n\n" 300 "private $classname$(int index, int value) {\n", 301 "classname", descriptor_->name()); 302 if (HasDescriptorMethods(descriptor_)) { 303 printer->Print(" this.index = index;\n"); 304 } 305 printer->Print( 306 " this.value = value;\n" 307 "}\n"); 308 309 printer->Print( 310 "\n" 311 "// @@protoc_insertion_point(enum_scope:$full_name$)\n", 312 "full_name", descriptor_->full_name()); 313 314 printer->Outdent(); 315 printer->Print("}\n\n"); 316} 317 318bool EnumGenerator::CanUseEnumValues() { 319 if (canonical_values_.size() != descriptor_->value_count()) { 320 return false; 321 } 322 for (int i = 0; i < descriptor_->value_count(); i++) { 323 if (descriptor_->value(i)->name() != canonical_values_[i]->name()) { 324 return false; 325 } 326 } 327 return true; 328} 329 330} // namespace java 331} // namespace compiler 332} // namespace protobuf 333} // namespace google 334