1 /*
2  * Copyright 2021 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  *   https://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.android.enterprise.connectedapps.processor.containers;
17 
18 import static java.util.stream.Collectors.toCollection;
19 
20 import com.google.android.enterprise.connectedapps.annotations.Cacheable;
21 import com.google.android.enterprise.connectedapps.annotations.CrossProfile;
22 import com.google.android.enterprise.connectedapps.processor.ProcessorConfiguration;
23 import com.google.android.enterprise.connectedapps.processor.SupportedTypes;
24 import com.google.auto.value.AutoValue;
25 import com.google.common.collect.ImmutableCollection;
26 import com.google.common.collect.ImmutableSet;
27 import com.google.common.hash.Hashing;
28 import com.squareup.javapoet.ClassName;
29 import java.nio.charset.StandardCharsets;
30 import java.util.Collection;
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Optional;
34 import java.util.stream.IntStream;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.element.TypeElement;
37 
38 /** Wrapper of a {@link CrossProfile} type. */
39 @AutoValue
40 public abstract class CrossProfileTypeInfo {
41 
crossProfileTypeElement()42   public abstract TypeElement crossProfileTypeElement();
43 
crossProfileMethods()44   public abstract ImmutableCollection<CrossProfileMethodInfo> crossProfileMethods();
45 
supportedTypes()46   public abstract SupportedTypes supportedTypes();
47 
connectorInfo()48   public abstract Optional<ConnectorInfo> connectorInfo();
49 
50   /**
51    * The verbatim (not prefixed) name of the interface class used to make cross-profile or
52    * cross-user calls.
53    */
generatedClassName()54   public abstract ClassName generatedClassName();
55 
simpleName()56   public String simpleName() {
57     return crossProfileTypeElement().getSimpleName().toString();
58   }
59 
className()60   public ClassName className() {
61     return ClassName.get(crossProfileTypeElement());
62   }
63 
isStatic()64   public boolean isStatic() {
65     return crossProfileMethods().stream().allMatch(CrossProfileMethodInfo::isStatic);
66   }
67 
68   /**
69    * Checks if there are any cacheable methods on this cross-profile type.
70    *
71    * <p>Where cacheable methods are methods annotated with {@link Cacheable} indicating that the
72    * result of a cross-profile call of that method should be cached.
73    */
hasCacheableMethod()74   public boolean hasCacheableMethod() {
75     return crossProfileMethods().stream().anyMatch(CrossProfileMethodInfo::isCacheable);
76   }
77 
78   /**
79    * Get a numeric identifier for the cross-profile type.
80    *
81    * <p>This identifier is based on the type's qualified name, and will not change between runs.
82    */
identifier()83   public long identifier() {
84     // Stored in a 64 bit long, with ~200 cross-profile types, chance of collision is 1 in 10^15
85     return Hashing.murmur3_128()
86         .hashString(crossProfileTypeElement().getQualifiedName().toString(), StandardCharsets.UTF_8)
87         .asLong();
88   }
89 
90   @SuppressWarnings("CheckReturnValue") // extract classes from annotation is incorrectly flagged
create( ValidatorContext context, ValidatorCrossProfileTypeInfo crossProfileType)91   public static CrossProfileTypeInfo create(
92       ValidatorContext context, ValidatorCrossProfileTypeInfo crossProfileType) {
93     TypeElement crossProfileTypeElement = crossProfileType.crossProfileTypeElement();
94 
95     List<ExecutableElement> crossProfileMethodElements = crossProfileType.crossProfileMethods();
96 
97     Collection<CrossProfileMethodInfo> crossProfileMethods =
98         IntStream.range(0, crossProfileMethodElements.size())
99             .mapToObj(
100                 t ->
101                     CrossProfileMethodInfo.create(
102                         t, crossProfileType, crossProfileMethodElements.get(t), context))
103             .collect(toCollection(LinkedHashSet::new));
104 
105     SupportedTypes.Builder supportedTypesBuilder = crossProfileType.supportedTypes().asBuilder();
106 
107     supportedTypesBuilder.filterUsed(
108         context, crossProfileMethods, crossProfileType.additionalUsedTypes());
109 
110     if (ProcessorConfiguration.GENERATE_TYPE_SPECIFIC_WRAPPERS) {
111       supportedTypesBuilder.replaceWrapperPrefix(
112           ClassName.bestGuess(
113               crossProfileType.crossProfileTypeElement().getQualifiedName().toString()));
114     }
115 
116     return new AutoValue_CrossProfileTypeInfo(
117         crossProfileTypeElement,
118         ImmutableSet.copyOf(crossProfileMethods),
119         supportedTypesBuilder.build(),
120         crossProfileType.connectorInfo(),
121         findGeneratedClassName(context, crossProfileTypeElement));
122   }
123 
findGeneratedClassName( ValidatorContext context, TypeElement typeElement)124   private static ClassName findGeneratedClassName(
125       ValidatorContext context, TypeElement typeElement) {
126     return ClassName.get(
127         context.elements().getPackageOf(typeElement).getQualifiedName().toString(),
128         typeElement.getSimpleName().toString());
129   }
130 }
131