package com.lambdaworks.apigenerator; import java.io.File; import java.util.*; import java.util.function.Function; import java.util.function.Supplier; import com.lambdaworks.redis.internal.LettuceSets; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.ReferenceType; import com.github.javaparser.ast.type.Type; /** * Create reactive API based on the templates. * * @author Mark Paluch */ @RunWith(Parameterized.class) public class CreateReactiveApi { private Set<String> KEEP_METHOD_RESULT_TYPE = LettuceSets.unmodifiableSet( "digest", "close", "isOpen", "BaseRedisCommands.reset", "getStatefulConnection"); private CompilationUnitFactory factory; @Parameterized.Parameters(name = "Create {0}") public static List<Object[]> arguments() { List<Object[]> result = new ArrayList<>(); for (String templateName : Constants.TEMPLATE_NAMES) { result.add(new Object[] { templateName }); } return result; } /** * * @param templateName */ public CreateReactiveApi(String templateName) { String targetName = templateName.replace("Commands", "ReactiveCommands"); File templateFile = new File(Constants.TEMPLATES, "com/lambdaworks/redis/api/" + templateName + ".java"); String targetPackage; if (templateName.contains("RedisSentinel")) { targetPackage = "com.lambdaworks.redis.sentinel.api.rx"; } else { targetPackage = "com.lambdaworks.redis.api.rx"; } factory = new CompilationUnitFactory(templateFile, Constants.SOURCES, targetPackage, targetName, commentMutator(), methodTypeMutator(), methodDeclaration -> true, importSupplier(), null, methodCommentMutator()); } /** * Mutate type comment. * * @return */ protected Function<String, String> commentMutator() { return s -> s.replaceAll("\\$\\{intent\\}", "Observable commands").replaceAll("@since 3.0", "@since 4.0") + "* @generated by " + getClass().getName() + "\r\n "; } protected Function<Comment, Comment> methodCommentMutator() { return comment -> { if(comment != null && comment.getContent() != null){ comment.setContent(comment.getContent().replaceAll("List<(.*)>", "$1").replaceAll("Set<(.*)>", "$1")); } return comment; }; } /** * Mutate type to async result. * * @return */ protected Function<MethodDeclaration, Type> methodTypeMutator() { return method -> { ClassOrInterfaceDeclaration classOfMethod = (ClassOrInterfaceDeclaration) method.getParentNode(); if (KEEP_METHOD_RESULT_TYPE.contains(method.getName()) || KEEP_METHOD_RESULT_TYPE.contains(classOfMethod.getName() + "." + method.getName())) { return method.getType(); } String typeAsString = method.getType().toStringWithoutComments().trim(); if (typeAsString.equals("void")) { typeAsString = "Success"; } if (typeAsString.startsWith("List<")) { typeAsString = typeAsString.substring(5, typeAsString.length() - 1); } else if (typeAsString.startsWith("Set<")) { typeAsString = typeAsString.substring(4, typeAsString.length() - 1); } return new ReferenceType(new ClassOrInterfaceType("Observable<" + typeAsString + ">")); }; } /** * Supply additional imports. * * @return */ protected Supplier<List<String>> importSupplier() { return () -> Collections.singletonList("rx.Observable"); } @Test public void createInterface() throws Exception { factory.createInterface(); } }