1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.api.generator.engine.ast; 16 17 import com.google.auto.value.AutoValue; 18 import com.google.common.collect.ImmutableList; 19 import java.util.Arrays; 20 import java.util.List; 21 import java.util.Objects; 22 import javax.annotation.Nullable; 23 24 @AutoValue 25 public abstract class ConcreteReference implements Reference { 26 private static final String EXTENDS = "extends"; 27 28 private static final String COMMA = ", "; 29 private static final String DOT = "."; 30 private static final String SPACE = " "; 31 private static final String LEFT_ANGLE = "<"; 32 private static final String RIGHT_ANGLE = ">"; 33 private static final String QUESTION_MARK = "?"; 34 35 private static final Class<?> WILDCARD_CLAZZ = ReferenceWildcard.class; 36 37 // Private. clazz()38 abstract Class<?> clazz(); 39 40 @Override accept(AstNodeVisitor visitor)41 public void accept(AstNodeVisitor visitor) { 42 visitor.visit(this); 43 } 44 45 @Nullable 46 @Override wildcardUpperBound()47 public abstract Reference wildcardUpperBound(); 48 49 @Override generics()50 public abstract ImmutableList<Reference> generics(); 51 52 @Override isStaticImport()53 public abstract boolean isStaticImport(); 54 55 @Override name()56 public String name() { 57 StringBuilder sb = new StringBuilder(); 58 if (isWildcard()) { 59 sb.append(QUESTION_MARK); 60 if (wildcardUpperBound() != null) { 61 // Handle the upper bound. 62 sb.append(SPACE); 63 sb.append(EXTENDS); 64 sb.append(SPACE); 65 sb.append(wildcardUpperBound().name()); 66 } 67 } else { 68 if (hasEnclosingClass() && !isStaticImport()) { 69 sb.append(clazz().getEnclosingClass().getSimpleName()); 70 sb.append(DOT); 71 } 72 sb.append(clazz().getSimpleName()); 73 } 74 if (!generics().isEmpty()) { 75 sb.append(LEFT_ANGLE); 76 for (int i = 0; i < generics().size(); i++) { 77 Reference r = generics().get(i); 78 sb.append(r.name()); 79 if (i < generics().size() - 1) { 80 sb.append(COMMA); 81 } 82 } 83 sb.append(RIGHT_ANGLE); 84 } 85 return sb.toString(); 86 } 87 88 @Override simpleName()89 public String simpleName() { 90 return clazz().getSimpleName(); 91 } 92 93 @Override pakkage()94 public String pakkage() { 95 return clazz().getPackage().getName(); 96 } 97 98 @Override useFullName()99 public abstract boolean useFullName(); 100 101 @Override enclosingClassNames()102 public ImmutableList<String> enclosingClassNames() { 103 if (!hasEnclosingClass()) { 104 return ImmutableList.of(); 105 } 106 // The innermost type will be the last element in the list. 107 ImmutableList.Builder<String> listBuilder = new ImmutableList.Builder<>(); 108 Class<?> currentClz = clazz(); 109 while (currentClz.getEnclosingClass() != null) { 110 listBuilder.add(currentClz.getEnclosingClass().getSimpleName()); 111 currentClz = currentClz.getEnclosingClass(); 112 } 113 return listBuilder.build(); 114 } 115 116 @Override fullName()117 public String fullName() { 118 return clazz().getCanonicalName(); 119 } 120 121 @Override hasEnclosingClass()122 public boolean hasEnclosingClass() { 123 return clazz().getEnclosingClass() != null; 124 } 125 126 @Override isFromPackage(String pkg)127 public boolean isFromPackage(String pkg) { 128 return clazz().getPackage().getName().equals(pkg); 129 } 130 131 @Override isSupertypeOrEquals(Reference other)132 public boolean isSupertypeOrEquals(Reference other) { 133 // Don't check generics for cases like "List<String> foo = new ArrayList<>(); 134 if (!isAssignableFrom(other)) { 135 return false; 136 } 137 138 if (generics().size() == other.generics().size()) { 139 for (int i = 0; i < generics().size(); i++) { 140 Reference thisGeneric = generics().get(i); 141 Reference otherGeneric = other.generics().get(i); 142 if (!thisGeneric.isSupertypeOrEquals(otherGeneric)) { 143 return false; 144 } 145 } 146 } 147 148 return true; 149 } 150 151 @Override isAssignableFrom(Reference other)152 public boolean isAssignableFrom(Reference other) { 153 if (other instanceof VaporReference && ((VaporReference) other).supertypeReference() != null) { 154 return isAssignableFrom(((VaporReference) other).supertypeReference()); 155 } 156 157 if (!(other instanceof ConcreteReference)) { 158 return false; 159 } 160 161 if (generics().size() == other.generics().size()) { 162 for (int i = 0; i < generics().size(); i++) { 163 if (!generics().get(i).isSupertypeOrEquals(other.generics().get(i))) { 164 return false; 165 } 166 } 167 } 168 169 return clazz().isAssignableFrom(((ConcreteReference) other).clazz()); 170 } 171 172 @Override isWildcard()173 public boolean isWildcard() { 174 return clazz().equals(WILDCARD_CLAZZ); 175 } 176 177 @Override equals(Object o)178 public boolean equals(Object o) { 179 if (!(o instanceof ConcreteReference)) { 180 return false; 181 } 182 183 ConcreteReference ref = (ConcreteReference) o; 184 return clazz().equals(ref.clazz()) 185 && generics().equals(ref.generics()) 186 && Objects.equals(wildcardUpperBound(), ref.wildcardUpperBound()); 187 } 188 189 @Override hashCode()190 public int hashCode() { 191 int wildcardUpperBoundHash = 192 wildcardUpperBound() == null ? 0 : 11 * wildcardUpperBound().hashCode(); 193 return 17 * clazz().hashCode() + 31 * generics().hashCode() + wildcardUpperBoundHash; 194 } 195 196 @Override copyAndSetGenerics(List<Reference> generics)197 public Reference copyAndSetGenerics(List<Reference> generics) { 198 return toBuilder().setGenerics(generics).build(); 199 } 200 withClazz(Class<?> clazz)201 public static ConcreteReference withClazz(Class<?> clazz) { 202 return builder().setClazz(clazz).build(); 203 } 204 wildcard()205 public static ConcreteReference wildcard() { 206 return withClazz(ReferenceWildcard.class); 207 } 208 wildcardWithUpperBound(Reference upperBoundReference)209 public static ConcreteReference wildcardWithUpperBound(Reference upperBoundReference) { 210 return builder().setClazz(WILDCARD_CLAZZ).setWildcardUpperBound(upperBoundReference).build(); 211 } 212 builder()213 public static Builder builder() { 214 return new AutoValue_ConcreteReference.Builder() 215 .setUseFullName(false) 216 .setGenerics(ImmutableList.of()) 217 .setIsStaticImport(false); 218 } 219 220 // Private. toBuilder()221 abstract Builder toBuilder(); 222 223 @AutoValue.Builder 224 public abstract static class Builder { setClazz(Class<?> clazz)225 public abstract Builder setClazz(Class<?> clazz); 226 setUseFullName(boolean useFullName)227 public abstract Builder setUseFullName(boolean useFullName); 228 setWildcardUpperBound(Reference reference)229 public abstract Builder setWildcardUpperBound(Reference reference); 230 setGenerics(Reference... references)231 public Builder setGenerics(Reference... references) { 232 return setGenerics(Arrays.asList(references)); 233 } 234 setGenerics(List<Reference> references)235 public abstract Builder setGenerics(List<Reference> references); 236 setIsStaticImport(boolean isStaticImport)237 public abstract Builder setIsStaticImport(boolean isStaticImport); 238 autoBuild()239 public abstract ConcreteReference autoBuild(); 240 241 // Private. clazz()242 abstract Class<?> clazz(); 243 generics()244 abstract ImmutableList<Reference> generics(); 245 isStaticImport()246 abstract boolean isStaticImport(); 247 build()248 public ConcreteReference build() { 249 NodeValidator.checkNoNullElements( 250 generics(), "generics", String.format("concrete reference %s", clazz().getSimpleName())); 251 252 setIsStaticImport(clazz().getEnclosingClass() != null && isStaticImport()); 253 return autoBuild(); 254 } 255 } 256 } 257