Skip to content

Commit

Permalink
Fixes #13: Support Java 8; support deployment to central; remove depe…
Browse files Browse the repository at this point in the history
…ndency on functional java.
  • Loading branch information
drewctaylor committed Apr 11, 2022
1 parent 5cbf08a commit 11f6f46
Show file tree
Hide file tree
Showing 7 changed files with 621 additions and 384 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/workflow-maven-deploy.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: workflow-maven-deploy

on: [push]
on: [ push ]

jobs:
build:
Expand All @@ -17,12 +17,15 @@ jobs:
server-id: ossrh
server-username: SERVER_USERNAME
server-password: SERVER_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: GPG_PASSPHRASE

- name: Step 3 - Maven Deploy.
run: mvn -B deploy -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
env:
SERVER_USERNAME: ${{ secrets.OSSRH_USERNAME }}
SERVER_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

- name: Step 4 - CodeCov.
uses: codecov/codecov-action@v1
694 changes: 422 additions & 272 deletions .idea/inspectionProfiles/Project.xml

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .mvn/jvm.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
45 changes: 35 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,69 @@
<parent>
<groupId>io.github.drewctaylor</groupId>
<artifactId>maven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.0.2</version>
</parent>

<artifactId>javapoet-maven-plugin</artifactId>
<version>1.0.5-SNAPSHOT</version>
<packaging>maven-plugin</packaging>

<name>${project.groupId}:${project.artifactId}</name>
<description>A simple maven plugin that allows you to use JavaPoet to generate sources for a project.</description>
<url>https://github.com/drewctaylor/javapoet-maven-plugin</url>
<inceptionYear>2020</inceptionYear>
<organization>
<name>Drew Taylor</name>
<url>https://github.com/drewctaylor</url>
</organization>

<licenses>
<license>
<name>MIT</name>
<url>https://opensource.org/licenses/MIT</url>
</license>
</licenses>

<developers>
<developer>
<name>Drew Taylor</name>
<email>[email protected]</email>
<organization>Drew Taylor</organization>
<organizationUrl>https://github.com/drewctaylor</organizationUrl>
</developer>
</developers>

<scm>
<connection>https://github.com/drewctaylor/javapoet-maven-plugin</connection>
<developerConnection>https://github.com/drewctaylor/javapoet-maven-plugin</developerConnection>
<url>https://github.com/drewctaylor/javapoet-maven-plugin</url>
</scm>

<dependencies>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>

<dependency>
<groupId>org.functionaljava</groupId>
<artifactId>functionaljava</artifactId>
<version>4.9</version>
</dependency>

<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.8.1</version>
<version>3.8.5</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<version>3.6.4</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.0-M1</version>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,131 +1,179 @@
package io.github.drewctaylor.maven.plugin.javapoet;

import com.squareup.javapoet.JavaFile;
import fj.F2;
import fj.F3;
import fj.P2;
import fj.Try;
import fj.TryEffect;
import fj.Unit;
import fj.data.HashMap;
import fj.data.List;
import fj.data.NonEmptyList;
import fj.data.Option;
import fj.data.Validation;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static fj.P.p;
import static fj.Semigroup.nonEmptyListSemigroup;
import static fj.data.HashMap.fromMap;
import static fj.data.List.iterableList;
import static fj.data.NonEmptyList.nel;
import static fj.data.Validation.sequence;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.empty;
import static java.util.stream.Stream.of;
import static java.util.stream.StreamSupport.stream;

/**
* Invoke the given methods with the given string parameters; write the returned JavaFile Stream to the given directory.
*/
@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public final class JavaPoetMojo extends AbstractMojo
{
private static Validation<NonEmptyList<Exception>, File> directoryFor(
final String path)
private static <T> BiFunction<Function<List<Exception>, T>, BiFunction<String, String, T>, T> classNameMethodNameFor(
final Matcher matcher,
final String classNameMethodName)
{
return Try.f(() ->
{
final File file = Paths.get(path).toFile();
final int groupForClassName = 1;
final int groupForMethodName = 4;

return matcher.matches() ?
(
fExceptionList,
fClassNameMethodName) -> fClassNameMethodName.apply(matcher.group(groupForClassName), matcher.group(groupForMethodName)) :
(
fExceptionList,
fClassNameMethodName) -> fExceptionList.apply(singletonList(new Exception(format("The plugin could not parse the method '%s'.", classNameMethodName))));
}

if (file.isFile())
{
throw new Exception(format("The path exists and references a file '%s'.", path));
}
private static <T> BiFunction<Function<List<Exception>, T>, Function<List<JavaFile>, T>, T> javaFileList(
final String className,
final String methodName,
final String parameter)
{
try
{
final List<JavaFile> javaFileList = stream(((Iterable<JavaFile>) Class.forName(className).getMethod(methodName, String.class).invoke(null, parameter)).spliterator(), false).collect(toList());
return (
fExceptionList,
fJavaFileIterable) -> fJavaFileIterable.apply(javaFileList);
}
catch (final ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException exception)
{
return (
fExceptionList,
fJavaFileList) -> fExceptionList.apply(singletonList(exception));
}
}

if (!file.exists() && !file.mkdirs())
{
throw new Exception(format("The plugin could not create the directory '%s'.", path));
}
private static <T> BiFunction<Function<List<Exception>, T>, Function<File, T>, T> directoryFor(
final String path)
{
final File file = Paths.get(path).toFile();

return file.isFile() ?
(
fExceptionList,
fFile) -> fExceptionList.apply(singletonList(new Exception(format("The path exists and references a file '%s'.", path)))) :
!file.exists() && !file.mkdirs() ?
(
fExceptionList,
fFile) -> fExceptionList.apply(singletonList(new Exception(format("The plugin could not create the directory '%s'.", path)))) :
(
fExceptionList,
fFile) -> fFile.apply(file);
}

return file;
})._1().f().map(NonEmptyList::nel);
private static <T> BiFunction<Function<List<Exception>, T>, Function<List<JavaFile>, T>, T> javaFileStreamListFor(
final Map<String, String> classNameMethodNameParameterMap)
{
final String regexForIdentifier = "[\\p{Alpha}_$][\\p{Alpha}\\p{Digit}_$]*";
final String regex = format("((%s)(\\.%s)*)\\.(%s)", regexForIdentifier, regexForIdentifier, regexForIdentifier);
final Pattern pattern = Pattern.compile(regex);

return classNameMethodNameParameterMap.keySet().stream()
.map(classNameMethodName -> JavaPoetMojo.<BiFunction<Function<List<Exception>, T>, Function<List<JavaFile>, T>, T>>classNameMethodNameFor(pattern.matcher(classNameMethodName), classNameMethodName).apply(
exceptionList -> (
fExceptionList,
fJavaFileList) -> fExceptionList.apply(exceptionList),
(
className,
methodName) -> JavaPoetMojo.<BiFunction<Function<List<Exception>, T>, Function<List<JavaFile>, T>, T>>javaFileList(className, methodName, classNameMethodNameParameterMap.get(classNameMethodName)).apply(
exceptionList -> (
fExceptionList,
fJavaFileList) -> fExceptionList.apply(exceptionList),
javaFileList -> (
fExceptionList,
fJavaFileList) -> fJavaFileList.apply(javaFileList))))
.reduce(
(
fExceptionList,
fJavaFileList) -> fJavaFileList.apply(emptyList()),
(
validation1,
validation2) -> (
fExceptionList,
fJavaFileList) -> validation1.apply(
exceptionList -> validation2.apply(
exceptionListInner -> fExceptionList.apply(concat(exceptionList.stream(), exceptionListInner.stream()).collect(toList())),
javaFileListInner -> fExceptionList.apply(exceptionList)),
javaFileList -> validation2.apply(
exceptionListInner -> fExceptionList.apply(exceptionListInner),
javaFileListInner -> fJavaFileList.apply(concat(javaFileList.stream(), javaFileListInner.stream()).collect(toList())))));
}

private static Validation<NonEmptyList<Exception>, List<JavaFile>> javaFileStreamListFor(
final HashMap<String, String> classNameMethodNameParameterMap)
private static Stream<Exception> writeTo(
final File file,
final JavaFile javaFile)
{
final var regexForIdentifier = "[\\p{Alpha}_$][\\p{Alpha}\\p{Digit}_$]*";
final var regex = format("((%s)(\\.%s)*)\\.(%s)", regexForIdentifier, regexForIdentifier, regexForIdentifier);
final var pattern = Pattern.compile(regex);
final var groupForClassName = 1;
final var groupForMethodName = 4;

final F2<Matcher, String, Validation<NonEmptyList<Exception>, P2<String, String>>> classNameMethodNameFor = (
matcher,
classNameMethodName) -> Option
.iif(matcher.matches(), () -> p(matcher.group(groupForClassName), matcher.group(groupForMethodName)))
.toValidation(nel(new Exception(format("The plugin could not parse the method '%s'.", classNameMethodName))));

@SuppressWarnings("unchecked")
final F3<String, String, String, Validation<NonEmptyList<Exception>, List<JavaFile>>> javaFileListFor = (
className,
methodName,
parameter) -> Try.f(() -> Class.forName(className).getMethod(methodName, String.class).invoke(null, parameter))._1()
.map(object -> iterableList((Iterable<JavaFile>) object))
.f().map(NonEmptyList::nel);

return sequence(nonEmptyListSemigroup(), classNameMethodNameParameterMap.toList()
.map(p -> classNameMethodNameFor.f(pattern.matcher(p._1()), p._1()).map(pInner -> pInner.append(p._2())))
.map(validation -> validation.bind(p -> javaFileListFor.f(p._1(), p._2(), p._3()))))
.map(List::join);
try
{
javaFile.writeTo(file);
return empty();
}
catch (final IOException ioException)
{
return of(ioException);
}
}

static Validation<NonEmptyList<Exception>, List<Unit>> executeHelper(
static List<Exception> executeHelper(
final String path,
final Map<String, String> classNameMethodNameParameterMap)
{
requireNonNull(path);
requireNonNull(classNameMethodNameParameterMap);

final F2<File, JavaFile, Validation<NonEmptyList<Exception>, Unit>> writeTo = (
file,
javaFile) -> TryEffect.f(() -> javaFile.writeTo(file))._1()
.f().map(NonEmptyList::nel);

return directoryFor(path).bind(file -> javaFileStreamListFor(fromMap(classNameMethodNameParameterMap)).bind(list -> sequence(nonEmptyListSemigroup(), list.map(javaFile -> writeTo.f(file, javaFile)))));
return JavaPoetMojo.<List<Exception>>directoryFor(path).apply(
identity(),
directory -> JavaPoetMojo.<List<Exception>>javaFileStreamListFor(classNameMethodNameParameterMap).apply(
identity(),
javaFileList -> javaFileList.stream().flatMap(javaFile -> writeTo(directory, javaFile)).collect(toList())));
}

/**
* A path to which to write the java file.
* A path to which to write a java file.
*/
@SuppressWarnings(
{ "unused", "InstanceVariableMayNotBeInitialized" })
@Parameter(alias = "path", defaultValue = "${project.basedir}/src/main/java/")
private String path;

/**
* A map from a fully-qualified method name to a string parameter.
*/
@SuppressWarnings(
{ "unused", "InstanceVariableMayNotBeInitialized" })
@Parameter(alias = "methods", required = true)
private Map<String, String> methods;

/**
* Invoke the given methods with the given string parameters; write the returned JavaFile Stream to the given directory.
* Invoke a method with a string parameter; write a returned JavaFile Stream to the given directory.
*/
@Override
public void execute()
{
executeHelper(path, methods)
.f().forEach(nel -> nel.forEach(exception -> getLog().error(exception)));
executeHelper(path, methods).forEach(exception -> getLog().error(exception));
}
}
Loading

0 comments on commit 11f6f46

Please sign in to comment.