-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
32 changed files
with
1,513 additions
and
390 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
src/main/java/org/minimallycorrect/javatransformer/api/ClassMember.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package org.minimallycorrect.javatransformer.api; | ||
|
||
import org.jetbrains.annotations.Contract; | ||
|
||
public interface ClassMember extends Annotated, Accessible, HasCodeFragment, Named { | ||
@Contract(pure = true) | ||
ClassInfo getClassInfo(); | ||
} |
195 changes: 45 additions & 150 deletions
195
src/main/java/org/minimallycorrect/javatransformer/api/ClassPath.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,178 +1,73 @@ | ||
package org.minimallycorrect.javatransformer.api; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.file.FileVisitResult; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.SimpleFileVisitor; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.zip.ZipEntry; | ||
import java.util.zip.ZipInputStream; | ||
import java.util.List; | ||
|
||
import lombok.NonNull; | ||
import lombok.SneakyThrows; | ||
import lombok.val; | ||
import javax.annotation.Nonnull; | ||
import javax.annotation.Nullable; | ||
|
||
import org.jetbrains.annotations.Contract; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import com.github.javaparser.JavaParser; | ||
import com.github.javaparser.ast.CompilationUnit; | ||
import com.github.javaparser.ast.body.TypeDeclaration; | ||
|
||
import org.minimallycorrect.javatransformer.internal.util.JVMUtil; | ||
import org.minimallycorrect.javatransformer.internal.util.Joiner; | ||
|
||
// TODO: make this faster by using dumb regexes instead of JavaParser? | ||
// probably not worth doing | ||
public class ClassPath { | ||
private final HashSet<String> classes = new HashSet<>(); | ||
private final HashSet<Path> inputPaths = new HashSet<>(); | ||
private final ClassPath parent; | ||
private boolean loaded; | ||
|
||
private ClassPath(@Nullable ClassPath parent) { | ||
this.parent = parent; | ||
} | ||
|
||
public ClassPath() { | ||
this((ClassPath) null); | ||
} | ||
|
||
public ClassPath(Collection<Path> paths) { | ||
this(); | ||
addPaths(paths); | ||
} | ||
|
||
public ClassPath createChildWithExtraPaths(Collection<Path> paths) { | ||
val searchPath = new ClassPath(this); | ||
searchPath.addPaths(paths); | ||
return searchPath; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
initialise(); | ||
return "[" + parent.toString() + ", " + inputPaths + " classes:\n" + Joiner.on("\n").join(classes.stream().sorted()) + "]"; | ||
} | ||
import org.minimallycorrect.javatransformer.internal.ClassPaths; | ||
|
||
public interface ClassPath extends Iterable<ClassInfo> { | ||
/** | ||
* Returns whether the given class name exists in this class path | ||
* Returns whether the given class name exists | ||
* | ||
* @param className class name in JLS format: package1.package2.ClassName, package1.package2.ClassName$InnerClass | ||
* @param className class name in JLS format: {@code package1.package2.ClassName}, {@code package1.package2.ClassName$InnerClass} | ||
* @return true if the class exists | ||
*/ | ||
@Contract("null -> fail") | ||
public boolean classExists(@NonNull String className) { | ||
initialise(); | ||
return classes.contains(className) || (parent != null && parent.classExists(className)); | ||
@Contract(value = "null -> fail", pure = true) | ||
default boolean classExists(@Nonnull String className) { | ||
return getClassInfo(className) != null; | ||
} | ||
|
||
/** | ||
* Adds a {@link Path} to this {@link ClassPath} | ||
* Returns the class info for the given class | ||
* | ||
* @param path {@link Path} to add | ||
* @return true if the path was added, false if the path already existed in this {@link ClassPath} | ||
* This instance is only guaranteed to provide information required for type resolution. Other information such as method bodies and annotations may be stripped | ||
* | ||
* @param className class name in JLS format: {@code package1.package2.ClassName}, {@code package1.package2.ClassName$InnerClass} | ||
* @return ClassInfo for the given name, or null if not found | ||
*/ | ||
@Contract("null -> fail") | ||
public boolean addPath(@NonNull Path path) { | ||
path = path.normalize().toAbsolutePath(); | ||
val add = !parentHasPath(path) && inputPaths.add(path); | ||
if (add && loaded) | ||
loadPath(path); | ||
return add; | ||
} | ||
|
||
@Contract("null -> fail") | ||
public void addPaths(@NonNull Collection<Path> paths) { | ||
for (Path path : paths) | ||
addPath(path); | ||
} | ||
@Contract(value = "null -> fail", pure = true) | ||
@Nullable | ||
ClassInfo getClassInfo(@Nonnull String className); | ||
|
||
/** | ||
* @param path path must be normalized and absolute | ||
* Adds the given paths to this {@link ClassPath} | ||
* | ||
* @param paths Paths to add | ||
*/ | ||
private boolean parentHasPath(Path path) { | ||
val parent = this.parent; | ||
return parent != null && (parent.inputPaths.contains(path) || parent.parentHasPath(path)); | ||
} | ||
|
||
private void findPaths(ZipEntry e, ZipInputStream zis) { | ||
val entryName = e.getName(); | ||
if (entryName.endsWith(".java")) | ||
findJavaPaths(zis); | ||
|
||
if (entryName.endsWith(".class")) | ||
classes.add(JVMUtil.fileNameToClassName(entryName)); | ||
} | ||
|
||
private void findJavaPaths(ZipInputStream zis) { | ||
val parsed = JavaParser.parse(new InputStream() { | ||
public int read(@NonNull byte[] b, int off, int len) throws IOException { | ||
return zis.read(b, off, len); | ||
} | ||
|
||
public void close() throws IOException {} | ||
|
||
public int read() throws IOException { | ||
return zis.read(); | ||
} | ||
}); | ||
findJavaPaths(parsed); | ||
default void addPaths(List<Path> paths) { | ||
for (Path extraPath : paths) | ||
addPath(extraPath); | ||
} | ||
|
||
private void findJavaPaths(CompilationUnit compilationUnit) { | ||
val typeNames = compilationUnit.getTypes(); | ||
val packageDeclaration = compilationUnit.getPackageDeclaration().orElse(null); | ||
val prefix = packageDeclaration == null ? "" : packageDeclaration.getNameAsString() + '.'; | ||
for (TypeDeclaration<?> typeDeclaration : typeNames) | ||
findJavaPaths(typeDeclaration, prefix); | ||
} | ||
/** | ||
* Adds a path to this {@link ClassPath} | ||
* | ||
* @param path Path to add | ||
* @return True if the path was added, false if it was already in this classpath | ||
* @throws UnsupportedOperationException if this {@link ClassPath} is immutable | ||
*/ | ||
boolean addPath(Path path); | ||
|
||
private void findJavaPaths(TypeDeclaration<?> typeDeclaration, String packagePrefix) { | ||
val name = packagePrefix + typeDeclaration.getNameAsString(); | ||
classes.add(name); | ||
for (val node : typeDeclaration.getChildNodes()) | ||
if (node instanceof TypeDeclaration) | ||
findJavaPaths((TypeDeclaration<?>) node, name + '.'); | ||
} | ||
/** | ||
* Checks if the given path is in this {@link ClassPath} | ||
* | ||
* @param path Path to check | ||
* @return True if this {@link ClassPath} contains the given path | ||
*/ | ||
boolean hasPath(Path path); | ||
|
||
private void initialise() { | ||
if (loaded) | ||
return; | ||
loaded = true; | ||
for (Path path : inputPaths) | ||
loadPath(path); | ||
@Contract(pure = true) | ||
static @Nonnull ClassPath of(@Nonnull Path... paths) { | ||
return of(ClassPaths.SystemClassPath.SYSTEM_CLASS_PATH, paths); | ||
} | ||
|
||
@SneakyThrows | ||
private void loadPath(Path path) { | ||
if (Files.isDirectory(path)) | ||
Files.walkFileTree(path, new SimpleFileVisitor<Path>() { | ||
@Override | ||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
val entryName = path.relativize(file).toString().replace(File.separatorChar, '/'); | ||
if (entryName.endsWith(".java")) { | ||
val parsed = JavaParser.parse(file); | ||
findJavaPaths(parsed); | ||
} | ||
return super.visitFile(file, attrs); | ||
} | ||
}); | ||
else if (Files.isRegularFile(path)) | ||
try (val zis = new ZipInputStream(Files.newInputStream(path))) { | ||
ZipEntry e; | ||
while ((e = zis.getNextEntry()) != null) { | ||
try { | ||
findPaths(e, zis); | ||
} finally { | ||
zis.closeEntry(); | ||
} | ||
} | ||
} | ||
@Contract(pure = true) | ||
static @Nonnull ClassPath of(@Nullable ClassPath parent, Path... paths) { | ||
return ClassPaths.of(parent, paths); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.