• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Dagger Authors.
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 
17 package dagger.internal.codegen.base;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 
21 import com.google.auto.common.MoreElements;
22 import com.google.auto.common.MoreTypes;
23 import com.google.auto.value.AutoValue;
24 import com.google.common.base.Equivalence;
25 import com.squareup.javapoet.ClassName;
26 import com.squareup.javapoet.CodeBlock;
27 import com.squareup.javapoet.ParameterizedTypeName;
28 import com.squareup.javapoet.TypeName;
29 import dagger.model.Key;
30 import java.util.Optional;
31 import javax.lang.model.element.Name;
32 import javax.lang.model.type.DeclaredType;
33 import javax.lang.model.type.TypeMirror;
34 import javax.lang.model.type.TypeVisitor;
35 import javax.lang.model.util.SimpleTypeVisitor8;
36 
37 /**
38  * Information about an {@code Optional} {@link TypeMirror}.
39  *
40  * <p>{@link com.google.common.base.Optional} and {@link java.util.Optional} are supported.
41  */
42 @AutoValue
43 public abstract class OptionalType {
44 
45   /** A variant of {@code Optional}. */
46   public enum OptionalKind {
47     /** {@link com.google.common.base.Optional}. */
48     GUAVA_OPTIONAL(com.google.common.base.Optional.class, "absent"),
49 
50     /** {@link java.util.Optional}. */
51     JDK_OPTIONAL(java.util.Optional.class, "empty"),
52     ;
53 
54     private final Class<?> clazz;
55     private final String absentFactoryMethodName;
56 
OptionalKind(Class<?> clazz, String absentFactoryMethodName)57     OptionalKind(Class<?> clazz, String absentFactoryMethodName) {
58       this.clazz = clazz;
59       this.absentFactoryMethodName = absentFactoryMethodName;
60     }
61 
62     /** Returns {@code valueType} wrapped in the correct class. */
of(TypeName valueType)63     public ParameterizedTypeName of(TypeName valueType) {
64       return ParameterizedTypeName.get(ClassName.get(clazz), valueType);
65     }
66 
67     /** Returns an expression for the absent/empty value. */
absentValueExpression()68     public CodeBlock absentValueExpression() {
69       return CodeBlock.of("$T.$L()", clazz, absentFactoryMethodName);
70     }
71 
72     /**
73      * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}.
74      */
parameterizedAbsentValueExpression(OptionalType optionalType)75     public CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) {
76       return CodeBlock.of("$T.<$T>$L()", clazz, optionalType.valueType(), absentFactoryMethodName);
77     }
78 
79     /** Returns an expression for the present {@code value}. */
presentExpression(CodeBlock value)80     public CodeBlock presentExpression(CodeBlock value) {
81       return CodeBlock.of("$T.of($L)", clazz, value);
82     }
83 
84     /**
85      * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no
86      * matter what type the value is.
87      */
presentObjectExpression(CodeBlock value)88     public CodeBlock presentObjectExpression(CodeBlock value) {
89       return CodeBlock.of("$T.<$T>of($L)", clazz, Object.class, value);
90     }
91   }
92 
93   private static final TypeVisitor<Optional<OptionalKind>, Void> OPTIONAL_KIND =
94       new SimpleTypeVisitor8<Optional<OptionalKind>, Void>(Optional.empty()) {
95         @Override
96         public Optional<OptionalKind> visitDeclared(DeclaredType t, Void p) {
97           for (OptionalKind optionalKind : OptionalKind.values()) {
98             Name qualifiedName = MoreElements.asType(t.asElement()).getQualifiedName();
99             if (qualifiedName.contentEquals(optionalKind.clazz.getCanonicalName())) {
100               return Optional.of(optionalKind);
101             }
102           }
103           return Optional.empty();
104         }
105       };
106 
107   /**
108    * The optional type itself, wrapped using {@link MoreTypes#equivalence()}.
109    *
110    * @deprecated Use {@link #declaredOptionalType()} instead.
111    */
112   @Deprecated
wrappedDeclaredOptionalType()113   protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredOptionalType();
114 
115   /** The optional type itself. */
116   @SuppressWarnings("deprecation")
declaredOptionalType()117   private DeclaredType declaredOptionalType() {
118     return wrappedDeclaredOptionalType().get();
119   }
120 
121   /** Which {@code Optional} type is used. */
kind()122   public OptionalKind kind() {
123     return declaredOptionalType().accept(OPTIONAL_KIND, null).get();
124   }
125 
126   /** The value type. */
valueType()127   public TypeMirror valueType() {
128     return declaredOptionalType().getTypeArguments().get(0);
129   }
130 
131   /** Returns {@code true} if {@code type} is an {@code Optional} type. */
isOptional(TypeMirror type)132   private static boolean isOptional(TypeMirror type) {
133     return type.accept(OPTIONAL_KIND, null).isPresent();
134   }
135 
136   /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */
isOptional(Key key)137   public static boolean isOptional(Key key) {
138     return isOptional(key.type());
139   }
140 
141   /**
142    * Returns a {@link OptionalType} for {@code type}.
143    *
144    * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type
145    */
from(TypeMirror type)146   public static OptionalType from(TypeMirror type) {
147     checkArgument(isOptional(type), "%s must be an Optional", type);
148     return new AutoValue_OptionalType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
149   }
150 
151   /**
152    * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}.
153    *
154    * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type
155    */
from(Key key)156   public static OptionalType from(Key key) {
157     return from(key.type());
158   }
159 }
160