diff --git a/README.md b/README.md index d90913e..00e3df8 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,54 @@ public interface MyFluentInterface { } ``` - +### Using validators at fluent interface method parameters +FluApiGen provides some basic validators that can be applied to the fluent method parameters at runtime. + +- _Matches_ : Regular Expression validator for Strings +- _MinLength / MaxLength_ : Checks length of Strings or size of Arrays or Collections +- _NotEmpty_ : Checks if String, array and Collection are not empty +- _NotNull_ : Checks if passed argument isn't null + +To be able to use validators, the _fluapigen_validation_api_ library must be linked at compile and runtime. + +````java +MyRootInterface setName(@Matches("Max.*") @FluentApiBackingBeanMapping("name") String name); +```` + +In this example the fluent api would throw a _ValidatorException_ if the name doesn't start with "Max". + +It's possible to use multiple validators on a parameter and even to provide custom validators: + +````java +@FluentApiValidator(value = Matches.ValidatorImpl.class, parameterNames = {"value"}) +public @interface Matches { + + String value(); + + class ValidatorImpl implements Validator { + + private final String regularExpression; + + public ValidatorImpl(String regularExpression) { + this.regularExpression = regularExpression; + } + + @Override + public boolean validate(String obj) { + return Pattern.compile(regularExpression).matcher(obj).matches(); + } + + } + +} +```` + +Validators are annotations annotated with _FluentApiValidator_ meta annotation. The _FluentApiValidator_ annotation is used to reference the validators implementation class that must implement the _Validator_ interface. +Validation criteria can be added as annotation attribute. The _FluentApiValidator_ meta annotation defines the attribute to validator constructor mapping via the parameterNames attribute. +The validator implementation must provide a matching constructor. + +*Remark : The feature is currently under development and not 100% done. There will still be improvements regarding processing time validation and error output* + ### Javas default methods in fluent api and backing bean in interfaces Default methods will be ignored during processing of fluent api and backing bean interfaces and can be used for different tasks: diff --git a/fluapigen-api/pom.xml b/fluapigen-api/pom.xml index e0db9e9..fae8949 100644 --- a/fluapigen-api/pom.xml +++ b/fluapigen-api/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.fluapigen fluapigen - 0.7.0 + 0.8.0 fluapigen-api @@ -25,7 +25,14 @@ - + + + + io.toolisticon.fluapigen + fluapigen-validation-api + + + diff --git a/fluapigen-example/pom.xml b/fluapigen-example/pom.xml index ac5bba6..91b21d3 100644 --- a/fluapigen-example/pom.xml +++ b/fluapigen-example/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.fluapigen fluapigen - 0.7.0 + 0.8.0 fluapigen-example diff --git a/fluapigen-example/src/main/java/io/toolisticon/fluapigen/ExampleFluentApi.java b/fluapigen-example/src/main/java/io/toolisticon/fluapigen/ExampleFluentApi.java index cbcfb8c..aeb5c3a 100644 --- a/fluapigen-example/src/main/java/io/toolisticon/fluapigen/ExampleFluentApi.java +++ b/fluapigen-example/src/main/java/io/toolisticon/fluapigen/ExampleFluentApi.java @@ -8,6 +8,7 @@ import io.toolisticon.fluapigen.api.FluentApiInterface; import io.toolisticon.fluapigen.api.FluentApiRoot; + @FluentApi("ExampleFluentApiStarter") public class ExampleFluentApi { diff --git a/fluapigen-integrationTest/pom.xml b/fluapigen-integrationTest/pom.xml index f0a37cd..2665605 100644 --- a/fluapigen-integrationTest/pom.xml +++ b/fluapigen-integrationTest/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.fluapigen fluapigen - 0.7.0 + 0.8.0 fluapigen-integrationTest @@ -29,7 +29,11 @@ fluapigen-processor ${project.version} - + + io.toolisticon.fluapigen + fluapigen-api + ${project.version} + @@ -97,6 +101,11 @@ fluapigen-api + + io.toolisticon.fluapigen + fluapigen-validation-api + + io.toolisticon.cute cute diff --git a/fluapigen-integrationTest/src/main/java/io/toolisticon/fluapigen/integrationtest/ValidatorExample.java b/fluapigen-integrationTest/src/main/java/io/toolisticon/fluapigen/integrationtest/ValidatorExample.java new file mode 100644 index 0000000..af67139 --- /dev/null +++ b/fluapigen-integrationTest/src/main/java/io/toolisticon/fluapigen/integrationtest/ValidatorExample.java @@ -0,0 +1,47 @@ +package io.toolisticon.fluapigen.integrationtest; + +import io.toolisticon.fluapigen.api.FluentApi; +import io.toolisticon.fluapigen.api.FluentApiBackingBean; +import io.toolisticon.fluapigen.api.FluentApiBackingBeanField; +import io.toolisticon.fluapigen.api.FluentApiBackingBeanMapping; +import io.toolisticon.fluapigen.api.FluentApiCommand; +import io.toolisticon.fluapigen.api.FluentApiInterface; +import io.toolisticon.fluapigen.api.FluentApiRoot; +import io.toolisticon.fluapigen.validation.api.Matches; +import io.toolisticon.fluapigen.validation.api.MaxLength; +import io.toolisticon.fluapigen.validation.api.NotNull; + +@FluentApi("ValidatorExampleStarter") +public class ValidatorExample { + + // Backing Bean Interface + @FluentApiBackingBean + interface MyBackingBean { + + @FluentApiBackingBeanField("name") + String getName(); + + } + + // Fluent Api interfaces + @FluentApiInterface(MyBackingBean.class) + @FluentApiRoot + public interface MyRootInterface { + + MyRootInterface setName(@NotNull @MaxLength(8) @Matches("aaa.*") @FluentApiBackingBeanMapping("name") String name); + + @FluentApiCommand(MyCommand.class) + void myCommand(); + + } + + // Commands + @FluentApiCommand + static class MyCommand { + static void myCommand(MyBackingBean backingBean) { + System.out.println(backingBean.getName()); + } + } + + +} \ No newline at end of file diff --git a/fluapigen-integrationTest/src/test/java/io/toolisticon/fluapigen/integrationtest/ValidatorExampleTest.java b/fluapigen-integrationTest/src/test/java/io/toolisticon/fluapigen/integrationtest/ValidatorExampleTest.java new file mode 100644 index 0000000..9c4dd9e --- /dev/null +++ b/fluapigen-integrationTest/src/test/java/io/toolisticon/fluapigen/integrationtest/ValidatorExampleTest.java @@ -0,0 +1,21 @@ +package io.toolisticon.fluapigen.integrationtest; + +import org.junit.Test; + +public class ValidatorExampleTest { + + @Test(expected = RuntimeException.class) + public void testValidator_failingValidation() { + + ValidatorExampleStarter.setName("bbbadadadsd").myCommand(); + + } + + @Test() + public void testValidator() { + + ValidatorExampleStarter.setName("aaaBDSXS").myCommand(); + + } + +} diff --git a/fluapigen-processor/pom.xml b/fluapigen-processor/pom.xml index 55a651f..e02a1f8 100644 --- a/fluapigen-processor/pom.xml +++ b/fluapigen-processor/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.fluapigen fluapigen - 0.7.0 + 0.8.0 fluapigen-processor diff --git a/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelInterfaceMethodParameter.java b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelInterfaceMethodParameter.java index f540d35..b0af018 100644 --- a/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelInterfaceMethodParameter.java +++ b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelInterfaceMethodParameter.java @@ -7,6 +7,7 @@ import io.toolisticon.fluapigen.api.FluentApiBackingBeanMapping; import io.toolisticon.fluapigen.api.FluentApiConverter; import io.toolisticon.fluapigen.api.TargetBackingBean; +import io.toolisticon.fluapigen.validation.api.FluentApiValidator; import java.util.List; import java.util.Optional; @@ -44,7 +45,7 @@ public Optional getBackingBeanField() { switch (fluentApiBackingBeanMapping.target()) { case THIS: { - returnValue = backingBeanModel.getFieldById(fluentApiBackingBeanMapping.value()); + returnValue = backingBeanModel.getFieldById(fluentApiBackingBeanMapping.value()); break; } case NEXT: { @@ -66,10 +67,18 @@ public FluentApiBackingBeanMappingWrapper getFluentApiBackingBeanMapping() { return fluentApiBackingBeanMapping; } - public boolean hasConverter () { + public boolean hasConverter() { return !fluentApiBackingBeanMapping.converterIsDefaultValue(); - //&& !getFluentApiBackingBeanMapping().converterAsFqn().equals(FluentApiConverter.NoConversion.class.getCanonicalName() + //&& !getFluentApiBackingBeanMapping().converterAsFqn().equals(FluentApiConverter.NoConversion.class.getCanonicalName() + } + + + public boolean hasValidators() { + return getValidators().size() > 0; + } + public List getValidators() { + return this.parameterElement.getAnnotations().stream().filter(e -> e.asElement().hasAnnotation(FluentApiValidator.class)).map(ModelValidator::new).collect(Collectors.toList()); } public String getParameterName() { @@ -77,7 +86,7 @@ public String getParameterName() { } String addConverterIfNeeded(String parameterName) { - return !hasConverter() ? parameterName : "new " + getFluentApiBackingBeanMapping().converterAsTypeMirrorWrapper().getQualifiedName() + "().convert(" + parameterName +")"; + return !hasConverter() ? parameterName : "new " + getFluentApiBackingBeanMapping().converterAsTypeMirrorWrapper().getQualifiedName() + "().convert(" + parameterName + ")"; } @@ -170,7 +179,7 @@ else if (backBeanField.get().isArray()) { } // just parameter assignment - return " = " + addConverterIfNeeded(getParameterName()) +";"; + return " = " + addConverterIfNeeded(getParameterName()) + ";"; } } @@ -188,7 +197,7 @@ private void validateConverter(ModelBackingBeanField backBeanField) { if (!sourceType.isAssignableTo(converterTypeArguments.get(0)) || !converterTypeArguments.get(1).isAssignableTo(targetType)) { - throw new IncompatibleConverterException(fluentApiBackingBeanMapping,sourceType,targetType,converter,converterTypeArguments); + throw new IncompatibleConverterException(fluentApiBackingBeanMapping, sourceType, targetType, converter, converterTypeArguments); } } diff --git a/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelValidator.java b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelValidator.java new file mode 100644 index 0000000..f518faf --- /dev/null +++ b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/ModelValidator.java @@ -0,0 +1,99 @@ +package io.toolisticon.fluapigen.processor; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.TypeMirrorWrapper; +import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; +import io.toolisticon.aptk.tools.wrapper.AnnotationMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.AnnotationValueWrapper; +import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; + +import javax.lang.model.element.ExecutableElement; +import java.util.List; + +public class ModelValidator { + + + private final AnnotationMirrorWrapper validatorAnnotation; + + public ModelValidator(AnnotationMirrorWrapper validatorAnnotation) { + this.validatorAnnotation = validatorAnnotation; + } + + FluentApiValidatorWrapper getValidatorAnnotation() { + return FluentApiValidatorWrapper.wrap(this.validatorAnnotation.asElement().unwrap()); + } + + // TODO NOT USED YET, MUST BE IMPLEMENTED + public boolean validate() { + // must check if parameter types are assignable + FluentApiValidatorWrapper validatorMetaAnnotation = getValidatorAnnotation(); + TypeMirrorWrapper validatorImplType = validatorMetaAnnotation.valueAsTypeMirrorWrapper(); + String[] parameterNames = validatorMetaAnnotation.parameterNames(); + + if (parameterNames.length > 0) { + + } else if (!validatorImplType.getTypeElement().isPresent()) { + // couldn't get type element + } else { + // must have a noarg constructor or just the default + TypeElementWrapper validatorImplTypeElement = validatorImplType.getTypeElement().get(); + boolean hasNoargConstructor = validatorImplTypeElement.filterEnclosedElements().applyFilter(AptkCoreMatchers.IS_CONSTRUCTOR).applyFilter(AptkCoreMatchers.HAS_NO_PARAMETERS).getResult().size() == 1; + boolean hasJustDefaultConstructor = validatorImplTypeElement.filterEnclosedElements().applyFilter(AptkCoreMatchers.IS_CONSTRUCTOR).hasSize(0); + + if (! ( hasNoargConstructor || hasJustDefaultConstructor)) { + //ToolingProvider.getTooling().getMessager(). + //this.validatorAnnotation. + } + } + + return true; + } + + + public String validatorExpression() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("new ").append(getValidatorAnnotation().valueAsFqn()).append("("); + + boolean isFirst = true; + for (String parameterName : getValidatorAnnotation().parameterNames()) { + + // add separator + if (!isFirst) { + stringBuilder.append(", "); + } else { + isFirst = false; + } + + AnnotationValueWrapper attribute = validatorAnnotation.getAttributeWithDefault(parameterName); + + if (attribute.isString()) { + stringBuilder.append("\"").append(attribute.getStringValue()).append("\""); + } else if (attribute.isClass()) { + stringBuilder.append(attribute.getClassValue().getQualifiedName()).append(".class"); + } else if (attribute.isInteger()) { + stringBuilder.append(attribute.getIntegerValue()); + } else if (attribute.isLong()) { + stringBuilder.append(attribute.getLongValue()).append("L"); + } else if (attribute.isBoolean()) { + stringBuilder.append(attribute.getBooleanValue()); + } else if (attribute.isFloat()) { + stringBuilder.append(attribute.getFloatValue()).append("f"); + } else if (attribute.isDouble()) { + stringBuilder.append(attribute.getFloatValue()); + } else if (attribute.isEnum()) { + stringBuilder.append(TypeElementWrapper.toTypeElement(attribute.getEnumValue().getEnclosingElement().get()).getQualifiedName()); + stringBuilder.append("."); + stringBuilder.append(attribute.getEnumValue().getSimpleName()); + } + + } + + stringBuilder.append(")"); + return stringBuilder.toString(); + } + + public String getValidatorSummary() { + return validatorAnnotation.getStringRepresentation(); + } + +} diff --git a/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/package-info.java b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/package-info.java index 19c4162..e3dd254 100644 --- a/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/package-info.java +++ b/fluapigen-processor/src/main/java/io/toolisticon/fluapigen/processor/package-info.java @@ -11,7 +11,8 @@ FluentApiImplicitValue.class, FluentApiBackingBeanMapping.class, FluentApiParentBackingBeanMapping.class, - FluentApiInlineBackingBeanMapping.class + FluentApiInlineBackingBeanMapping.class, + FluentApiValidator.class }) package io.toolisticon.fluapigen.processor; @@ -24,4 +25,5 @@ import io.toolisticon.fluapigen.api.FluentApiImplicitValue; import io.toolisticon.fluapigen.api.FluentApiInlineBackingBeanMapping; import io.toolisticon.fluapigen.api.FluentApiInterface; -import io.toolisticon.fluapigen.api.FluentApiParentBackingBeanMapping; \ No newline at end of file +import io.toolisticon.fluapigen.api.FluentApiParentBackingBeanMapping; +import io.toolisticon.fluapigen.validation.api.FluentApiValidator; \ No newline at end of file diff --git a/fluapigen-processor/src/main/resources/FluentApi.tpl b/fluapigen-processor/src/main/resources/FluentApi.tpl index 5ee2479..661e8d7 100644 --- a/fluapigen-processor/src/main/resources/FluentApi.tpl +++ b/fluapigen-processor/src/main/resources/FluentApi.tpl @@ -11,6 +11,8 @@ import java.util.Deque; import java.util.HashSet; import java.util.stream.Collectors; +import io.toolisticon.fluapigen.validation.api.ValidatorException; + /** * An empty class. */ @@ -78,7 +80,15 @@ public class ${ model.className } { !{for method : interface.methods} @Override public ${method.methodSignature} { - +!{for parameter : method.allParameters} +!{if parameter.hasValidators} +!{for validator : parameter.validators} + if(!${validator.validatorExpression}.validate(${parameter.parameterName})) { + throw new ValidatorException("Parameter '${parameter.parameterName}' of method '${method.executableElement.simpleName}' failed validation: ${validator.validatorSummary}"); + } +!{/for} +!{/if} +!{/for} !{if method.hasInlineBackingBeanMapping} ${method.inlineBackingBean.className} inlineBackingBean = new ${method.inlineBackingBean.className}(); !{for parameter : method.parametersBoundToInlineBB} diff --git a/fluapigen-processor/src/test/java/io/toolisticon/fluapigen/processor/ValidatorTest.java b/fluapigen-processor/src/test/java/io/toolisticon/fluapigen/processor/ValidatorTest.java new file mode 100644 index 0000000..71a37ea --- /dev/null +++ b/fluapigen-processor/src/test/java/io/toolisticon/fluapigen/processor/ValidatorTest.java @@ -0,0 +1,63 @@ +package io.toolisticon.fluapigen.processor; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.aptk.tools.wrapper.VariableElementWrapper; +import io.toolisticon.cute.CompileTestBuilder; +import io.toolisticon.cute.PassIn; +import io.toolisticon.cute.UnitTest; +import io.toolisticon.fluapigen.validation.api.Matches; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import javax.lang.model.element.VariableElement; + +public class ValidatorTest { + + CompileTestBuilder.UnitTestBuilder unitTestBuilder; + CompileTestBuilder.CompilationTestBuilder compilationTestBuilder; + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + + unitTestBuilder = CompileTestBuilder + .unitTest(); + + compilationTestBuilder = CompileTestBuilder.compilationTest().addProcessors(FluentApiProcessor.class); + } + + + interface TestInterfaceWithMatchesValidator { + TestInterfaceWithMatchesValidator doSomething(@PassIn @Matches("aaa.*") String parameter); + } + + @Test + public void testMatchesValidator() { + unitTestBuilder.defineTestWithPassedInElement(TestInterfaceWithMatchesValidator.class, (UnitTest) (processingEnvironment, element) -> { + + try { + ToolingProvider.setTooling(processingEnvironment); + + ModelValidator unit = new ModelValidator(VariableElementWrapper.wrap(element).getAnnotationMirror(Matches.class).get()); + MatcherAssert.assertThat(unit.validatorExpression(), Matchers.is("new io.toolisticon.fluapigen.validation.api.Matches.ValidatorImpl(\"aaa.*\")")); + + } finally { + ToolingProvider.clearTooling(); + } + + }).compilationShouldSucceed() + .executeTest(); + } + + @Test + public void compilationTestValidator() { + compilationTestBuilder.addSources("/testcases/TestcaseValidator.java") + .compilationShouldSucceed() + .executeTest(); + } + + +} diff --git a/fluapigen-processor/src/test/resources/testcases/TestcaseValidator.java b/fluapigen-processor/src/test/resources/testcases/TestcaseValidator.java new file mode 100644 index 0000000..b7fccce --- /dev/null +++ b/fluapigen-processor/src/test/resources/testcases/TestcaseValidator.java @@ -0,0 +1,47 @@ +package io.toolisticon.fluapigen.processor.tests; + +import io.toolisticon.fluapigen.api.FluentApi; +import io.toolisticon.fluapigen.api.FluentApiBackingBean; +import io.toolisticon.fluapigen.api.FluentApiBackingBeanField; +import io.toolisticon.fluapigen.api.FluentApiBackingBeanMapping; +import io.toolisticon.fluapigen.api.FluentApiCommand; +import io.toolisticon.fluapigen.api.FluentApiInterface; +import io.toolisticon.fluapigen.api.FluentApiRoot; +import io.toolisticon.fluapigen.validation.api.Matches; + +@FluentApi("Xyz") +public class TestcaseValidator { + + // Backing Bean Interface + @FluentApiBackingBean + interface MyBackingBean { + + @FluentApiBackingBeanField("name") + String getName(); + + } + + // Fluent Api interfaces + @FluentApiInterface(MyBackingBean.class) + @FluentApiRoot + public interface MyRootInterface { + + MyRootInterface setName(@Matches("aaa.*") @FluentApiBackingBeanMapping("name") String name); + + @FluentApiCommand(MyCommand.class) + void myCommand(); + + } + + // Commands + @FluentApiCommand + static class MyCommand{ + static void myCommand (MyBackingBean backingBean) { + System.out.println(backingBean.getName()); + } + } + + + + +} \ No newline at end of file diff --git a/fluapigen-validation-api/pom.xml b/fluapigen-validation-api/pom.xml new file mode 100644 index 0000000..e2cb556 --- /dev/null +++ b/fluapigen-validation-api/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + fluapigen-validation-api + jar + + + io.toolisticon.fluapigen + fluapigen + 0.8.0 + + + fluapigen-validation-api + + + + + + + + maven-compiler-plugin + + + + + + + + diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/FluentApiValidator.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/FluentApiValidator.java new file mode 100644 index 0000000..db49a58 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/FluentApiValidator.java @@ -0,0 +1,27 @@ +package io.toolisticon.fluapigen.validation.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation to declare validators via annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface FluentApiValidator { + + /** + * The validator to be applied. + * @return + */ + Class> value(); + + /** + * Used to map parameters to validator constructor. + * @return the parameter names + */ + String[] parameterNames () default {}; + +} diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Matches.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Matches.java new file mode 100644 index 0000000..1a83227 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Matches.java @@ -0,0 +1,26 @@ +package io.toolisticon.fluapigen.validation.api; + + +import java.util.regex.Pattern; + +@FluentApiValidator(value = Matches.ValidatorImpl.class, parameterNames = {"value"}) +public @interface Matches { + + String value(); + + class ValidatorImpl implements Validator { + + private final String regularExpression; + + public ValidatorImpl(String regularExpression) { + this.regularExpression = regularExpression; + } + + @Override + public boolean validate(String obj) { + return Pattern.compile(regularExpression).matcher(obj).matches(); + } + + } + +} diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MaxLength.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MaxLength.java new file mode 100644 index 0000000..cc0eb18 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MaxLength.java @@ -0,0 +1,36 @@ +package io.toolisticon.fluapigen.validation.api; + +import java.lang.reflect.Array; +import java.util.Collection; + + +@FluentApiValidator(value = MaxLength.ValidatorImpl.class, parameterNames = {"value"}) +public @interface MaxLength { + + int value(); + + class ValidatorImpl implements Validator { + + private final int maxLength; + + public ValidatorImpl(int maxLength) { + this.maxLength = maxLength; + } + + @Override + public boolean validate(Object obj) { + + if (obj.getClass().isArray()) { + return Array.getLength(obj) <= maxLength; + } else if (obj instanceof Collection) { + return ((Collection) obj).size() <= maxLength; + } else if (obj instanceof String) { + return ((String) obj).length() <= maxLength; + } + return obj != null; + } + } + +} + + diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MinLength.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MinLength.java new file mode 100644 index 0000000..f5bdbd6 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/MinLength.java @@ -0,0 +1,36 @@ +package io.toolisticon.fluapigen.validation.api; + +import java.lang.reflect.Array; +import java.util.Collection; + + +@FluentApiValidator(value = MinLength.ValidatorImpl.class, parameterNames = {"value"}) +public @interface MinLength { + + int value(); + + class ValidatorImpl implements Validator { + + private final int minLength; + + public ValidatorImpl(int minLength) { + this.minLength = minLength; + } + + @Override + public boolean validate(Object obj) { + + if (obj.getClass().isArray()) { + return Array.getLength(obj) >= minLength; + } else if (obj instanceof Collection) { + return ((Collection) obj).size() >= minLength; + } else if (obj instanceof String) { + return ((String) obj).length() >= minLength; + } + return obj != null; + } + } + +} + + diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotEmpty.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotEmpty.java new file mode 100644 index 0000000..909031a --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotEmpty.java @@ -0,0 +1,24 @@ +package io.toolisticon.fluapigen.validation.api; + +import java.lang.reflect.Array; +import java.util.Collection; + +@FluentApiValidator(NotEmpty.ValidatorImpl.class) +public @interface NotEmpty { + + class ValidatorImpl implements Validator { + @Override + public boolean validate(Object obj) { + + if (obj.getClass().isArray()) { + return Array.getLength(obj) > 0; + } else if (obj instanceof Collection) { + return !((Collection) obj).isEmpty(); + } else if (obj instanceof String) { + return !((String) obj).isEmpty(); + } + return obj != null; + } + } + +} diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotNull.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotNull.java new file mode 100644 index 0000000..8434af6 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/NotNull.java @@ -0,0 +1,13 @@ +package io.toolisticon.fluapigen.validation.api; + +@FluentApiValidator(NotNull.ValidatorImpl.class) +public @interface NotNull { + + class ValidatorImpl implements Validator { + @Override + public boolean validate(Object obj) { + return obj != null; + } + } + +} diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Validator.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Validator.java new file mode 100644 index 0000000..c55c890 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/Validator.java @@ -0,0 +1,24 @@ +package io.toolisticon.fluapigen.validation.api; + +/** + * The Validator interface. + * + * @param The type which can be validated + */ +public interface Validator { + /** + * Validates passed in instance + * + * @param obj the object to validate + * @return true if obj is valid otherwise false + */ + boolean validate(TYPE obj); + + class ValidatorException extends RuntimeException { + + ValidatorException(String message) { + super(message); + } + + } +} diff --git a/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/ValidatorException.java b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/ValidatorException.java new file mode 100644 index 0000000..429f690 --- /dev/null +++ b/fluapigen-validation-api/src/main/java/io/toolisticon/fluapigen/validation/api/ValidatorException.java @@ -0,0 +1,9 @@ +package io.toolisticon.fluapigen.validation.api; + +public class ValidatorException extends RuntimeException{ + + public ValidatorException(String message) { + super(message); + } + +} diff --git a/pom.xml b/pom.xml index d1e76a3..c69e883 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.toolisticon.fluapigen fluapigen - 0.7.0 + 0.8.0 pom fluapigen @@ -73,7 +73,7 @@ 0.12.1 0.11.0 - 0.22.5 + 0.22.7 4.13.2 @@ -619,6 +619,14 @@ + + + io.toolisticon.fluapigen + fluapigen-validation-api + ${project.version} + compile + + io.toolisticon.fluapigen fluapigen-api @@ -682,6 +690,7 @@ + fluapigen-validation-api fluapigen-api fluapigen-processor fluapigen-example