Skip to content

Commit

Permalink
Merge pull request #15 from fa4nir/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
fa4nir committed Apr 2, 2023
2 parents c915e46 + 4522ffc commit a0e63ae
Show file tree
Hide file tree
Showing 29 changed files with 622 additions and 54 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -24,8 +24,8 @@ to listening your notifications.
## Dependencies

```
implementation 'io.github.fa4nir:fa4nir:1.0.2'
annotationProcessor 'io.github.fa4nir:fa4nir:1.0.2'
implementation 'io.github.fa4nir:fa4nir:1.0.3'
annotationProcessor 'io.github.fa4nir:fa4nir:1.0.3'
```

# Examples
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Expand Up @@ -47,7 +47,7 @@ allprojects {
apply plugin: 'java-library'

group = 'io.github.fa4nir'
version = '1.0.3'
version = '1.0.4'

repositories {
mavenCentral()
Expand Down Expand Up @@ -119,7 +119,7 @@ publishing {

groupId = 'io.github.fa4nir'
artifactId = 'fa4nir'
version = '1.0.3'
version = '1.0.4'

artifact(sourceJar)
artifact(javadocJar)
Expand Down
Expand Up @@ -8,4 +8,5 @@
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayloadPredicate {
String marker() default "";
}
Expand Up @@ -5,7 +5,8 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnStatement {
String marker() default "";
}
@@ -0,0 +1,11 @@
package io.github.fa4nir.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringBean {
}
@@ -0,0 +1,12 @@
package io.github.fa4nir.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueMarker {
String marker();
}
Expand Up @@ -41,7 +41,9 @@ public interface OverridingMethodsDefinition {

List<? extends VariableElement> getTargetParameters();

List<ExecutableElement> getPredicateMethods();
Map<String, ExecutableElement> getPredicateMethods();

List<ExecutableElement> getListOfPredicateMethods();

String getNotifyToTarget();

Expand Down
Expand Up @@ -9,6 +9,7 @@
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.beans.Introspector;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -218,18 +219,19 @@ public OverridingMethodsDefinitionBuilder predicateMethods() {
.filter(method -> method instanceof ExecutableElement)
.filter(method -> Objects.nonNull(method.getAnnotation(PayloadPredicate.class)))
.map(method -> ((ExecutableElement) method))
.filter(this::isBoolType)
.collect(Collectors.toList());
if (this.predicateMethods.size() > 1) {
throw new IllegalArgumentException("Only one predicate method.");
} else if (this.predicateMethods.size() == 1) {
TypeMirror type = this.predicateMethods.get(0).getReturnType();
if (!TypeKind.BOOLEAN.equals(type.getKind())) {
throw new IllegalArgumentException("Method predicate should return boolean");
}
}
return this;
}

private boolean isBoolType(ExecutableElement method) {
TypeMirror type = method.getReturnType();
if (!TypeKind.BOOLEAN.equals(type.getKind())) {
throw new IllegalArgumentException("Method predicate should return boolean");
}
return true;
}

@Override
public OverridingMethodsDefinitionBuilder notifyToTarget() {
this.notifyToTarget = this.annotationNotifyTo.name();
Expand All @@ -253,9 +255,6 @@ public OverridingMethodsDefinitionBuilder sourceReturnType() {
.filter(method -> !this.annotationNotifyTo.name().equals(method.getSimpleName().toString()))
.filter(method -> Objects.nonNull(method.getAnnotation(ReturnStatement.class)))
.count();
if (count > 1) {
throw new IllegalArgumentException("Return statement should have only one exemplar.");
}
if (!this.isTargetMethodHasReturnStatement && !this.sourceMethodReturnType.getKind().equals(TypeKind.VOID)) {
if (count == 0) {
throw new IllegalArgumentException("Should exist ReturnStatement annotation to mark which result should be return from listener.");
Expand Down Expand Up @@ -366,7 +365,25 @@ public List<? extends VariableElement> getTargetParameters() {
}

@Override
public List<ExecutableElement> getPredicateMethods() {
public Map<String, ExecutableElement> getPredicateMethods() {
if (predicateMethods.size() > 1) {
return predicateMethods.stream()
.filter(method -> {
PayloadPredicate annotation = method.getAnnotation(PayloadPredicate.class);
if (StringUtils.isBlank(annotation.marker())) {
throw new IllegalArgumentException("You should use unique name inside PayloadPredicate and UniqueMarker with the same name.");
}
return true;
}).collect(Collectors.toMap(
key -> key.getAnnotation(PayloadPredicate.class).marker(),
Function.identity()
));
}
return new HashMap<>();
}

@Override
public List<ExecutableElement> getListOfPredicateMethods() {
return predicateMethods;
}

Expand All @@ -382,7 +399,7 @@ public String getFallBackMethodName() {

@Override
public boolean isPredicateMethodsSize() {
return predicateMethods.size() == 1;
return predicateMethods.size() >= 1;
}

@Override
Expand Down
Expand Up @@ -8,6 +8,6 @@

public interface AnnotationTransferFactory {

TypeSpec newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition);
TypeSpec.Builder newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition);

}
Expand Up @@ -31,7 +31,7 @@ public TransmitterAbstractClassFactory(InterceptMethodFactory overridingIntercep
}

@Override
public TypeSpec newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition) {
public TypeSpec.Builder newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition) {
TypeMirror typeMirror = element.asType();
TypeName sourceClassType = ClassName.get(typeMirror);
TypeElement source = processingEnv.getElementUtils().
Expand All @@ -42,12 +42,12 @@ public TypeSpec newTypeSpec(Element element, ProcessingEnvironment processingEnv
Map.Entry<TypeMirror, List<MethodSpec>> overrideMethods =
overrideMethods(definition.isSupper(), sourceSupperElement, source, definition.getTarget());
return TypeSpec.classBuilder(definition.getBeanName())
.addField(this.transmitterTargetFieldsFactory.newField(definition.getTargetTypeName(), definition.getTargetAsFieldName()))
.superclass(overrideMethods.getKey())
.addField(this.transmitterTargetFieldsFactory.newField(
definition.getTargetTypeName(), definition.getTargetAsFieldName())
).superclass(overrideMethods.getKey())
.addModifiers(Modifier.PUBLIC)
.addMethod(definition.getConstructor())
.addMethods(overrideMethods.getValue())
.build();
.addMethods(overrideMethods.getValue());
}

private Map.Entry<TypeMirror, List<MethodSpec>>
Expand Down
Expand Up @@ -4,6 +4,10 @@
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.github.fa4nir.core.annotations.NotifyTo;
import io.github.fa4nir.core.annotations.PayloadPredicate;
import io.github.fa4nir.core.annotations.ReturnStatement;
import io.github.fa4nir.core.annotations.UniqueMarker;
import io.github.fa4nir.core.definitions.TransmitterDefinition;
import io.github.fa4nir.core.factories.fields.FieldsFactory;
import io.github.fa4nir.core.factories.methods.InterceptMethodFactory;
Expand All @@ -17,6 +21,8 @@
import javax.lang.model.type.TypeMirror;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

public class TransmitterInterfaceFactory implements AnnotationTransferFactory {
Expand All @@ -31,20 +37,77 @@ public TransmitterInterfaceFactory(InterceptMethodFactory overridingInterceptor,
}

@Override
public TypeSpec newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition) {
public TypeSpec.Builder newTypeSpec(Element element, ProcessingEnvironment processingEnv, TransmitterDefinition definition) {
TypeMirror typeMirror = element.asType();
TypeName sourceClassType = ClassName.get(typeMirror);
TypeElement source = processingEnv.getElementUtils().
getTypeElement(sourceClassType.toString());
validation(source, definition.getTarget());
List<? extends TypeMirror> interfaces = ValidationUtils.validInterface(source.getInterfaces());
Map.Entry<TypeMirror, List<MethodSpec>> overrideMethods = overrideMethods(definition.isSupper(), interfaces, source, definition.getTarget());
Map.Entry<TypeMirror, List<MethodSpec>> overrideMethods = overrideMethods(
definition.isSupper(), interfaces, source, definition.getTarget());
return TypeSpec.classBuilder(definition.getBeanName())
.addField(this.transmitterTargetFieldsFactory.newField(definition.getTargetTypeName(), definition.getTargetAsFieldName()))
.addSuperinterface(overrideMethods.getKey())
.addModifiers(Modifier.PUBLIC)
.addMethod(definition.getConstructor())
.addMethods(overrideMethods.getValue())
.build();
.addMethods(overrideMethods.getValue());
}

private void validation(TypeElement source, Element target) {
List<? extends Element> sourceMethods = source.getEnclosedElements();
List<? extends Element> targetMethods = target.getEnclosedElements();

List<NotifyTo> notifyToAnnotations = sourceMethods.stream()
.filter(method -> method instanceof ExecutableElement)
.map(method -> ((ExecutableElement) method))
.map(method -> method.getAnnotation(NotifyTo.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());

List<UniqueMarker> uniqueMarkersAnnotations = sourceMethods.stream()
.filter(method -> method instanceof ExecutableElement)
.map(method -> ((ExecutableElement) method))
.map(method -> method.getAnnotation(UniqueMarker.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());

List<PayloadPredicate> payloadPredicateAnnotations = targetMethods.stream()
.filter(method -> method instanceof ExecutableElement)
.map(method -> ((ExecutableElement) method))
.map(method -> method.getAnnotation(PayloadPredicate.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());

List<ReturnStatement> returnStatementAnnotations = targetMethods.stream()
.filter(method -> method instanceof ExecutableElement)
.map(method -> ((ExecutableElement) method))
.map(method -> method.getAnnotation(ReturnStatement.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());

if (notifyToAnnotations.size() > 1) {
if (uniqueMarkersAnnotations.size() != payloadPredicateAnnotations.size()) {
throw new IllegalArgumentException(String.format("Transmitter: %s, Receiver: %s, Miss mapping. 2 annotation NotifyTo and 2 annotation PayloadPredicate," +
" but UniqueMarker annotation less then PayloadPredicate", source.getSimpleName().toString(), target.getSimpleName().toString()));
} else {
Map<String, UniqueMarker> groupByUniqueMarker = uniqueMarkersAnnotations.stream().collect(
Collectors.toMap(UniqueMarker::marker, Function.identity())
);
Map<String, PayloadPredicate> groupByPayloadPredicate = payloadPredicateAnnotations.stream().collect(
Collectors.toMap(PayloadPredicate::marker, Function.identity())
);
groupByUniqueMarker.forEach((name, uniqueMarker) -> {
PayloadPredicate payloadPredicate = groupByPayloadPredicate.get(name);
if (Objects.isNull(payloadPredicate)) {
throw new IllegalArgumentException("Miss match naming between UniqueMarker and PayloadPredicate");
}
});
}
}
if (returnStatementAnnotations.size() > notifyToAnnotations.size()) {
throw new IllegalArgumentException(String.format("Target: %s, Something wrong with ReturnStatement!", target.getSimpleName().toString()));
}
}

private Map.Entry<TypeMirror, List<MethodSpec>>
Expand Down
Expand Up @@ -30,7 +30,8 @@ public static VariableElement getVariableElement(List<? extends VariableElement>
if (num != -1) {
return targetParameters.get(num);
} else if (StringUtils.isNoneBlank(name)) {
return groupOfSourceParameters.get(name);
return Objects.requireNonNull(groupOfSourceParameters.get(name),
"Can't you recheck parameters name because not found parameter with this name: " + name);
}
throw new IllegalArgumentException("Cannot find parameter in annotation " + annotation.getClass());
}
Expand Down
Expand Up @@ -24,8 +24,13 @@ public MethodSpec.Builder wrap(String parametersAsString, OverridingMethodsDefin
List<CodeBlock> callsToDelegateMethods = ofDelegateDefinitions(definition);
callsToDelegateMethods.forEach(builder::addStatement);
} else {
builder.addStatement("this.$N.$N($N)",
definition.getTargetFieldName(), definition.getTargetMethod().getSimpleName().toString(), parametersAsString);
if (definition.hasAnnotationReturnStatement()) {
builder.addStatement("$N = this.$N.$N($N)", definition.sourceMethodResultName(),
definition.getTargetFieldName(), definition.getTargetMethod().getSimpleName().toString(), parametersAsString);
} else {
builder.addStatement("this.$N.$N($N)",
definition.getTargetFieldName(), definition.getTargetMethod().getSimpleName().toString(), parametersAsString);
}
}
return builder;
}
Expand Down
@@ -1,12 +1,15 @@
package io.github.fa4nir.core.wrappers;

import com.squareup.javapoet.MethodSpec;
import io.github.fa4nir.core.annotations.UniqueMarker;
import io.github.fa4nir.core.definitions.OverridingMethodsDefinition;
import org.apache.commons.lang3.StringUtils;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static io.github.fa4nir.core.utils.ParametersUtils.parametersAsString;

Expand All @@ -21,18 +24,33 @@ public PredicateMethodWrapper(OverrideMethodWrapper wrapper) {
@Override
public MethodSpec.Builder wrap(String parametersAsString, OverridingMethodsDefinition definition, MethodSpec.Builder builder) {
if (definition.getPredicateMethods().size() > 0) {
ExecutableElement predicate = definition.getPredicateMethods().get(0);
String predicateSimpleName = predicate.getSimpleName().toString();
List<? extends VariableElement> predicateParameters = predicate.getParameters();
String predicateParametersAsString = parametersAsString(definition.getSourceParameters(), definition.getGroupOfSourceParameters(), predicateParameters);
if (StringUtils.isBlank(predicateParametersAsString)) {
throw new IllegalArgumentException("Predicate mast contains any of parameters.");
UniqueMarker marker = Objects.requireNonNull(
definition.getSourceMethod().getAnnotation(UniqueMarker.class),
"Transmitter has any predicate methods, so mark source methods as UniqueMarker with unique name.");
Map<String, ExecutableElement> groupByMarker = definition.getPredicateMethods();
ExecutableElement predicate = Objects.requireNonNull(groupByMarker.get(marker.marker()),
"If you use two predicate you should mark methods UniqueMarker and set unique name.");
return predicateMethodControllFlow(parametersAsString, definition, builder, predicate);
} else if (definition.getListOfPredicateMethods().size() > 0) {
if (definition.getListOfPredicateMethods().size() > 1) {
throw new IllegalArgumentException("You should use UniqueMarker annotation");
}
builder.beginControlFlow("if(this.$N.$N($N))", definition.getTargetFieldName(), predicateSimpleName, predicateParametersAsString);
return this.wrapper.wrap(parametersAsString, definition, builder)
.endControlFlow();
ExecutableElement predicate = definition.getListOfPredicateMethods().get(0);
return predicateMethodControllFlow(parametersAsString, definition, builder, predicate);
}
return this.wrapper.wrap(parametersAsString, definition, builder);
}

private MethodSpec.Builder predicateMethodControllFlow(String parametersAsString, OverridingMethodsDefinition definition, MethodSpec.Builder builder, ExecutableElement predicate) {
String predicateSimpleName = predicate.getSimpleName().toString();
List<? extends VariableElement> predicateParameters = predicate.getParameters();
String predicateParametersAsString = parametersAsString(definition.getSourceParameters(), definition.getGroupOfSourceParameters(), predicateParameters);
if (StringUtils.isBlank(predicateParametersAsString)) {
throw new IllegalArgumentException("Predicate mast contains any of parameters.");
}
builder.beginControlFlow("if(this.$N.$N($N))", definition.getTargetFieldName(), predicateSimpleName, predicateParametersAsString);
return this.wrapper.wrap(parametersAsString, definition, builder)
.endControlFlow();
}

}
@@ -0,0 +1,11 @@
package io.github.fa4nir.examples.listeners;

import io.github.fa4nir.examples.payloads.Person;

public interface CustomListenerWithMultipleReturnStatement {

Person onSuccess(String parameter0, Double parameters2);

String notifyToIt(long id, String name, int age);

}

0 comments on commit a0e63ae

Please sign in to comment.