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