Skip to content

Commit

Permalink
compiler: WIP call and event handlers in proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
lundibundi committed May 27, 2017
1 parent 74abf59 commit 29e3ffc
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ public HandlerAnnotatedInterface(Class<?> annotation, TypeElement typeElement,
handlersName = "handlers";
} else {
try {
annotatedInterface.getAnnotation(Handler.class).value();
final Handler handlerAnnotation = annotatedInterface.getAnnotation(Handler.class);
if (handlerAnnotation != null) {
handlerAnnotation.value();
}
} catch (MirroredTypeException e) {
// intended ...
jstpHandlerClass = e.getTypeMirror();
Expand All @@ -114,7 +117,7 @@ public HandlerAnnotatedInterface(Class<?> annotation, TypeElement typeElement,
public void generateCode(Filer filer) throws
ExceptionHandlerInvokeException,
ClassCastException,
IOException, PropertyFormatException {
IOException {
String implementationClassName = PREFIX + annotatedInterface.getSimpleName();

if (handlersName == null) {
Expand Down Expand Up @@ -199,58 +202,64 @@ public void generateCode(Filer filer) throws
javaFile.writeTo(filer);
}

private MethodSpec createInvokeMethod(ExecutableElement method, String handlersName)
throws ClassCastException, PropertyFormatException {
final String name = HANDLER_METHOD + capitalize(method.getSimpleName().toString());
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name)
.addModifiers(Modifier.PRIVATE)
.addParameter(JSTP_VALUE_TYPENAME, MESSAGE_PARAMETER_NAME);

String payloadName = MESSAGE_PARAMETER_NAME;

TypeMirror payloadType = getClassFromTyped(method, true);
CodeBlock payloadData = PropertyGetterUtils
.composeGetterFromAnnotations(MESSAGE_PARAMETER_NAME, method);
if (payloadData == null && method.getAnnotation(NoDefaultGet.class) == null) {
// no method annotation and no explicit denial of default getter
// so by default payload is second argument
payloadData = PropertyGetterUtils.composeCustomGetter(MESSAGE_PARAMETER_NAME, "{1}");
}
MethodSpec createInvokeMethod(ExecutableElement method, String handlersName)
throws ClassCastException {
try {
final String name = HANDLER_METHOD + capitalize(method.getSimpleName().toString());
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name)
.addModifiers(Modifier.PRIVATE)
.addParameter(JSTP_VALUE_TYPENAME, MESSAGE_PARAMETER_NAME);

if (payloadData != null) {
payloadName = "internalPayload";
methodBuilder.addStatement(VARIABLE_DEFINITION, payloadType, payloadName, payloadData);
}
String payloadName = MESSAGE_PARAMETER_NAME;

final List<? extends VariableElement> parameters = method.getParameters();
TypeMirror payloadType = getClassFromTyped(method, true);
CodeBlock payloadData = null;
payloadData = PropertyGetterUtils
.composeGetterFromAnnotations(MESSAGE_PARAMETER_NAME, method);
if (payloadData == null && method.getAnnotation(NoDefaultGet.class) == null) {
// no method annotation and no explicit denial of default getter
// so by default payload is second argument
payloadData = PropertyGetterUtils.composeCustomGetter(MESSAGE_PARAMETER_NAME, "{1}");
}

if (parameters.size() == 1) {
VariableElement parameter = parameters.get(0);
payloadType = parameter.asType();
}
if (payloadData != null) {
payloadName = "internalPayload";
methodBuilder.addStatement(VARIABLE_DEFINITION, payloadType, payloadName, payloadData);
}

composeArgumentGetters(method, methodBuilder, payloadType, payloadName);
final List<? extends VariableElement> parameters = method.getParameters();

Class notNullAnnotation = method.getAnnotation(NotNull.class) != null ? null : NotNull.class;
final String controlFlow = composeCondition(" || ", " == null", parameters, notNullAnnotation);
if (controlFlow != null) {
methodBuilder.beginControlFlow(controlFlow)
.addStatement("return")
.endControlFlow();
}
if (parameters.size() == 1) {
VariableElement parameter = parameters.get(0);
payloadType = parameter.asType();
}

String methodCall = composeMethodCall(method.getSimpleName().toString(), parameters);
composeArgumentGetters(method, methodBuilder, payloadType, payloadName);

if (handlersName != null) {
final String handlersForLoop = String.format("for ($T h : %s)", handlersName);
methodBuilder.beginControlFlow(handlersForLoop, interfaceTypeName)
.addStatement("h.$L", methodCall)
.endControlFlow();
} else {
methodBuilder.addStatement(methodCall);
}
Class notNullAnnotation = method.getAnnotation(NotNull.class) != null ? null : NotNull.class;
final String controlFlow = composeCondition(" || ", " == null", parameters,
notNullAnnotation);
if (controlFlow != null) {
methodBuilder.beginControlFlow(controlFlow)
.addStatement("return")
.endControlFlow();
}

return methodBuilder.build();
String methodCall = composeMethodCall(method.getSimpleName().toString(), parameters);

if (handlersName != null) {
final String handlersForLoop = String.format("for ($T h : %s)", handlersName);
methodBuilder.beginControlFlow(handlersForLoop, interfaceTypeName)
.addStatement("h.$L", methodCall)
.endControlFlow();
} else {
methodBuilder.addStatement(methodCall);
}

return methodBuilder.build();
} catch (PropertyFormatException e) {
throw new RuntimeException("Cannot create handler for " + method.getSimpleName(), e);
}
}

private String capitalize(String name) {
Expand Down Expand Up @@ -380,8 +389,8 @@ private String composeMethodCall(String methodName, List<? extends VariableEleme
return builder.toString();
}

private void composeCatchClauses(MethodSpec.Builder builder,
List<ExecutableElement> errorHandlers, String handlersName)
void composeCatchClauses(MethodSpec.Builder builder,
List<ExecutableElement> errorHandlers, String handlersName)
throws ExceptionHandlerInvokeException {
Map<TypeMirror, List<ExecutableElement>> exceptionHandlers = new TreeMap<>(
new Comparator<TypeMirror>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.metarhia.jstp.compiler;

import com.metarhia.jstp.compiler.annotations.handlers.ErrorHandler;
import com.metarhia.jstp.compiler.annotations.handlers.Handler;
import com.metarhia.jstp.compiler.annotations.proxy.Call;
import com.metarhia.jstp.compiler.annotations.proxy.CallHandler;
import com.metarhia.jstp.compiler.annotations.proxy.Event;
import com.metarhia.jstp.compiler.annotations.proxy.EventHandler;
import com.metarhia.jstp.compiler.annotations.proxy.Proxy;
import com.metarhia.jstp.connection.Connection;
import com.metarhia.jstp.core.Handlers.ManualHandler;
import com.metarhia.jstp.core.JSInterfaces.JSObject;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
Expand All @@ -19,6 +24,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
Expand Down Expand Up @@ -61,18 +67,26 @@ public class ProxyAnnotatedInterface {
private String remoteInterfaceName;
private boolean singletonClass;

private List<ExecutableElement> errorHandlers;

private TypeSpec.Builder classBuilder;
private Builder mainConstructorBuilder;
private TypeUtils typeUtils;
private TypeName interfaceClassName;
private TypeName interfaceTypeName;

private HandlerAnnotatedInterface handlerAnnotatedProcessor;

public ProxyAnnotatedInterface(TypeElement typeElement,
Elements elements, Types types,
Messager messager) {
this.messager = messager;
annotatedInterface = typeElement;
typeUtils = new TypeUtils(types, elements);

handlerAnnotatedProcessor = new HandlerAnnotatedInterface(
Handler.class, typeElement, elements, types);

interfaceClassName = ClassName.get(annotatedInterface.asType());
interfaceTypeName = TypeName.get(annotatedInterface.asType());

Expand All @@ -95,7 +109,7 @@ public void generateCode(Filer filer) throws

classBuilder.addField(CONNECTION_PARAMETER_TYPE, CONNECTION_FIELD_NAME, Modifier.PRIVATE);

final Builder mainConstructorBuilder = MethodSpec.constructorBuilder()
mainConstructorBuilder = MethodSpec.constructorBuilder()
.addParameter(CONNECTION_PARAMETER_CLASSNAME, CONNECTION_FIELD_NAME)
.addStatement(VARIABLE_ASSIGNMENT, "this." + CONNECTION_FIELD_NAME,
CONNECTION_FIELD_NAME);
Expand All @@ -121,19 +135,32 @@ public void generateCode(Filer filer) throws
mainConstructorBuilder.addModifiers(Modifier.PUBLIC);
}

classBuilder.addMethod(mainConstructorBuilder.build());
// add all error handlers to generate clauses in listeners
for (Element e : annotatedInterface.getEnclosedElements()) {
if (e.getKind() == ElementKind.METHOD) {
ExecutableElement method = (ExecutableElement) e;
if (method.getAnnotation(ErrorHandler.class) != null) {
errorHandlers.add(method);
}
}
}

boolean makeAbstract = false;
// generate methods to enclose the ones in the interface
for (Element e : annotatedInterface.getEnclosedElements()) {
if (e.getKind() == ElementKind.METHOD) {
ExecutableElement method = (ExecutableElement) e;
MethodSpec actionMethod = createActionHandler(method);
if (actionMethod != null) {
classBuilder.addMethod(actionMethod);
if (method.getAnnotation(EventHandler.class) != null) {
makeAbstract = true;
}
addActionHandler(method);
}
}

if (makeAbstract) {
classBuilder.addModifiers(Modifier.ABSTRACT);
}

addDelegateMethod(CONNECTION_FIELD_NAME, Connection.class, "setCallHandler",
Arrays.asList("interfaceName", "methodName", "handler"),
String.class, String.class, ManualHandler.class);
Expand All @@ -142,6 +169,8 @@ public void generateCode(Filer filer) throws
Arrays.asList("interfaceName", "eventName", "handler"),
String.class, String.class, ManualHandler.class);

classBuilder.addMethod(mainConstructorBuilder.build());

// save to file
JavaFile javaFile = JavaFile.builder(
typeUtils.getElements()
Expand Down Expand Up @@ -180,15 +209,50 @@ private MethodSpec createDelegateMethod(String caller, Method method, List<Strin
return delegateBuilder.build();
}

private MethodSpec createActionHandler(ExecutableElement method) {
private void addActionHandler(ExecutableElement method) throws PropertyFormatException {
if (method.getAnnotation(Call.class) != null) {
String[] names = getInterfaceAndMethod(method, remoteInterfaceName, Call.class, "value");
return createCallMethod(names[0], names[1], method);
final MethodSpec callMethod = createCallMethod(names[0], names[1], method);
classBuilder.addMethod(callMethod);
} else if (method.getAnnotation(Event.class) != null) {
String[] names = getInterfaceAndMethod(method, remoteInterfaceName, Event.class, "value");
return createEventMethod(names[0], names[1], method);
final MethodSpec eventMethod = createEventMethod(names[0], names[1], method);
classBuilder.addMethod(eventMethod);
} else if (method.getAnnotation(EventHandler.class) != null
|| method.getAnnotation(CallHandler.class) != null) {
Class<? extends Annotation> annotation = EventHandler.class;
String[] names;
String methodName = "addEventHandler";
if (method.getAnnotation(CallHandler.class) != null) {
annotation = CallHandler.class;
methodName = "setCallHandler";
}
names = getInterfaceAndMethod(method, remoteInterfaceName, annotation, "value");
// add helper method
final MethodSpec handlerMethod = createHandlerMethod(method);
classBuilder.addMethod(handlerMethod);
// add handler to constructor
final TypeSpec manualHandler = createManualHandler(null, handlerMethod.name);
mainConstructorBuilder.addStatement("$L.$L(\"$L\", \"$L\", $L)",
CONNECTION_FIELD_NAME, methodName, names[0], names[1], manualHandler);
}
return null;
}

private TypeSpec createManualHandler(String caller, String methodName) {
final MethodSpec.Builder builder = MethodSpec.methodBuilder("handle")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(JSObject.class, "message");

final CodeBlock methodCall = composeMethodCall(caller, methodName,
Collections.singletonList("message"));

builder.addStatement("$1L", methodCall);

return TypeSpec.anonymousClassBuilder("")
.addSuperinterface(ManualHandler.class)
.addMethod(builder.build())
.build();
}

private MethodSpec createCallMethod(String remoteInterface, String remoteMethod,
Expand Down Expand Up @@ -233,6 +297,11 @@ private MethodSpec createEventMethod(String remoteInterface, String eventName,
return eventMethodBuilder.build();
}

private MethodSpec createHandlerMethod(ExecutableElement method)
throws PropertyFormatException {
return handlerAnnotatedProcessor.createInvokeMethod(method, null);
}

private CodeBlock composeParamMethodCall(String methodName,
List<? extends VariableElement> parameters) {
return composeParamMethodCall(null, methodName, parameters);
Expand All @@ -247,7 +316,7 @@ private CodeBlock composeParamMethodCall(String name, String methodName,
return composeMethodCall(name, methodName, names);
}

private CodeBlock composeMethodCall(String name, String methodName,
private CodeBlock composeMethodCall(String caller, String methodName,
Collection<String> parameters) {
StringBuilder builder = new StringBuilder();
int i = 0;
Expand All @@ -258,16 +327,16 @@ private CodeBlock composeMethodCall(String name, String methodName,
}
}

if (name == null) {
if (caller == null) {
return CodeBlock.of(METHOD_CALL_PATTERN, methodName, builder.toString());
} else if (name.contains(".")) {
int lastDot = name.lastIndexOf(".");
String packageName = name.substring(0, lastDot);
String className = name.substring(lastDot + 1);
} else if (caller.contains(".")) {
int lastDot = caller.lastIndexOf(".");
String packageName = caller.substring(0, lastDot);
String className = caller.substring(lastDot + 1);
final ClassName clazz = ClassName.get(packageName, className);
return CodeBlock.of(METHOD_STATIC_CALL_PATTERN, clazz, methodName, builder.toString());
} else {
return CodeBlock.of(METHOD_FIELD_CALL_PATTERN, name, methodName, builder.toString());
return CodeBlock.of(METHOD_FIELD_CALL_PATTERN, caller, methodName, builder.toString());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.metarhia.jstp.compiler.annotations.proxy;

/**
* Created by lundibundi on 5/27/17.
*/
public @interface CallHandler {

String[] value() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.metarhia.jstp.compiler.annotations.proxy;

/**
* Created by lundibundi on 5/27/17.
*/
public @interface EventHandler {

String[] value() default "";
}

0 comments on commit 29e3ffc

Please sign in to comment.