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