Skip to content

Commit 182b65f

Browse files
author
Soroosh Sarabadani
committed
Support multi level interfaces
1 parent da476f9 commit 182b65f

File tree

1 file changed

+145
-21
lines changed

1 file changed

+145
-21
lines changed

operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java

Lines changed: 145 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22

33
import static io.javaoperatorsdk.operator.ControllerUtils.CONTROLLERS_RESOURCE_PATH;
44
import static io.javaoperatorsdk.operator.ControllerUtils.DONEABLES_RESOURCE_PATH;
5+
import static javax.lang.model.type.TypeKind.DECLARED;
6+
import static javax.lang.model.type.TypeKind.TYPEVAR;
57

68
import com.google.auto.service.AutoService;
7-
import com.squareup.javapoet.*;
9+
import com.squareup.javapoet.ClassName;
10+
import com.squareup.javapoet.JavaFile;
11+
import com.squareup.javapoet.MethodSpec;
12+
import com.squareup.javapoet.ParameterizedTypeName;
13+
import com.squareup.javapoet.TypeName;
14+
import com.squareup.javapoet.TypeSpec;
815
import io.fabric8.kubernetes.api.builder.Function;
916
import io.fabric8.kubernetes.client.CustomResourceDoneable;
1017
import io.javaoperatorsdk.operator.api.ResourceController;
@@ -14,19 +21,32 @@
1421
import java.util.List;
1522
import java.util.Set;
1623
import java.util.stream.Collectors;
17-
import javax.annotation.processing.*;
24+
import java.util.stream.IntStream;
25+
import javax.annotation.processing.AbstractProcessor;
26+
import javax.annotation.processing.ProcessingEnvironment;
27+
import javax.annotation.processing.Processor;
28+
import javax.annotation.processing.RoundEnvironment;
29+
import javax.annotation.processing.SupportedAnnotationTypes;
30+
import javax.annotation.processing.SupportedSourceVersion;
1831
import javax.lang.model.SourceVersion;
19-
import javax.lang.model.element.*;
32+
import javax.lang.model.element.Element;
33+
import javax.lang.model.element.ElementKind;
34+
import javax.lang.model.element.Modifier;
35+
import javax.lang.model.element.PackageElement;
36+
import javax.lang.model.element.TypeElement;
37+
import javax.lang.model.element.TypeParameterElement;
2038
import javax.lang.model.type.DeclaredType;
2139
import javax.lang.model.type.TypeKind;
2240
import javax.lang.model.type.TypeMirror;
41+
import javax.lang.model.type.TypeVariable;
2342
import javax.tools.Diagnostic;
2443
import javax.tools.JavaFileObject;
2544

2645
@SupportedAnnotationTypes("io.javaoperatorsdk.operator.api.Controller")
2746
@SupportedSourceVersion(SourceVersion.RELEASE_8)
2847
@AutoService(Processor.class)
2948
public class ControllerAnnotationProcessor extends AbstractProcessor {
49+
3050
private AccumulativeMappingWriter controllersResourceWriter;
3151
private AccumulativeMappingWriter doneablesResourceWriter;
3252
private Set<String> generatedDoneableClassFiles = new HashSet<>();
@@ -63,10 +83,13 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
6383

6484
private void generateDoneableClass(TypeElement controllerClassSymbol) {
6585
try {
86+
System.out.println(controllerClassSymbol.toString());
6687
final TypeMirror resourceType = findResourceType(controllerClassSymbol);
88+
System.out.println("the resource type is " + resourceType);
6789

6890
TypeElement customerResourceTypeElement =
6991
processingEnv.getElementUtils().getTypeElement(resourceType.toString());
92+
System.out.println("the customerResourceTypeElement is " + customerResourceTypeElement);
7093

7194
final String doneableClassName = customerResourceTypeElement.getSimpleName() + "Doneable";
7295
final String destinationClassFileName =
@@ -124,34 +147,24 @@ private void generateDoneableClass(TypeElement controllerClassSymbol) {
124147

125148
private TypeMirror findResourceType(TypeElement controllerClassSymbol) throws Exception {
126149
try {
127-
final DeclaredType controllerType =
128-
collectAllInterfaces(controllerClassSymbol).stream()
129-
.filter(i -> i.toString().startsWith(ResourceController.class.getCanonicalName()))
130-
.findFirst()
131-
.orElseThrow(
132-
() ->
133-
new Exception(
134-
"ResourceController is not implemented by "
135-
+ controllerClassSymbol.toString()));
136-
return controllerType.getTypeArguments().get(0);
150+
final var chain = findChain((DeclaredType) controllerClassSymbol.asType());
151+
final var customResourceClass = getCustomResourceClass(chain);
152+
return customResourceClass;
137153
} catch (Exception e) {
138154
e.printStackTrace();
139155
return null;
140156
}
141157
}
142158

143-
private List<DeclaredType> collectAllInterfaces(TypeElement element) {
159+
private List<TypeMirror> collectAllInterfaces(TypeElement element) {
144160
try {
145-
List<DeclaredType> interfaces =
146-
new ArrayList<>(element.getInterfaces())
147-
.stream().map(t -> (DeclaredType) t).collect(Collectors.toList());
161+
List<TypeMirror> interfaces = new ArrayList<>(element.getInterfaces());
162+
interfaces.add(element.getSuperclass());
148163
TypeElement superclass = ((TypeElement) ((DeclaredType) element.getSuperclass()).asElement());
149164
while (superclass.getSuperclass().getKind() != TypeKind.NONE) {
150-
interfaces.addAll(
151-
superclass.getInterfaces().stream()
152-
.map(t -> (DeclaredType) t)
153-
.collect(Collectors.toList()));
165+
interfaces.addAll(superclass.getInterfaces());
154166
superclass = ((TypeElement) ((DeclaredType) superclass.getSuperclass()).asElement());
167+
interfaces.add(element.getSuperclass());
155168
}
156169
return interfaces;
157170
} catch (Exception e) {
@@ -166,4 +179,115 @@ private String makeQualifiedClassName(String packageName, String className) {
166179
}
167180
return packageName + "." + className;
168181
}
182+
183+
private List<DeclaredType> findChain(DeclaredType declaredType) {
184+
final var resourceControllerType =
185+
processingEnv
186+
.getTypeUtils()
187+
.getDeclaredType(
188+
processingEnv
189+
.getElementUtils()
190+
.getTypeElement(ResourceController.class.getCanonicalName()),
191+
processingEnv.getTypeUtils().getWildcardType(null, null));
192+
final var result = new ArrayList<DeclaredType>();
193+
result.add(declaredType);
194+
var superElement = ((TypeElement) ((DeclaredType) declaredType).asElement());
195+
var superclass = (DeclaredType) superElement.getSuperclass();
196+
boolean interfaceFound = false;
197+
final var matchingInterfaces =
198+
superElement.getInterfaces().stream()
199+
.filter(
200+
intface ->
201+
processingEnv.getTypeUtils().isAssignable(intface, resourceControllerType))
202+
.map(i -> (DeclaredType) i)
203+
.collect(Collectors.toList());
204+
if (!matchingInterfaces.isEmpty()) {
205+
result.addAll(matchingInterfaces);
206+
interfaceFound = true;
207+
}
208+
209+
while (superclass.getKind() != TypeKind.NONE) {
210+
if (interfaceFound) {
211+
final var lastFoundInterface = result.get(result.size() - 1);
212+
final var marchingInterfaces =
213+
((TypeElement) lastFoundInterface.asElement())
214+
.getInterfaces().stream()
215+
.filter(
216+
intface ->
217+
processingEnv
218+
.getTypeUtils()
219+
.isAssignable(intface, resourceControllerType))
220+
.map(i -> (DeclaredType) i)
221+
.collect(Collectors.toList());
222+
223+
if (marchingInterfaces.size() > 0) {
224+
result.addAll(marchingInterfaces);
225+
continue;
226+
} else {
227+
break;
228+
}
229+
}
230+
231+
if (processingEnv.getTypeUtils().isAssignable(superclass, resourceControllerType)) {
232+
result.add(superclass);
233+
}
234+
235+
superElement = (TypeElement) superclass.asElement();
236+
final var matchedInterfaces =
237+
superElement.getInterfaces().stream()
238+
.filter(
239+
intface ->
240+
processingEnv.getTypeUtils().isAssignable(intface, resourceControllerType))
241+
.map(i -> (DeclaredType) i)
242+
.collect(Collectors.toList());
243+
if (matchedInterfaces.size() > 0) {
244+
result.addAll(matchedInterfaces);
245+
interfaceFound = true;
246+
continue;
247+
}
248+
249+
if (superElement.getSuperclass().getKind() == TypeKind.NONE) {
250+
break;
251+
}
252+
superclass = (DeclaredType) superElement.getSuperclass();
253+
}
254+
255+
return result;
256+
}
257+
258+
private TypeMirror getCustomResourceClass(List<DeclaredType> chain) {
259+
var lastIndex = chain.size() - 1;
260+
String typeName;
261+
final List<? extends TypeMirror> typeArguments = (chain.get(lastIndex)).getTypeArguments();
262+
if (typeArguments.get(0).getKind() == TYPEVAR) {
263+
typeName = ((TypeVariable) typeArguments.get(0)).asElement().getSimpleName().toString();
264+
} else if (typeArguments.get(0).getKind() == DECLARED) {
265+
return typeArguments.get(0);
266+
} else {
267+
typeName = "";
268+
}
269+
270+
while (lastIndex > 0) {
271+
lastIndex -= 1;
272+
final List<? extends TypeMirror> tArguments = (chain.get(lastIndex)).getTypeArguments();
273+
final List<? extends TypeParameterElement> typeParameters =
274+
((TypeElement) ((chain.get(lastIndex)).asElement())).getTypeParameters();
275+
final String tName = typeName;
276+
final var typeIndex =
277+
IntStream.range(0, typeParameters.size())
278+
.filter(i -> typeParameters.get(i).getSimpleName().toString().equals(tName))
279+
.findFirst()
280+
.getAsInt();
281+
282+
final TypeMirror matchedType = tArguments.get(typeIndex);
283+
if (matchedType.getKind() == TYPEVAR) {
284+
typeName = ((TypeVariable) matchedType).asElement().getSimpleName().toString();
285+
} else if (matchedType.getKind() == DECLARED) {
286+
return matchedType;
287+
} else {
288+
typeName = "";
289+
}
290+
}
291+
return null;
292+
}
169293
}

0 commit comments

Comments
 (0)