Skip to content

Commit

Permalink
chore: further code health refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
CarstenWickner committed Nov 19, 2023
1 parent 8248ead commit f3eca09
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ private MethodScope doFindGetter() {
possibleGetterNames.add("get" + capitalisedFieldName);
possibleGetterNames.add("is" + capitalisedFieldName);
// @since 4.32.0 - for a field like "isBool" also consider "isBool()" as potential getter method
if (declaredName.startsWith("is") && declaredName.length() > 2 && Character.isUpperCase(declaredName.charAt(2))) {
boolean fieldNameStartsWithIs = declaredName.startsWith("is") && declaredName.length() > 2 && Character.isUpperCase(declaredName.charAt(2));
if (fieldNameStartsWithIs) {
possibleGetterNames.add(declaredName);
}
ResolvedMethod[] methods = this.getDeclaringTypeMembers().getMemberMethods();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,11 @@ public <A extends Annotation> A getContainerItemAnnotationConsideringFieldAndGet
if (this.isFakeContainerItemScope()) {
return this.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass, considerOtherAnnotation);
}
if (this.getOverriddenType() != null
&& this.getDeclaredType().getErasedType() == Optional.class
&& this.getOverriddenType().getErasedType() == this.getDeclaredType().getTypeParameters().get(0).getErasedType()) {
// in addition to an explicitly marked "faked container item scope", also support a type wrapped in an Optional
if (this.getOverriddenType() == null || this.getDeclaredType().getErasedType() != Optional.class) {
return null;
}
if (this.getOverriddenType().getErasedType() == this.getDeclaredType().getTypeParameters().get(0).getErasedType()) {
return this.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass, considerOtherAnnotation);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,18 @@ public FieldScope findGetterField() {
* @return associated field
*/
private FieldScope doFindGetterField() {
if (this.getType() == null || !this.isPublic() || this.getArgumentCount() > 0) {
// void and non-public methods or those with arguments are not deemed to be getters
if (this.getType() == null || this.getArgumentCount() > 0) {
// void methods or those with arguments are not deemed to be getters
return null;
}
String methodName = this.getDeclaredName();
Set<String> possibleFieldNames = new HashSet<>(3);
if (methodName.startsWith("get")) {
getPossibleFieldNamesStartingWithGet(methodName, possibleFieldNames);
} else if (methodName.startsWith("is")) {
getPossibleFieldNamesStartingWithIs(methodName, possibleFieldNames);
if (!this.isPublic()) {
// only public methods are deemed eligible to be getters
return null;
}
String methodName = this.getDeclaredName();
Set<String> possibleFieldNames = Stream.of("get", "is")
.flatMap(prefix -> getPossibleFieldNames(prefix, methodName))
.collect(Collectors.toSet());
if (possibleFieldNames.isEmpty()) {
// method name does not fall into getter conventions
return null;
Expand All @@ -150,28 +151,25 @@ private FieldScope doFindGetterField() {
.orElse(null);
}

private static void getPossibleFieldNamesStartingWithGet(String methodName, Set<String> possibleFieldNames) {
if (methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) {
// ensure that the variable starts with a lower-case letter
possibleFieldNames.add(methodName.substring(3, 4).toLowerCase() + methodName.substring(4));
private static Stream<String> getPossibleFieldNames(String prefix, String methodName) {
if (!methodName.startsWith(prefix) || prefix.length() == methodName.length()) {
return Stream.of();
}
// @since 4.32.0 - conforming with JavaBeans API specification edge case when second character in field name is in uppercase
if (methodName.length() > 4 && Character.isUpperCase(methodName.charAt(4))) {
possibleFieldNames.add(methodName.substring(3));
}
}

private static void getPossibleFieldNamesStartingWithIs(String methodName, Set<String> possibleFieldNames) {
if (methodName.length() > 2 && Character.isUpperCase(methodName.charAt(2))) {
Stream.Builder<String> possibleFieldNames = Stream.builder();
String methodNameWithoutPrefix = methodName.substring(prefix.length());
if (Character.isUpperCase(methodNameWithoutPrefix.charAt(0))) {
// ensure that the variable starts with a lower-case letter
possibleFieldNames.add(methodName.substring(2, 3).toLowerCase() + methodName.substring(3));
// since 4.32.0: a method "isBool()" is considered a possible getter for a field "isBool" as well as for "bool"
possibleFieldNames.add(methodName);
possibleFieldNames.add(methodNameWithoutPrefix.substring(0, 1).toLowerCase() + methodNameWithoutPrefix.substring(1));
if ("is".equals(prefix)) {
// since 4.32.0: a method "isBool()" is considered a possible getter for a field "isBool" as well as for "bool"
possibleFieldNames.add(methodName);
}
}
// @since 4.32.0 - conforming with JavaBeans API specification edge case when second character in field name is in uppercase
if (methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) {
possibleFieldNames.add(methodName.substring(2));
if (methodNameWithoutPrefix.length() > 1 && Character.isUpperCase(methodNameWithoutPrefix.charAt(1))) {
possibleFieldNames.add(methodNameWithoutPrefix);
}
return possibleFieldNames.build();
}

/**
Expand Down Expand Up @@ -213,7 +211,9 @@ public <A extends Annotation> A getAnnotationConsideringFieldAndGetter(Class<A>
A annotation = this.getAnnotation(annotationClass, considerOtherAnnotation);
if (annotation == null) {
MemberScope<?, ?> associatedField = this.findGetterField();
annotation = associatedField == null ? null : associatedField.getAnnotation(annotationClass, considerOtherAnnotation);
if (associatedField != null) {
annotation = associatedField.getAnnotation(annotationClass, considerOtherAnnotation);
}
}
return annotation;
}
Expand All @@ -224,7 +224,9 @@ public <A extends Annotation> A getContainerItemAnnotationConsideringFieldAndGet
A annotation = this.getContainerItemAnnotation(annotationClass, considerOtherAnnotation);
if (annotation == null) {
MemberScope<?, ?> associatedField = this.findGetterField();
annotation = associatedField == null ? null : associatedField.getContainerItemAnnotation(annotationClass, considerOtherAnnotation);
if (associatedField != null) {
annotation = associatedField.getContainerItemAnnotation(annotationClass, considerOtherAnnotation);
}
}
return annotation;
}
Expand All @@ -239,21 +241,19 @@ public <A extends Annotation> A getContainerItemAnnotationConsideringFieldAndGet
@Override
protected String doGetSchemaPropertyName() {
String result = this.getName();
if (this.getContext().isDerivingFieldsFromArgumentFreeMethods() && this.getArgumentCount() == 0) {
if (this.getOverriddenName() == null) {
// remove the "get"/"is" prefix from non-overridden method names
if (result.startsWith("get") && result.length() > 3) {
result = Character.toLowerCase(result.charAt(3)) + result.substring(4);
} else if (result.startsWith("is") && result.length() > 2) {
result = Character.toLowerCase(result.charAt(2)) + result.substring(3);
} else {
result += "()";
}
}
} else {
if (!this.getContext().isDerivingFieldsFromArgumentFreeMethods() || this.getArgumentCount() > 0) {
result += this.getArgumentTypes().stream()
.map(this.getContext()::getMethodPropertyArgumentTypeDescription)
.collect(Collectors.joining(", ", "(", ")"));
} else if (this.getOverriddenName() == null) {
// remove the "get"/"is" prefix from non-overridden method names
if (result.startsWith("get") && result.length() > 3) {
result = Character.toLowerCase(result.charAt(3)) + result.substring(4);
} else if (result.startsWith("is") && result.length() > 2) {
result = Character.toLowerCase(result.charAt(2)) + result.substring(3);
} else {
result += "()";
}
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.List;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -188,10 +189,15 @@ public TypeScope createTypeScope(ResolvedType type) {
*/
public ResolvedType getTypeParameterFor(ResolvedType type, Class<?> erasedSuperType, int parameterIndex) {
List<ResolvedType> typeParameters = type.typeParametersFor(erasedSuperType);
if (typeParameters == null
|| (!typeParameters.isEmpty() && typeParameters.size() <= parameterIndex)
|| (typeParameters.isEmpty() && erasedSuperType.getTypeParameters().length <= parameterIndex)) {
// given type is not a super type of the type in scope or given index is out of bounds
if (typeParameters == null) {
// given type is not a super type of the type in scope
return null;
}
if (!typeParameters.isEmpty() && typeParameters.size() <= parameterIndex) {
// given index is out of bounds for the specific type
}
if (typeParameters.isEmpty() && erasedSuperType.getTypeParameters().length <= parameterIndex) {
// given index is out of bounds for the designated super type
return null;
}
if (typeParameters.isEmpty()) {
Expand Down Expand Up @@ -441,4 +447,27 @@ private String getTypeDescription(ResolvedType type, boolean simpleClassNames) {
public String getMethodPropertyArgumentTypeDescription(ResolvedType type) {
return this.getSimpleTypeDescription(type);
}

/**
* Helper function to write generic code that targets either a {@link FieldScope} or {@link MethodScope}.
*
* @param <R> type of expected return value
* @param member field or method being targeted
* @param fieldAction action to perform in case the given member is a {@link FieldScope}
* @param methodAction action to perform in case the given member is a {@link MethodScope}
* @return value returned by the performed action
* @throws IllegalStateException if given member is neither a {@link FieldScope} or {@link MethodScope}
*/
public <R> R performActionOnMember(MemberScope<?, ?> member, Function<FieldScope, R> fieldAction,
Function<MethodScope, R> methodAction) {
R result;
if (member instanceof FieldScope) {
result = fieldAction.apply((FieldScope) member);
} else if (member instanceof MethodScope) {
result = methodAction.apply((MethodScope) member);
} else {
throw new IllegalStateException("Unsupported member scope of type: " + member.getClass());
}
return result;
}
}
Loading

0 comments on commit f3eca09

Please sign in to comment.