Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasstamann committed Feb 5, 2024
2 parents 818ff32 + 91095c8 commit 6ab797f
Show file tree
Hide file tree
Showing 42 changed files with 1,042 additions and 345 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![release_on_master](https://github.com/toolisticon/FluApiGen/actions/workflows/release.yml/badge.svg?branch=master)](https://github.com/toolisticon/FluApiGen/actions/workflows/release.yml)
[![codecov](https://codecov.io/gh/toolisticon/FluApiGen/branch/develop/graph/badge.svg?token=FlcugFxC64)](https://codecov.io/gh/toolisticon/FluApiGen)


Implementing and especially maintaining of fluent and immutable apis is one of a most annoying and difficult tasks to do in java developing.

You usually have to implement a lot of boilerplate code that is only needed to handle and clone the fluent apis internal state.
Expand Down
2 changes: 1 addition & 1 deletion fluapigen-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>io.toolisticon.fluapigen</groupId>
<artifactId>fluapigen</artifactId>
<version>0.8.3</version>
<version>0.9.0</version>
</parent>

<name>fluapigen-api</name>
Expand Down
2 changes: 1 addition & 1 deletion fluapigen-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>io.toolisticon.fluapigen</groupId>
<artifactId>fluapigen</artifactId>
<version>0.8.3</version>
<version>0.9.0</version>
</parent>

<name>fluapigen-example</name>
Expand Down
2 changes: 1 addition & 1 deletion fluapigen-integrationTest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>io.toolisticon.fluapigen</groupId>
<artifactId>fluapigen</artifactId>
<version>0.8.3</version>
<version>0.9.0</version>
</parent>

<name>fluapigen-integrationTest</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.toolisticon.fluapigen.validation.api.Matches;
import io.toolisticon.fluapigen.validation.api.MaxLength;
import io.toolisticon.fluapigen.validation.api.NotNull;
import io.toolisticon.fluapigen.validation.api.Nullable;

@FluentApi("ValidatorExampleStarter")
public class ValidatorExample {
Expand All @@ -26,10 +27,18 @@ interface MyBackingBean {
// Fluent Api interfaces
@FluentApiInterface(MyBackingBean.class)
@FluentApiRoot
@Nullable
public interface MyRootInterface {

MyRootInterface setName(@NotNull @MaxLength(8) @Matches("aaa.*") @FluentApiBackingBeanMapping("name") String name);

@NotNull
MyRootInterface setNameWithNullableOnParameter(@Nullable @MaxLength(8) @Matches("aaa.*") @FluentApiBackingBeanMapping("name") String name);

@Nullable
MyRootInterface setNameWithNullableOnMethod(@NotNull @MaxLength(8) @Matches("aaa.*") @FluentApiBackingBeanMapping("name") String name);


@FluentApiCommand(MyCommand.class)
void myCommand();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.toolisticon.fluapigen.integrationtest;

import io.toolisticon.fluapigen.validation.api.ValidatorException;
import org.junit.Test;

public class ValidatorExampleTest {
Expand All @@ -15,7 +16,14 @@ public void testValidator_failingValidation() {
public void testValidator() {

ValidatorExampleStarter.setName("aaaBDSXS").myCommand();
ValidatorExampleStarter.setNameWithNullableOnParameter(null).myCommand();
ValidatorExampleStarter.setNameWithNullableOnMethod("aaaBDSXS").myCommand();

}

@Test(expected = ValidatorException.class)
public void testOverruledNotNullAtParameter() {
ValidatorExampleStarter.setNameWithNullableOnMethod(null).myCommand();
}

}
2 changes: 1 addition & 1 deletion fluapigen-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>io.toolisticon.fluapigen</groupId>
<artifactId>fluapigen</artifactId>
<version>0.8.3</version>
<version>0.9.0</version>
</parent>

<name>fluapigen-processor</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.toolisticon.aptk.tools.wrapper.VariableElementWrapper;
import io.toolisticon.fluapigen.api.FluentApiBackingBeanMapping;
import io.toolisticon.fluapigen.api.FluentApiCommand;
import io.toolisticon.fluapigen.api.FluentApiInlineBackingBeanMapping;
import io.toolisticon.fluapigen.api.FluentApiParentBackingBeanMapping;
import io.toolisticon.fluapigen.api.TargetBackingBean;

Expand Down Expand Up @@ -288,6 +289,21 @@ public boolean hasInlineBackingBeanMapping() {
public boolean validate() {

boolean outcome = true;

// check inline backing bean
if (this.inlineBackingBeanMapping != null) {

// validate
this.inlineBackingBeanMapping.validate();

if (!getInlineBackingBean().isPresent()) {
this.inlineBackingBeanMapping.compilerMessage().asError().write(FluentApiProcessorCompilerMessages.ERROR_INLINE_BB_MAPPING_COULDNT_BE_RESOLVED);
return false;
}


}

// check parameters
// -> names must match an existing target field

Expand Down Expand Up @@ -325,20 +341,6 @@ public boolean validate() {

}

// check inline backing bean
if (this.inlineBackingBeanMapping != null) {

// validate
this.inlineBackingBeanMapping.validate();

if (!getInlineBackingBean().isPresent()) {
this.inlineBackingBeanMapping.compilerMessage().asError().write(FluentApiProcessorCompilerMessages.ERROR_INLINE_BB_MAPPING_COULDNT_BE_RESOLVED);
outcome = false;
}


}

// must check command case
if (isCommandMethod()) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage;
import io.toolisticon.aptk.tools.InterfaceUtils;
import io.toolisticon.aptk.tools.TypeMirrorWrapper;
import io.toolisticon.aptk.tools.wrapper.ElementWrapper;
import io.toolisticon.aptk.tools.wrapper.VariableElementWrapper;
import io.toolisticon.fluapigen.api.FluentApiBackingBeanMapping;
import io.toolisticon.fluapigen.api.FluentApiConverter;
import io.toolisticon.fluapigen.api.FluentApiInlineBackingBeanMapping;
import io.toolisticon.fluapigen.api.TargetBackingBean;
import io.toolisticon.fluapigen.validation.api.FluentApiValidator;

import javax.lang.model.element.Element;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -83,7 +87,29 @@ public boolean hasValidators() {
}

public List<ModelValidator> getValidators() {
return this.parameterElement.getAnnotations().stream().filter(e -> e.asElement().hasAnnotation(FluentApiValidator.class)).map(e -> new ModelValidator(this.parameterElement, e)).collect(Collectors.toList());

// First get all enclosing elements - order is from element to top level parent
List<ElementWrapper<Element>> enclosingElements = this.parameterElement.getAllEnclosingElements(true);

// now map it to ModelValidators
List<ModelValidator> allValidatorsWithoutOverwrites = enclosingElements.stream()
.flatMap(
e -> e.getAnnotations().stream()
.filter(f -> f.asElement().hasAnnotation(FluentApiValidator.class))
.map(f -> new ModelValidator(this.parameterElement, f))
).collect(Collectors.toList());

// Must remove overwritten validators - add them one by one from lowest to highest level and check if the element to add is already overruled by thos in list.
List<ModelValidator> result = new ArrayList();

for (ModelValidator nextValidator : allValidatorsWithoutOverwrites) {
if (!nextValidator.isOverruledBy(result)) {
result.add(nextValidator);
}
}

return result;

}

public String getParameterName() {
Expand Down Expand Up @@ -209,6 +235,7 @@ private void validateConverter(ModelBackingBeanField backBeanField) {
@DeclareCompilerMessage(code = "001", enumValueName = "BB_MAPPING_ANNOTATION_MUST_BE_PRESENT", message = "${0} annotation must be present on parameter", processorClass = FluentApiProcessor.class)
@DeclareCompilerMessage(code = "002", enumValueName = "PARAMETER_AND_MAPPED_BB_FIELD_MUST_HAVE_SAME_TYPE", message = "Parameter type (${0}) must match backing bean field type (${1})", processorClass = FluentApiProcessor.class)
@DeclareCompilerMessage(code = "003", enumValueName = "BB_MAPPING_COULDNT_BE_RESOLVED", message = "Field ${0} doesn't exist in mapped backing bean ${1}. It must be one of: [${2}]", processorClass = FluentApiProcessor.class)
@DeclareCompilerMessage(code = "004", enumValueName = "INLINE_BB_MAPPING_ANNOTATION_IS_MISSING_ON_METHOD", message = "Parameter ${0} has target INLINE but no ${1} annotation is present on method", processorClass = FluentApiProcessor.class)
public boolean validate() {

// check validators
Expand All @@ -225,6 +252,12 @@ public boolean validate() {
return false;
}

// must check if Inline annotation is placed on method for INLINE target
if (fluentApiBackingBeanMapping.target() == TargetBackingBean.INLINE && !modelInterfaceMethod.getExecutableElement().hasAnnotation(FluentApiInlineBackingBeanMapping.class)) {
parameterElement.compilerMessage().asError().write(FluentApiProcessorCompilerMessages.INLINE_BB_MAPPING_ANNOTATION_IS_MISSING_ON_METHOD, parameterElement.getSimpleName(), FluentApiInlineBackingBeanMapping.class.getSimpleName());
return false;
}

if (!getBackingBeanField().isPresent()) {

Optional<ModelBackingBean> referencedBackingBean = fluentApiBackingBeanMapping.target() == TargetBackingBean.THIS ? Optional.of(backingBeanModel) : modelInterfaceMethod.getNextBackingBean();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import javax.lang.model.element.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ModelValidator {
Expand All @@ -28,6 +29,23 @@ FluentApiValidatorWrapper getValidatorAnnotation() {
return FluentApiValidatorWrapper.wrap(this.validatorAnnotation.asElement().unwrap());
}

// visible for testing
AnnotationMirrorWrapper getAnnotationMirrorWrapper() {
return validatorAnnotation;
}

public boolean isOverruledBy (List<ModelValidator> existingLowestLevelValidators) {
for (ModelValidator existinModelValidator : existingLowestLevelValidators) {

for (String overruledAnnotation : existinModelValidator.getValidatorAnnotation().overwritesAsFqn()) {
if (overruledAnnotation.equals(validatorAnnotation.asTypeMirror().getQualifiedName())) {
return true;
}
}

}
return false;
}

@DeclareCompilerMessage(code = "752", enumValueName = "ERROR_BROKEN_VALIDATOR_ATTRIBUTE_NAME_MISMATCH", message = "The configured validator ${0} seems to have a broken annotation attribute to validator constructor parameter mapping. Attributes names ${1} doesn't exist. Please fix validator implementation or don't use it.", processorClass = FluentApiProcessor.class)
@DeclareCompilerMessage(code = "753", enumValueName = "ERROR_BROKEN_VALIDATOR_CONSTRUCTOR_PARAMETER_MAPPING", message = "The configured validator ${0} seems to have a broken annotation attribute to validator constructor parameter mapping. Please fix validator implementation or don't use it.", processorClass = FluentApiProcessor.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.toolisticon.aptk.common.ToolingProvider;
import io.toolisticon.aptk.tools.MessagerUtils;
import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper;
import io.toolisticon.cute.CompileTestBuilder;
import io.toolisticon.cute.Cute;
import io.toolisticon.cute.PassIn;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -29,7 +29,10 @@ interface ExceptionTest {
@Test
public void testWriteCompilerMessage() {

CompileTestBuilder.unitTest().<ExecutableElement>defineTestWithPassedInElement(ExceptionTest.class, (processingEnvironment, element) -> {
Cute.unitTest()
.when()
.passInElement().<ExecutableElement>fromClass(ExceptionTest.class)
.intoUnitTest((processingEnvironment, element) -> {


try {
Expand All @@ -43,8 +46,10 @@ public void testWriteCompilerMessage() {
ToolingProvider.clearTooling();
}
})
.compilationShouldFail()
.expectErrorMessageThatContains(FluentApiProcessorCompilerMessages.ERROR_CANNOT_FIND_NEXT_BACKING_BEAN.getCode(), "String method()").executeTest();
.thenExpectThat()
.compilationFails()
.andThat().compilerMessage().ofKindError().contains(FluentApiProcessorCompilerMessages.ERROR_CANNOT_FIND_NEXT_BACKING_BEAN.getCode(), "String method()")
.executeTest();

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import io.toolisticon.aptk.cute.APTKUnitTestProcessor;
import io.toolisticon.aptk.tools.MessagerUtils;
import io.toolisticon.aptk.tools.corematcher.CoreMatcherValidationMessages;
import io.toolisticon.cute.CompileTestBuilder;
import io.toolisticon.cute.Cute;
import io.toolisticon.cute.CuteApi;
import io.toolisticon.cute.PassIn;
import io.toolisticon.fluapigen.api.FluentApi;
import io.toolisticon.fluapigen.api.FluentApiBackingBean;
Expand All @@ -19,14 +20,13 @@
*/
public class CustomFluentApiBackingBeanWrapperCodeTest {

CompileTestBuilder.UnitTestBuilder unitTestBuilder;
CuteApi.UnitTestRootInterface unitTestBuilder;

@Before
public void init() {
MessagerUtils.setPrintMessageCodes(true);

unitTestBuilder = CompileTestBuilder
.unitTest();
unitTestBuilder = Cute.unitTest();
}

@FluentApi("MyTestApi")
Expand All @@ -44,15 +44,17 @@ private interface BackingBean {
@Test
public void validationTest_NoRoot() {

unitTestBuilder.<TypeElement>defineTestWithPassedInElement(NotAccessibleTestApi.class, new APTKUnitTestProcessor<TypeElement>() {
unitTestBuilder.when()
.passInElement().<TypeElement>fromClass(NotAccessibleTestApi.class)
.intoUnitTest(new APTKUnitTestProcessor<TypeElement>() {
@Override
public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElement typeElement) {

MatcherAssert.assertThat("should return false", !CustomFluentApiBackingBeanWrapperCode.validate(FluentApiBackingBeanWrapper.wrap(typeElement)));
}
})
.compilationShouldFail()
.expectErrorMessage().thatContains(CoreMatcherValidationMessages.BY_MODIFIER.getCode())
.thenExpectThat().compilationFails()
.andThat().compilerMessage().ofKindError().contains(CoreMatcherValidationMessages.BY_MODIFIER.getCode())
.executeTest();


Expand All @@ -72,18 +74,19 @@ static class BackingBean {
@Test
public void validationTest_PlacedOnClass() {

unitTestBuilder.<TypeElement>defineTestWithPassedInElement(PlacedOnClassTestApi.class, new APTKUnitTestProcessor<TypeElement>() {
unitTestBuilder.when()
.passInElement().<TypeElement>fromClass(PlacedOnClassTestApi.class)
.intoUnitTest(new APTKUnitTestProcessor<TypeElement>() {
@Override
public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElement typeElement) {

MatcherAssert.assertThat("should return false", !CustomFluentApiBackingBeanWrapperCode.validate(FluentApiBackingBeanWrapper.wrap(typeElement)));
}
})
.compilationShouldFail()
.expectErrorMessage().thatContains(CoreMatcherValidationMessages.IS_INTERFACE.getCode())
.thenExpectThat().compilationFails()
.andThat().compilerMessage().ofKindError().contains(CoreMatcherValidationMessages.IS_INTERFACE.getCode())
.executeTest();


}

}
Loading

0 comments on commit 6ab797f

Please sign in to comment.