module.auto
Bytecode parsing sufficient to enable auto-importing for programmatic modules
Determines required imports - used for JPMS and OSGi auto-import.
ImportsOf does the grunt work, Reader provides a suitable abstraction for package-to-module mappers.
Examples
The examples below use a bit of test support for class and packages:private static String cn(Class<?> type)
{
return type.getCanonicalName();
}
private static String pkg(Class<?> type)
{
return type.getPackage().getName();
}
Simple example
Given a class:
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImportsSlf4jApi {
private static final Logger LOG = LoggerFactory.getLogger(ImportsSlf4jApi.class);
public static void main(String[] args)
{
LOG.debug("hello " + Arrays.toString(args));
}
}
List all it’s imports:
Map<String, Set<String>> imports = new HashMap<>();
reader()
.addImportListener(imports::put)
.processClass(Resources.classAsBytes(ImportsSlf4jApi.class));
assertThat(imports, is(aMapWithSize(1)));
assertThat(imports, hasEntry(equalTo(cn(ImportsSlf4jApi.class)), containsInAnyOrder(
cn(Arrays.class),
cn(Logger.class),
cn(LoggerFactory.class),
cn(Object.class),
cn(Class.class),
cn(StringBuilder.class),
cn(String.class))));
Annotations selectively in/excluded
Given a heavily annotated class:
package io.earcam.acme;
import java.io.UncheckedIOException;
import java.util.Comparator;
import java.util.Objects;
import org.hamcrest.Matchers;
import org.hamcrest.core.IsAnything;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.Label;
import org.objectweb.asm.signature.SignatureReader;
import io.earcam.utilitarian.charstar.CharSequences;
@AcmeAnnotation(CharSequences.class)
public class Annotated implements Comparable<@AcmeAnnotation(Test.class) Annotated> {
@AcmeAnnotation(Label.class)
private int num = 42;
@AcmeAnnotation(SignatureReader.class)
public int getNum() throws @AcmeAnnotation(IllegalStateException.class) OutOfMemoryError
{
@AcmeAnnotation(Matchers.class)
int numnum = num;
return new @AcmeAnnotation(IsAnything.class) Integer(numnum).intValue();
}
@Override
public int compareTo(@AcmeAnnotation(Comparator.class) Annotated o)
{
return 0;
}
public static <T extends Comparable<T>> boolean isNaNaNullALul(T instance, Class<@AcmeAnnotation(Objects.class) T> type)
{
try {
return instance == null;
} catch(@AcmeAnnotation(UncheckedIOException.class) Throwable t) {
return true;
}
}
}
List all imports, including annotations and their elements:
Map<String, Set<String>> imports = new HashMap<>();
reader()
.addImportListener(imports::put)
.processClass(Resources.classAsBytes(Annotated.class));
assertThat(imports, is(aMapWithSize(1)));
assertThat(imports, hasEntry(equalTo(cn(Annotated.class)),
containsInAnyOrder(
cn(Object.class),
cn(Comparable.class),
cn(Class.class),
cn(Throwable.class),
cn(OutOfMemoryError.class),
cn(Integer.class),
cn(AcmeAnnotation.class),
cn(CharSequences.class),
cn(Test.class),
cn(Label.class),
cn(SignatureReader.class),
cn(IllegalStateException.class),
cn(Matchers.class),
cn(IsAnything.class),
cn(Comparator.class),
cn(Objects.class),
cn(UncheckedIOException.class))));
Or don’t (configure Reader to ignore):
reader() .ignoreAnnotations() .addImportListener(imports::put) .processClass(Resources.classAsBytes(Annotated.class)); assertThat(imports, is(aMapWithSize(1))); assertThat(imports, hasEntry(equalTo(cn(Annotated.class)), containsInAnyOrder( cn(Object.class), cn(Comparable.class), cn(Class.class), cn(Throwable.class), cn(OutOfMemoryError.class), cn(Integer.class))));
At a higher level
Map an artifact/archive/module; in this case defining-packages-to-required-packages:
Map<String, Set<String>> imports = new HashMap<>();
InputStream jar = bundle()
.add(ImportsSlf4jApi.class)
.add("irrelevant/file.txt", new ByteArrayInputStream("hello".getBytes(UTF_8)))
.build();
BiConsumer<String, Set<String>> listener = imports::put;
reader()
.addImportListener(listener)
.setImportedTypeReducer(Reader::typeToPackageReducer)
.setImportingTypeReducer(Reader::typeToPackageReducer)
.processJar(jar);
assertThat(imports, is(aMapWithSize(1)));
Set<String> expected = new HashSet<>(Arrays.asList(
pkg(Arrays.class),
pkg(Logger.class),
pkg(LoggerFactory.class),
pkg(Object.class),
pkg(Class.class),
pkg(StringBuilder.class),
pkg(String.class)));
assertThat(imports, hasEntry(equalTo(pkg(ImportsSlf4jApi.class)), equalTo(expected)));