Skip to content

Commit cdc9eb3

Browse files
committed
Use a class to customize component options instead of a static method
1 parent 7970520 commit cdc9eb3

File tree

5 files changed

+77
-128
lines changed

5 files changed

+77
-128
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.axellience.vuegwt.client.component.options;
2+
3+
/**
4+
* @author Adrien Baron
5+
*/
6+
public abstract class CustomizeOptions
7+
{
8+
public abstract void customizeOptions(VueComponentOptions vueComponentOptions);
9+
}

src/main/java/com/axellience/vuegwt/jsr69/component/ComponentGenerationUtil.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.axellience.vuegwt.client.component.HasRender;
44
import com.axellience.vuegwt.client.component.VueComponent;
5+
import com.axellience.vuegwt.client.component.options.CustomizeOptions;
56
import com.axellience.vuegwt.client.vue.VueFactory;
67
import com.axellience.vuegwt.jsr69.component.annotations.Component;
78
import com.axellience.vuegwt.jsr69.component.annotations.Computed;
@@ -76,6 +77,37 @@ public static List<TypeMirror> getComponentLocalComponents(Elements elementsUtil
7677
}
7778
}
7879

80+
/**
81+
* Return the list of {@link CustomizeOptions} that will be used to customize the options
82+
* before passing them down to Vue.js.
83+
* When retrieving this list, it can produce a {@link MirroredTypesException}.
84+
* The case is managed here and we always returns {@link TypeMirror} of the items.
85+
* @param elementsUtil The Element utils provided by the annotation processor environement.
86+
* @param component The {@link Component} annotation to process
87+
* @return The list of TypeMirror of the {@link CustomizeOptions} the {@link Component} depends on
88+
*/
89+
public static List<TypeMirror> getComponentCustomizeOptions(Elements elementsUtil,
90+
TypeElement component)
91+
{
92+
Component componentAnnotation = component.getAnnotation(Component.class);
93+
94+
try
95+
{
96+
Class<?>[] componentsClass = componentAnnotation.customizeOptions();
97+
98+
return Stream
99+
.of(componentsClass)
100+
.map(Class::getCanonicalName)
101+
.map(elementsUtil::getTypeElement)
102+
.map(TypeElement::asType)
103+
.collect(Collectors.toList());
104+
}
105+
catch (MirroredTypesException mte)
106+
{
107+
return new LinkedList<>(mte.getTypeMirrors());
108+
}
109+
}
110+
79111
/**
80112
* Resolve a variable element {@link TypeName}. If the type cannot be resolved (the TypeMirror
81113
* is of

src/main/java/com/axellience/vuegwt/jsr69/component/annotations/Component.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.axellience.vuegwt.jsr69.component.annotations;
22

33
import com.axellience.vuegwt.client.component.VueComponent;
4+
import com.axellience.vuegwt.client.component.options.CustomizeOptions;
45
import com.axellience.vuegwt.client.directive.VueDirective;
56

67
import java.lang.annotation.Retention;
@@ -38,6 +39,14 @@
3839
*/
3940
Class<? extends VueDirective>[] directives() default {};
4041

42+
/**
43+
* Object responsible to customize the options of the Component.
44+
* This can be used to register routes with Vue router, or more.
45+
* They are injected if the component is injected.
46+
* @return This list of {@link CustomizeOptions}
47+
*/
48+
Class<? extends CustomizeOptions>[] customizeOptions() default {};
49+
4150
/**
4251
* A flag to set that the component doesn't have a template.
4352
* If the component is abstract, or implement HasRender then it's consider false by default.

src/main/java/com/axellience/vuegwt/jsr69/component/annotations/VueCustomizeOptions.java

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/main/java/com/axellience/vuegwt/jsr69/component/factory/VueComponentFactoryGenerator.java

Lines changed: 27 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.axellience.vuegwt.client.Vue;
44
import com.axellience.vuegwt.client.component.VueComponent;
5+
import com.axellience.vuegwt.client.component.options.CustomizeOptions;
56
import com.axellience.vuegwt.client.component.options.VueComponentOptions;
67
import com.axellience.vuegwt.client.directive.options.VueDirectiveOptions;
78
import com.axellience.vuegwt.client.jsnative.jstypes.JsObject;
@@ -10,40 +11,32 @@
1011
import com.axellience.vuegwt.client.vue.VueJsConstructor;
1112
import com.axellience.vuegwt.jsr69.GenerationNameUtil;
1213
import com.axellience.vuegwt.jsr69.component.annotations.Component;
13-
import com.axellience.vuegwt.jsr69.component.annotations.VueCustomizeOptions;
1414
import com.squareup.javapoet.ClassName;
1515
import com.squareup.javapoet.CodeBlock;
1616
import com.squareup.javapoet.MethodSpec;
1717
import com.squareup.javapoet.ParameterizedTypeName;
18-
import com.squareup.javapoet.TypeName;
1918
import com.squareup.javapoet.TypeSpec.Builder;
2019

2120
import javax.annotation.processing.ProcessingEnvironment;
2221
import javax.inject.Inject;
23-
import javax.lang.model.element.ExecutableElement;
2422
import javax.lang.model.element.Modifier;
2523
import javax.lang.model.element.TypeElement;
26-
import javax.lang.model.element.VariableElement;
2724
import javax.lang.model.type.DeclaredType;
2825
import javax.lang.model.type.MirroredTypesException;
2926
import javax.lang.model.type.TypeMirror;
30-
import javax.lang.model.util.ElementFilter;
3127
import javax.lang.model.util.Elements;
3228
import javax.lang.model.util.Types;
33-
import javax.tools.Diagnostic.Kind;
3429
import java.util.LinkedList;
3530
import java.util.List;
3631
import java.util.Optional;
37-
import java.util.stream.Collectors;
3832
import java.util.stream.Stream;
3933

4034
import static com.axellience.vuegwt.client.tools.VueGWTTools.componentToTagName;
4135
import static com.axellience.vuegwt.client.tools.VueGWTTools.directiveToTagName;
4236
import static com.axellience.vuegwt.jsr69.GenerationNameUtil.*;
43-
import static com.axellience.vuegwt.jsr69.GenerationUtil.hasAnnotation;
37+
import static com.axellience.vuegwt.jsr69.component.ComponentGenerationUtil.getComponentCustomizeOptions;
4438
import static com.axellience.vuegwt.jsr69.component.ComponentGenerationUtil.getComponentLocalComponents;
4539
import static com.axellience.vuegwt.jsr69.component.ComponentGenerationUtil.getSuperComponentType;
46-
import static com.axellience.vuegwt.jsr69.component.ComponentGenerationUtil.resolveVariableTypeName;
4740

4841
/**
4942
* Generate {@link VueFactory} from the user {@link VueComponent} classes annotated by {@link
@@ -79,11 +72,12 @@ protected List<CodeBlock> createInitMethod(TypeElement component,
7972
VueComponentOptions.class,
8073
component.asType(),
8174
componentJsTypeName(component));
75+
processCustomizeOptions(component, initBuilder, initParametersCall);
8276

8377
// Extend the parent Component
8478
Optional<ClassName> superFactoryType =
8579
getSuperComponentType(component).map(GenerationNameUtil::componentFactoryName);
86-
80+
8781
if (superFactoryType.isPresent())
8882
{
8983
initBuilder.addParameter(superFactoryType.get(), "superFactory");
@@ -103,7 +97,6 @@ protected List<CodeBlock> createInitMethod(TypeElement component,
10397
registerProvider(component, initBuilder, initParametersCall);
10498
registerLocalComponents(component, initBuilder, initParametersCall);
10599
registerLocalDirectives(componentAnnotation, initBuilder);
106-
processCustomizeOptionsMethods(component, initBuilder, initParametersCall);
107100

108101
MethodSpec initMethod = initBuilder.build();
109102
vueFactoryClassBuilder.addMethod(initMethod);
@@ -221,123 +214,44 @@ private void addGetDirectivesStatement(MethodSpec.Builder injectDependenciesBuil
221214
}
222215

223216
/**
224-
* Process all the methods annotated with {@link VueCustomizeOptions}. These methods should be
225-
* called when the {@link VueFactory} is created. If they have more parameters than the {@link
226-
* VueComponentOptions} those parameters should be injected.
217+
* Process all the {@link CustomizeOptions} from the {@link Component} annotation.
227218
* @param component The {@link VueComponent} we generate for
228219
* @param initBuilder The builder for our {@link VueFactory} init method
229220
* @param staticInitParameters The list of static parameters to pass when calling the init
230221
* method from a static context
231222
*/
232-
private void processCustomizeOptionsMethods(TypeElement component,
233-
MethodSpec.Builder initBuilder, List<CodeBlock> staticInitParameters)
223+
private void processCustomizeOptions(TypeElement component, MethodSpec.Builder initBuilder,
224+
List<CodeBlock> staticInitParameters)
234225
{
235-
List<ExecutableElement> customizeOptionsMethods = ElementFilter
236-
.methodsIn(component.getEnclosedElements())
237-
.stream()
238-
.filter(method -> hasAnnotation(method, VueCustomizeOptions.class))
239-
.peek(this::validateCustomizeOptionsMethod)
240-
.collect(Collectors.toList());
241-
242-
customizeOptionsMethods.forEach(customizeOptionsMethod -> this.processCustomizeOptionsMethod(
243-
customizeOptionsMethod,
226+
getComponentCustomizeOptions(elements,
227+
component).forEach(customizeOptions -> this.processCustomizeOptions(customizeOptions,
244228
initBuilder,
245-
component,
246229
staticInitParameters));
247230
}
248231

249232
/**
250-
* Process a specific method annotated with {@link VueCustomizeOptions}. This method should be
251-
* called when the {@link VueFactory} is created. If it has more parameters than the {@link
252-
* VueComponentOptions} those parameters should be injected.
253-
* @param customizeOptionsMethod The method we are generating for
233+
* Process the {@link CustomizeOptions} from the {@link Component} annotation. An instance
234+
* of this class should be created with our factory and used to customize our
235+
* {@link VueComponentOptions} before passing them to Vue.
236+
* @param customizeOptions The {@link CustomizeOptions} we are generating for
254237
* @param initBuilder The builder for our {@link VueFactory} init method
255-
* @param component The {@link VueComponent} we generate for
256238
* @param staticInitParameters The list of static parameters to pass when calling the init
257239
* method from a static context
258240
*/
259-
private void processCustomizeOptionsMethod(ExecutableElement customizeOptionsMethod,
260-
MethodSpec.Builder initBuilder, TypeElement component, List<CodeBlock> staticInitParameters)
261-
{
262-
// Check if has at least one parameter to inject
263-
String methodName = customizeOptionsMethod.getSimpleName().toString();
264-
265-
List<String> parameters = new LinkedList<>();
266-
parameters.add("jsConstructor.getOptions()");
267-
268-
if (customizeOptionsMethod.getParameters().size() > 1)
269-
{
270-
for (int i = 1; i < customizeOptionsMethod.getParameters().size(); i++)
271-
{
272-
VariableElement parameter = customizeOptionsMethod.getParameters().get(i);
273-
274-
String parameterName = methodName + "_" + parameter.getSimpleName().toString();
275-
parameters.add(parameterName);
276-
initBuilder.addParameter(resolveVariableTypeName(parameter, messager),
277-
parameterName);
278-
staticInitParameters.add(CodeBlock.of("null"));
279-
}
280-
}
281-
282-
initBuilder.addStatement("$T.$L($L)",
283-
component.asType(),
284-
methodName,
285-
String.join(", ", parameters));
286-
}
287-
288-
/**
289-
* Validate that the {@link VueCustomizeOptions} method is declared correctly.
290-
* @param customizeOptionsMethod The method to validate
291-
*/
292-
private void validateCustomizeOptionsMethod(ExecutableElement customizeOptionsMethod)
293-
{
294-
// Check that the method is static
295-
if (!customizeOptionsMethod.getModifiers().contains(Modifier.STATIC))
296-
{
297-
printCustomizeOptionsError("The method should be static.", customizeOptionsMethod);
298-
return;
299-
}
300-
301-
// Check that the method is not private
302-
if (customizeOptionsMethod.getModifiers().contains(Modifier.PRIVATE))
303-
{
304-
printCustomizeOptionsError(
305-
"The method cannot be private, please make it at least package-protected.",
306-
customizeOptionsMethod);
307-
return;
308-
}
309-
310-
// Check we have at least one parameter
311-
if (customizeOptionsMethod.getParameters().isEmpty())
312-
{
313-
printCustomizeOptionsError(
314-
"The method should have at least one parameter of type VueComponentOptions.",
315-
customizeOptionsMethod);
316-
return;
317-
}
318-
319-
// Check first parameter type
320-
VariableElement parameter = customizeOptionsMethod.getParameters().get(0);
321-
TypeName parameterType = TypeName.get(parameter.asType());
322-
if (parameterType instanceof ParameterizedTypeName)
323-
parameterType = ((ParameterizedTypeName) parameterType).rawType;
324-
325-
if (!parameterType.equals(ClassName.get(VueComponentOptions.class)))
326-
{
327-
printCustomizeOptionsError("The method first parameter of type VueComponentOptions.",
328-
customizeOptionsMethod);
329-
}
330-
}
331-
332-
private void printCustomizeOptionsError(String message,
333-
ExecutableElement customizeOptionsMethod)
241+
private void processCustomizeOptions(TypeMirror customizeOptions,
242+
MethodSpec.Builder initBuilder, List<CodeBlock> staticInitParameters)
334243
{
335-
messager.printMessage(Kind.ERROR,
336-
"On method "
337-
+ customizeOptionsMethod.getSimpleName()
338-
+ " annotated with @VueCustomizeOptions in component "
339-
+ customizeOptionsMethod.getEnclosingElement().getSimpleName()
340-
+ ": "
341-
+ message);
244+
ClassName customizeOptionsClassName = ((ClassName) ClassName.get(customizeOptions));
245+
char c[] = customizeOptionsClassName.simpleName().toCharArray();
246+
c[0] = Character.toLowerCase(c[0]);
247+
String parameterName = new String(c);
248+
249+
initBuilder.addParameter(customizeOptionsClassName, parameterName);
250+
staticInitParameters.add(CodeBlock.of("new $T()", customizeOptionsClassName));
251+
252+
initBuilder.addStatement("$L.$L($L)",
253+
parameterName,
254+
"customizeOptions",
255+
"componentOptions");
342256
}
343257
}

0 commit comments

Comments
 (0)