• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.processor;
17 
18 import com.google.auto.common.MoreElements;
19 import com.google.auto.common.MoreTypes;
20 import com.google.common.base.Verify;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.ImmutableSet;
23 import java.util.List;
24 import javax.lang.model.element.TypeElement;
25 import javax.lang.model.type.DeclaredType;
26 import javax.lang.model.type.TypeKind;
27 import javax.lang.model.type.TypeMirror;
28 import javax.lang.model.util.Types;
29 
30 /**
31  * A wrapper for properties of Optional-like classes. This can be com.google.common.base.Optional,
32  * or any of Optional, OptionalDouble, OptionalInt, OptionalLong in java.util.
33  *
34  * @author emcmanus@google.com (Éamonn McManus)
35  */
36 public class Optionalish {
37   private static final ImmutableSet<String> OPTIONAL_CLASS_NAMES =
38       ImmutableSet.of(
39           "com.".concat("google.common.base.Optional"), // subterfuge to foil shading
40           "java.util.Optional",
41           "java.util.OptionalDouble",
42           "java.util.OptionalInt",
43           "java.util.OptionalLong");
44 
45   private final DeclaredType optionalType;
46   private final String className;
47 
Optionalish(DeclaredType optionalType)48   private Optionalish(DeclaredType optionalType) {
49     this.optionalType = optionalType;
50     this.className = MoreElements.asType(optionalType.asElement()).getQualifiedName().toString();
51   }
52 
53   /**
54    * Returns an instance wrapping the given TypeMirror, or null if it is not any kind of Optional.
55    *
56    * @param type the TypeMirror for the original optional type, for example {@code
57    *     Optional<String>}.
58    */
createIfOptional(TypeMirror type)59   static Optionalish createIfOptional(TypeMirror type) {
60     if (isOptional(type)) {
61       return new Optionalish(MoreTypes.asDeclared(type));
62     } else {
63       return null;
64     }
65   }
66 
isOptional(TypeMirror type)67   static boolean isOptional(TypeMirror type) {
68     if (type.getKind() != TypeKind.DECLARED) {
69       return false;
70     }
71     DeclaredType declaredType = MoreTypes.asDeclared(type);
72     TypeElement typeElement = MoreElements.asType(declaredType.asElement());
73     return OPTIONAL_CLASS_NAMES.contains(typeElement.getQualifiedName().toString())
74         && typeElement.getTypeParameters().size() == declaredType.getTypeArguments().size();
75   }
76 
77   /**
78    * Returns a string representing the raw type of this Optional. This will typically be just {@code
79    * "Optional"}, but it might be {@code "OptionalInt"} or {@code "java.util.Optional"} for example.
80    */
getRawType()81   public String getRawType() {
82     return TypeEncoder.encodeRaw(optionalType);
83   }
84 
85   /**
86    * Returns a string representing the method call to obtain the empty version of this Optional.
87    * This will be something like {@code "Optional.empty()"} or possibly {@code
88    * "java.util.Optional.empty()"}. It does not have a final semicolon.
89    *
90    * <p>This method is public so that it can be referenced as {@code p.optional.empty} from
91    * templates.
92    */
getEmpty()93   public String getEmpty() {
94     String empty = className.startsWith("java.util.") ? ".empty()" : ".absent()";
95     return TypeEncoder.encodeRaw(optionalType) + empty;
96   }
97 
getContainedType(Types typeUtils)98   TypeMirror getContainedType(Types typeUtils) {
99     List<? extends TypeMirror> typeArguments = optionalType.getTypeArguments();
100     switch (typeArguments.size()) {
101       case 1:
102         return typeArguments.get(0);
103       case 0:
104         return getContainedPrimitiveType(typeUtils);
105       default:
106         throw new AssertionError("Wrong number of type arguments: " + optionalType);
107     }
108   }
109 
ofNullable()110   String ofNullable() {
111     return className.equals("java.util.Optional") ? "ofNullable" : "fromNullable";
112   }
113 
114   private static final ImmutableMap<String, TypeKind> PRIMITIVE_TYPE_KINDS =
115       ImmutableMap.of(
116           "OptionalDouble", TypeKind.DOUBLE,
117           "OptionalInt", TypeKind.INT,
118           "OptionalLong", TypeKind.LONG);
119 
getContainedPrimitiveType(Types typeUtils)120   private TypeMirror getContainedPrimitiveType(Types typeUtils) {
121     String simpleName = optionalType.asElement().getSimpleName().toString();
122     TypeKind typeKind = PRIMITIVE_TYPE_KINDS.get(simpleName);
123     Verify.verifyNotNull(typeKind, "Could not get contained type of %s", optionalType);
124     return typeUtils.getPrimitiveType(typeKind);
125   }
126 }
127