temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 2 | // Copyright 2008 Google Inc. All rights reserved. |
Feng Xiao | e428862 | 2014-10-01 16:26:23 -0700 | [diff] [blame] | 3 | // https://developers.google.com/protocol-buffers/ |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 4 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 8 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 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. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 18 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 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. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 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 | |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 38 | #include <google/protobuf/compiler/java/java_context.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 39 | #include <google/protobuf/compiler/java/java_enum.h> |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 40 | #include <google/protobuf/compiler/java/java_doc_comment.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 41 | #include <google/protobuf/compiler/java/java_helpers.h> |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 42 | #include <google/protobuf/compiler/java/java_name_resolver.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 43 | #include <google/protobuf/io/printer.h> |
| 44 | #include <google/protobuf/descriptor.pb.h> |
| 45 | #include <google/protobuf/stubs/strutil.h> |
| 46 | |
| 47 | namespace google { |
| 48 | namespace protobuf { |
| 49 | namespace compiler { |
| 50 | namespace java { |
| 51 | |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 52 | namespace { |
| 53 | bool 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 | |
| 63 | EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, |
| 64 | bool immutable_api, |
| 65 | Context* context) |
| 66 | : descriptor_(descriptor), immutable_api_(immutable_api), |
| 67 | name_resolver_(context->GetNameResolver()) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 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 | |
| 84 | EnumGenerator::~EnumGenerator() {} |
| 85 | |
| 86 | void EnumGenerator::Generate(io::Printer* printer) { |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 87 | WriteEnumDocComment(printer, descriptor_); |
Feng Xiao | e841bac | 2015-12-11 17:09:20 -0800 | [diff] [blame] | 88 | printer->Print( |
| 89 | "public enum $classname$\n" |
| 90 | " implements com.google.protobuf.ProtocolMessageEnum {\n", |
| 91 | "classname", descriptor_->name()); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 92 | printer->Indent(); |
| 93 | |
| 94 | for (int i = 0; i < canonical_values_.size(); i++) { |
| 95 | map<string, string> vars; |
| 96 | vars["name"] = canonical_values_[i]->name(); |
| 97 | vars["index"] = SimpleItoa(canonical_values_[i]->index()); |
| 98 | vars["number"] = SimpleItoa(canonical_values_[i]->number()); |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 99 | WriteEnumValueDocComment(printer, canonical_values_[i]); |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 100 | if (canonical_values_[i]->options().deprecated()) { |
| 101 | printer->Print("@java.lang.Deprecated\n"); |
| 102 | } |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 103 | printer->Print(vars, |
| 104 | "$name$($index$, $number$),\n"); |
| 105 | } |
| 106 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 107 | if (SupportUnknownEnumValue(descriptor_->file())) { |
| 108 | printer->Print("UNRECOGNIZED(-1, -1),\n"); |
| 109 | } |
| 110 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 111 | printer->Print( |
| 112 | ";\n" |
| 113 | "\n"); |
| 114 | |
| 115 | // ----------------------------------------------------------------- |
| 116 | |
| 117 | for (int i = 0; i < aliases_.size(); i++) { |
| 118 | map<string, string> vars; |
| 119 | vars["classname"] = descriptor_->name(); |
| 120 | vars["name"] = aliases_[i].value->name(); |
| 121 | vars["canonical_name"] = aliases_[i].canonical_value->name(); |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 122 | WriteEnumValueDocComment(printer, aliases_[i].value); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 123 | printer->Print(vars, |
| 124 | "public static final $classname$ $name$ = $canonical_name$;\n"); |
| 125 | } |
| 126 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame] | 127 | for (int i = 0; i < descriptor_->value_count(); i++) { |
| 128 | map<string, string> vars; |
| 129 | vars["name"] = descriptor_->value(i)->name(); |
| 130 | vars["number"] = SimpleItoa(descriptor_->value(i)->number()); |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 131 | WriteEnumValueDocComment(printer, descriptor_->value(i)); |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame] | 132 | printer->Print(vars, |
| 133 | "public static final int $name$_VALUE = $number$;\n"); |
| 134 | } |
| 135 | printer->Print("\n"); |
| 136 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 137 | // ----------------------------------------------------------------- |
| 138 | |
| 139 | printer->Print( |
| 140 | "\n" |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 141 | "public final int getNumber() {\n"); |
| 142 | if (SupportUnknownEnumValue(descriptor_->file())) { |
| 143 | printer->Print( |
| 144 | " if (index == -1) {\n" |
| 145 | " throw new java.lang.IllegalArgumentException(\n" |
| 146 | " \"Can't get the number of an unknown enum value.\");\n" |
| 147 | " }\n"); |
| 148 | } |
| 149 | printer->Print( |
| 150 | " return value;\n" |
| 151 | "}\n" |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 152 | "\n" |
| 153 | "public static $classname$ valueOf(int value) {\n" |
| 154 | " switch (value) {\n", |
| 155 | "classname", descriptor_->name()); |
| 156 | printer->Indent(); |
| 157 | printer->Indent(); |
| 158 | |
| 159 | for (int i = 0; i < canonical_values_.size(); i++) { |
| 160 | printer->Print( |
| 161 | "case $number$: return $name$;\n", |
| 162 | "name", canonical_values_[i]->name(), |
| 163 | "number", SimpleItoa(canonical_values_[i]->number())); |
| 164 | } |
| 165 | |
| 166 | printer->Outdent(); |
| 167 | printer->Outdent(); |
| 168 | printer->Print( |
| 169 | " default: return null;\n" |
| 170 | " }\n" |
| 171 | "}\n" |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 172 | "\n" |
| 173 | "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n" |
| 174 | " internalGetValueMap() {\n" |
| 175 | " return internalValueMap;\n" |
| 176 | "}\n" |
Feng Xiao | eee38b0 | 2015-08-22 18:25:48 -0700 | [diff] [blame] | 177 | "private static final com.google.protobuf.Internal.EnumLiteMap<\n" |
| 178 | " $classname$> internalValueMap =\n" |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 179 | " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" |
| 180 | " public $classname$ findValueByNumber(int number) {\n" |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 181 | " return $classname$.valueOf(number);\n" |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 182 | " }\n" |
| 183 | " };\n" |
| 184 | "\n", |
| 185 | "classname", descriptor_->name()); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 186 | |
| 187 | // ----------------------------------------------------------------- |
| 188 | // Reflection |
| 189 | |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 190 | if (HasDescriptorMethods(descriptor_)) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 191 | printer->Print( |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 192 | "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" |
| 193 | " getValueDescriptor() {\n" |
| 194 | " return getDescriptor().getValues().get(index);\n" |
| 195 | "}\n" |
| 196 | "public final com.google.protobuf.Descriptors.EnumDescriptor\n" |
| 197 | " getDescriptorForType() {\n" |
| 198 | " return getDescriptor();\n" |
| 199 | "}\n" |
| 200 | "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" |
| 201 | " getDescriptor() {\n"); |
| 202 | |
| 203 | // TODO(kenton): Cache statically? Note that we can't access descriptors |
| 204 | // at module init time because it wouldn't work with descriptor.proto, but |
| 205 | // we can cache the value the first time getDescriptor() is called. |
| 206 | if (descriptor_->containing_type() == NULL) { |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 207 | if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) { |
| 208 | printer->Print( |
| 209 | " return $file$.getDescriptor().getEnumTypes().get($index$);\n", |
| 210 | "file", name_resolver_->GetClassName(descriptor_->file(), |
| 211 | immutable_api_), |
| 212 | "index", SimpleItoa(descriptor_->index())); |
| 213 | } else { |
| 214 | printer->Indent(); |
| 215 | if (EnumHasCustomOptions(descriptor_)) { |
| 216 | // We need to load the immutable classes in order to parse custom |
| 217 | // options. However, since file level enums (no outer class) are |
| 218 | // shared by immutable code and mutable code, the immutable classes |
| 219 | // may not exist. So we try to use Java reflection to retrieve the |
| 220 | // descriptor from immutable classes. |
| 221 | printer->Print( |
| 222 | "try {\n" |
| 223 | " java.lang.Class immutableFileClass =\n" |
| 224 | " java.lang.Class.forName(\"$immutable_file_class_name$\");\n" |
| 225 | " @java.lang.SuppressWarnings(\"unchecked\")\n" |
| 226 | " java.lang.reflect.Method m =\n" |
| 227 | " immutableFileClass.getMethod(\"getDescriptor\");\n" |
| 228 | " com.google.protobuf.Descriptors.FileDescriptor file =\n" |
| 229 | " (com.google.protobuf.Descriptors.FileDescriptor)\n" |
| 230 | " m.invoke(immutableFileClass);\n" |
| 231 | " return file.getEnumTypes().get($index$);\n" |
| 232 | "} catch (Exception e) {\n" |
| 233 | // Immutable classes cannot be found. Proceed as if custom options |
| 234 | // don't exist. |
| 235 | "}\n", |
| 236 | "immutable_file_class_name", |
| 237 | name_resolver_->GetImmutableClassName(descriptor_->file()), |
| 238 | "index", SimpleItoa(descriptor_->index())); |
| 239 | } |
| 240 | printer->Print( |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 241 | "return $immutable_package$.$descriptor_class$.$descriptor$\n" |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 242 | " .getEnumTypes().get($index$);\n", |
| 243 | "immutable_package", FileJavaPackage(descriptor_->file(), true), |
| 244 | "descriptor_class", |
| 245 | name_resolver_->GetDescriptorClassName(descriptor_->file()), |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 246 | "descriptor", "getDescriptor()", |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 247 | "index", SimpleItoa(descriptor_->index())); |
| 248 | printer->Outdent(); |
| 249 | } |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 250 | } else { |
| 251 | printer->Print( |
jieluo@google.com | 4de8f55 | 2014-07-18 00:47:59 +0000 | [diff] [blame] | 252 | " return $parent$.$descriptor$.getEnumTypes().get($index$);\n", |
| 253 | "parent", name_resolver_->GetClassName(descriptor_->containing_type(), |
| 254 | immutable_api_), |
| 255 | "descriptor", descriptor_->containing_type()->options() |
| 256 | .no_standard_descriptor_accessor() |
| 257 | ? "getDefaultInstance().getDescriptorForType()" |
| 258 | : "getDescriptor()", |
| 259 | "index", SimpleItoa(descriptor_->index())); |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 260 | } |
| 261 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 262 | printer->Print( |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 263 | "}\n" |
| 264 | "\n" |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 265 | "private static final $classname$[] VALUES = ", |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 266 | "classname", descriptor_->name()); |
| 267 | |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 268 | if (CanUseEnumValues()) { |
| 269 | // If the constants we are going to output are exactly the ones we |
| 270 | // have declared in the Java enum in the same order, then we can use |
| 271 | // the values() method that the Java compiler automatically generates |
| 272 | // for every enum. |
| 273 | printer->Print("values();\n"); |
| 274 | } else { |
| 275 | printer->Print( |
| 276 | "{\n" |
| 277 | " "); |
| 278 | for (int i = 0; i < descriptor_->value_count(); i++) { |
| 279 | printer->Print("$name$, ", |
| 280 | "name", descriptor_->value(i)->name()); |
| 281 | } |
| 282 | printer->Print( |
| 283 | "\n" |
| 284 | "};\n"); |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 285 | } |
| 286 | |
| 287 | printer->Print( |
| 288 | "\n" |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 289 | "public static $classname$ valueOf(\n" |
| 290 | " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" |
| 291 | " if (desc.getType() != getDescriptor()) {\n" |
| 292 | " throw new java.lang.IllegalArgumentException(\n" |
| 293 | " \"EnumValueDescriptor is not for this type.\");\n" |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 294 | " }\n", |
| 295 | "classname", descriptor_->name()); |
| 296 | if (SupportUnknownEnumValue(descriptor_->file())) { |
| 297 | printer->Print( |
| 298 | " if (desc.getIndex() == -1) {\n" |
| 299 | " return UNRECOGNIZED;\n" |
| 300 | " }\n"); |
| 301 | } |
| 302 | printer->Print( |
kenton@google.com | 80b1d62 | 2009-07-29 01:13:20 +0000 | [diff] [blame] | 303 | " return VALUES[desc.getIndex()];\n" |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 304 | "}\n" |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 305 | "\n"); |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 306 | |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 307 | printer->Print("private final int index;\n"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 308 | } |
| 309 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 310 | // ----------------------------------------------------------------- |
| 311 | |
| 312 | printer->Print( |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 313 | "private final int value;\n\n" |
| 314 | "private $classname$(int index, int value) {\n", |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 315 | "classname", descriptor_->name()); |
kenton@google.com | ab6950d | 2010-01-28 01:09:11 +0000 | [diff] [blame] | 316 | if (HasDescriptorMethods(descriptor_)) { |
| 317 | printer->Print(" this.index = index;\n"); |
| 318 | } |
| 319 | printer->Print( |
| 320 | " this.value = value;\n" |
| 321 | "}\n"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 322 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 323 | printer->Print( |
| 324 | "\n" |
| 325 | "// @@protoc_insertion_point(enum_scope:$full_name$)\n", |
| 326 | "full_name", descriptor_->full_name()); |
| 327 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 328 | printer->Outdent(); |
| 329 | printer->Print("}\n\n"); |
| 330 | } |
| 331 | |
xiaofeng@google.com | b55a20f | 2012-09-22 02:40:50 +0000 | [diff] [blame] | 332 | bool EnumGenerator::CanUseEnumValues() { |
| 333 | if (canonical_values_.size() != descriptor_->value_count()) { |
| 334 | return false; |
| 335 | } |
| 336 | for (int i = 0; i < descriptor_->value_count(); i++) { |
| 337 | if (descriptor_->value(i)->name() != canonical_values_[i]->name()) { |
| 338 | return false; |
| 339 | } |
| 340 | } |
| 341 | return true; |
| 342 | } |
| 343 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 344 | } // namespace java |
| 345 | } // namespace compiler |
| 346 | } // namespace protobuf |
| 347 | } // namespace google |