Skip to content

Commit d8fc80c

Browse files
authored
Support for validation of Duration and URI default values. (helidon-io#9166)
1 parent 18b995b commit d8fc80c

10 files changed

Lines changed: 263 additions & 23 deletions

File tree

builder/codegen/src/main/java/io/helidon/builder/codegen/PrototypeProperty.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,13 @@ static PrototypeProperty create(CodegenContext ctx,
7878

7979
boolean sameGeneric = element.hasAnnotation(Types.OPTION_SAME_GENERIC);
8080
// to help with defaults, setters, config mapping etc.
81-
TypeHandler typeHandler = TypeHandler.create(name, getterName, setterName, returnType, sameGeneric);
81+
TypeHandler typeHandler = TypeHandler.create(blueprint.typeName(),
82+
element,
83+
name,
84+
getterName,
85+
setterName,
86+
returnType,
87+
sameGeneric);
8288

8389
// all information from @ConfiguredOption annotation
8490
AnnotationDataOption configuredOption = AnnotationDataOption.create(typeHandler, element);

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandler.java

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Optional;
2727
import java.util.function.Consumer;
2828

29+
import io.helidon.codegen.CodegenValidator;
2930
import io.helidon.codegen.classmodel.ContentBuilder;
3031
import io.helidon.codegen.classmodel.Field;
3132
import io.helidon.codegen.classmodel.InnerClass;
@@ -34,39 +35,57 @@
3435
import io.helidon.common.types.AccessModifier;
3536
import io.helidon.common.types.TypeName;
3637
import io.helidon.common.types.TypeNames;
38+
import io.helidon.common.types.TypedElementInfo;
39+
40+
import static io.helidon.builder.codegen.Types.OPTION_DEFAULT;
3741

3842
class TypeHandler {
43+
private final TypeName enclosingType;
44+
private final TypedElementInfo annotatedMethod;
3945
private final String name;
4046
private final String getterName;
4147
private final String setterName;
4248
private final TypeName declaredType;
4349

44-
TypeHandler(String name, String getterName, String setterName, TypeName declaredType) {
50+
TypeHandler(TypeName enclosingType,
51+
TypedElementInfo annotatedMethod,
52+
String name,
53+
String getterName,
54+
String setterName,
55+
TypeName declaredType) {
56+
this.enclosingType = enclosingType;
57+
this.annotatedMethod = annotatedMethod;
4558
this.name = name;
4659
this.getterName = getterName;
4760
this.setterName = setterName;
4861
this.declaredType = declaredType;
4962
}
5063

51-
static TypeHandler create(String name, String getterName, String setterName, TypeName returnType, boolean sameGeneric) {
64+
static TypeHandler create(TypeName blueprintType,
65+
TypedElementInfo annotatedMethod,
66+
String name,
67+
String getterName,
68+
String setterName,
69+
TypeName returnType,
70+
boolean sameGeneric) {
5271
if (TypeNames.OPTIONAL.equals(returnType)) {
53-
return new TypeHandlerOptional(name, getterName, setterName, returnType);
72+
return new TypeHandlerOptional(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
5473
}
5574
if (TypeNames.SUPPLIER.equals(returnType)) {
56-
return new TypeHandlerSupplier(name, getterName, setterName, returnType);
75+
return new TypeHandlerSupplier(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
5776
}
5877
if (TypeNames.SET.equals(returnType)) {
59-
return new TypeHandlerSet(name, getterName, setterName, returnType);
78+
return new TypeHandlerSet(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
6079
}
6180

6281
if (TypeNames.LIST.equals(returnType)) {
63-
return new TypeHandlerList(name, getterName, setterName, returnType);
82+
return new TypeHandlerList(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
6483
}
6584
if (TypeNames.MAP.equals(returnType)) {
66-
return new TypeHandlerMap(name, getterName, setterName, returnType, sameGeneric);
85+
return new TypeHandlerMap(blueprintType, annotatedMethod, name, getterName, setterName, returnType, sameGeneric);
6786
}
6887

69-
return new TypeHandler(name, getterName, setterName, returnType);
88+
return new TypeHandler(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
7089
}
7190

7291
static AccessModifier setterAccessModifier(AnnotationDataOption configured) {
@@ -160,6 +179,8 @@ Consumer<ContentBuilder<?>> toDefaultValue(String defaultValue) {
160179
.addContent("\"");
161180
}
162181
if (TypeNames.DURATION.equals(typeName)) {
182+
183+
CodegenValidator.validateDuration(enclosingType, annotatedMethod, OPTION_DEFAULT, "value", defaultValue);
163184
return content -> content.addContent(Duration.class)
164185
.addContent(".parse(\"")
165186
.addContent(defaultValue)
@@ -177,6 +198,7 @@ Consumer<ContentBuilder<?>> toDefaultValue(String defaultValue) {
177198
.addContent("\")");
178199
}
179200
if (Types.URI.equals(typeName)) {
201+
CodegenValidator.validateUri(enclosingType, annotatedMethod, OPTION_DEFAULT, "value", defaultValue);
180202
return content -> content.addContent(URI.class)
181203
.addContent(".create(\"")
182204
.addContent(defaultValue)
@@ -628,8 +650,13 @@ private void factorySetter(InnerClass.Builder classBuilder,
628650
static class OneTypeHandler extends TypeHandler {
629651
private final TypeName actualType;
630652

631-
OneTypeHandler(String name, String getterName, String setterName, TypeName declaredType) {
632-
super(name, getterName, setterName, declaredType);
653+
OneTypeHandler(TypeName enclosingType,
654+
TypedElementInfo annotatedMethod,
655+
String name,
656+
String getterName,
657+
String setterName,
658+
TypeName declaredType) {
659+
super(enclosingType, annotatedMethod, name, getterName, setterName, declaredType);
633660

634661
if (declaredType.typeArguments().isEmpty()) {
635662
this.actualType = TypeNames.STRING;

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerCollection.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import io.helidon.codegen.classmodel.Method;
5151
import io.helidon.common.types.TypeName;
5252
import io.helidon.common.types.TypeNames;
53+
import io.helidon.common.types.TypedElementInfo;
5354

5455
import static io.helidon.builder.codegen.Types.COMMON_CONFIG;
5556
import static io.helidon.codegen.CodegenUtil.capitalize;
@@ -94,14 +95,16 @@ abstract class TypeHandlerCollection extends TypeHandler.OneTypeHandler {
9495
private final String collector;
9596
private final Optional<String> configMapper;
9697

97-
TypeHandlerCollection(String name,
98+
TypeHandlerCollection(TypeName blueprintType,
99+
TypedElementInfo annotatedMethod,
100+
String name,
98101
String getterName,
99102
String setterName,
100103
TypeName declaredType,
101104
TypeName collectionType,
102105
String collector,
103106
Optional<String> configMapper) {
104-
super(name, getterName, setterName, declaredType);
107+
super(blueprintType, annotatedMethod, name, getterName, setterName, declaredType);
105108
this.collectionType = collectionType;
106109
this.collectionImplType = collectionImplType(collectionType);
107110
this.collector = collector;

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerList.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@
2121
import io.helidon.codegen.CodegenUtil;
2222
import io.helidon.codegen.classmodel.Method;
2323
import io.helidon.common.types.TypeName;
24+
import io.helidon.common.types.TypedElementInfo;
2425

2526
import static io.helidon.common.types.TypeNames.LIST;
2627

2728
class TypeHandlerList extends TypeHandlerCollection {
2829

29-
TypeHandlerList(String name, String getterName, String setterName, TypeName declaredType) {
30-
super(name, getterName, setterName, declaredType, LIST, "toList()", Optional.empty());
30+
TypeHandlerList(TypeName blueprintType,
31+
TypedElementInfo annotatedMethod,
32+
String name, String getterName, String setterName, TypeName declaredType) {
33+
super(blueprintType, annotatedMethod, name, getterName, setterName, declaredType, LIST, "toList()", Optional.empty());
3134
}
3235

3336
static String isMutatedField(String propertyName) {

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerMap.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.helidon.codegen.classmodel.TypeArgument;
3131
import io.helidon.common.types.TypeName;
3232
import io.helidon.common.types.TypeNames;
33+
import io.helidon.common.types.TypedElementInfo;
3334

3435
import static io.helidon.codegen.CodegenUtil.capitalize;
3536
import static io.helidon.common.types.TypeNames.LIST;
@@ -43,8 +44,10 @@ class TypeHandlerMap extends TypeHandler {
4344
private final TypeName implTypeName;
4445
private final boolean sameGeneric;
4546

46-
TypeHandlerMap(String name, String getterName, String setterName, TypeName declaredType, boolean sameGeneric) {
47-
super(name, getterName, setterName, declaredType);
47+
TypeHandlerMap(TypeName blueprintType,
48+
TypedElementInfo annotatedMethod,
49+
String name, String getterName, String setterName, TypeName declaredType, boolean sameGeneric) {
50+
super(blueprintType, annotatedMethod, name, getterName, setterName, declaredType);
4851
this.sameGeneric = sameGeneric;
4952

5053
this.implTypeName = collectionImplType(MAP);

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerOptional.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import io.helidon.common.types.AccessModifier;
3030
import io.helidon.common.types.TypeName;
3131
import io.helidon.common.types.TypeNames;
32+
import io.helidon.common.types.TypedElementInfo;
3233

3334
import static io.helidon.builder.codegen.Types.CHAR_ARRAY;
3435
import static io.helidon.codegen.CodegenUtil.capitalize;
@@ -67,8 +68,10 @@ class TypeHandlerOptional extends TypeHandler.OneTypeHandler {
6768
BOXED_VOID, PRIMITIVE_VOID
6869
);
6970

70-
TypeHandlerOptional(String name, String getterName, String setterName, TypeName declaredType) {
71-
super(name, getterName, setterName, declaredType);
71+
TypeHandlerOptional(TypeName blueprintType,
72+
TypedElementInfo annotatedMethod,
73+
String name, String getterName, String setterName, TypeName declaredType) {
74+
super(blueprintType, annotatedMethod, name, getterName, setterName, declaredType);
7275
}
7376

7477
@Override

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerSet.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@
1919
import java.util.Optional;
2020

2121
import io.helidon.common.types.TypeName;
22+
import io.helidon.common.types.TypedElementInfo;
2223

2324
import static io.helidon.common.types.TypeNames.SET;
2425

2526
class TypeHandlerSet extends TypeHandlerCollection {
2627

27-
TypeHandlerSet(String name, String getterName, String setterName, TypeName declaredType) {
28-
super(name,
28+
TypeHandlerSet(TypeName blueprintType,
29+
TypedElementInfo annotatedMethod,
30+
String name, String getterName, String setterName, TypeName declaredType) {
31+
super(blueprintType,
32+
annotatedMethod,
33+
name,
2934
getterName,
3035
setterName,
3136
declaredType,

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerSupplier.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@
2525
import io.helidon.codegen.classmodel.Method;
2626
import io.helidon.common.types.TypeName;
2727
import io.helidon.common.types.TypeNames;
28+
import io.helidon.common.types.TypedElementInfo;
2829

2930
import static io.helidon.builder.codegen.Types.CHAR_ARRAY;
3031
import static io.helidon.common.types.TypeNames.SUPPLIER;
3132

3233
class TypeHandlerSupplier extends TypeHandler.OneTypeHandler {
3334

34-
TypeHandlerSupplier(String name, String getterName, String setterName, TypeName declaredType) {
35-
super(name, getterName, setterName, declaredType);
35+
TypeHandlerSupplier(TypeName blueprintType,
36+
TypedElementInfo annotatedMethod,
37+
String name, String getterName, String setterName, TypeName declaredType) {
38+
super(blueprintType, annotatedMethod, name, getterName, setterName, declaredType);
3639
}
3740

3841
@Override
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates.
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+
* http://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+
17+
package io.helidon.codegen;
18+
19+
import java.net.URI;
20+
import java.time.Duration;
21+
22+
import io.helidon.common.types.TypeName;
23+
import io.helidon.common.types.TypedElementInfo;
24+
25+
/**
26+
* Validation utilities.
27+
*/
28+
public final class CodegenValidator {
29+
private CodegenValidator() {
30+
}
31+
32+
/**
33+
* Validate a URI value in an annotation.
34+
*
35+
* @param enclosingType type that owns the element
36+
* @param element annotated element
37+
* @param annotationType type of annotation
38+
* @param property property of annotation
39+
* @param value actual value read from the annotation property
40+
* @return the value
41+
* @throws io.helidon.codegen.CodegenException with correct source element describing the problem
42+
*/
43+
public static String validateUri(TypeName enclosingType,
44+
TypedElementInfo element,
45+
TypeName annotationType,
46+
String property,
47+
String value) {
48+
try {
49+
URI.create(value);
50+
return value;
51+
} catch (Exception e) {
52+
throw new CodegenException("URI expression of annotation " + annotationType.fqName() + "."
53+
+ property + "(): "
54+
+ "\"" + value + "\" cannot be parsed. Invalid URI.",
55+
e,
56+
element.originatingElement().orElseGet(() -> enclosingType.fqName() + "."
57+
+ element.elementName()));
58+
}
59+
}
60+
61+
/**
62+
* Validate a duration annotation on a method, field, or constructor.
63+
*
64+
* @param enclosingType type that owns the element
65+
* @param element annotated element
66+
* @param annotationType type of annotation
67+
* @param property property of annotation
68+
* @param value actual value read from the annotation property
69+
* @return the value
70+
* @throws io.helidon.codegen.CodegenException with correct source element describing the problem
71+
*/
72+
public static String validateDuration(TypeName enclosingType,
73+
TypedElementInfo element,
74+
TypeName annotationType,
75+
String property,
76+
String value) {
77+
try {
78+
Duration.parse(value);
79+
return value;
80+
} catch (Exception e) {
81+
throw new CodegenException("Duration expression of annotation " + annotationType.fqName() + "."
82+
+ property + "(): "
83+
+ "\"" + value + "\" cannot be parsed. Duration expects an"
84+
+ " expression such as 'PT1S' (1 second), 'PT0.1S' (tenth of a second)."
85+
+ " Please check javadoc of " + Duration.class.getName() + " class.",
86+
e,
87+
element.originatingElement().orElseGet(() -> enclosingType.fqName() + "."
88+
+ element.elementName()));
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)