Extract tags from JAR and ZIP archives
This commit is contained in:
45
src/main/java/xyz/naofal/jtags/ArchiveExtractor.java
Normal file
45
src/main/java/xyz/naofal/jtags/ArchiveExtractor.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package xyz.naofal.jtags;
|
||||
|
||||
import static xyz.naofal.jtags.JtagsLogger.logger;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class ArchiveExtractor {
|
||||
static List<String> extractArchive(Path archive, Path destination, String pattern) {
|
||||
logger.info("Extracting " + archive.toString() + "...");
|
||||
List<String> extractedPaths = new ArrayList<>();
|
||||
Pattern compiledPattern = Pattern.compile(pattern);
|
||||
try (InputStream in = new FileInputStream(archive.toFile());
|
||||
ZipInputStream zis = new ZipInputStream(in); ) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
logger.finest("Extracting " + entry.getName() + "...");
|
||||
if (!compiledPattern.matcher(entry.getName()).matches()) {
|
||||
zis.closeEntry();
|
||||
entry = zis.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
Path entryPath = destination.resolve(entry.getName());
|
||||
Files.createDirectories(entryPath.getParent());
|
||||
logger.finest(() -> "Extracting " + entryPath.toString() + "...");
|
||||
var bytes = zis.readAllBytes();
|
||||
Files.write(entryPath, bytes);
|
||||
extractedPaths.add(entryPath.toString());
|
||||
zis.closeEntry();
|
||||
}
|
||||
return extractedPaths;
|
||||
} catch (IOException ex) {
|
||||
logger.severe(ex.toString());
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Jtags {
|
||||
static class Options {
|
||||
@@ -17,6 +18,7 @@ public class Jtags {
|
||||
boolean excludeAnonymous = false;
|
||||
List<Class<? extends TagField>> fields =
|
||||
List.of(TagField.StaticTag.class, TagField.Package.class, TagField.EnclosingType.class);
|
||||
Optional<Path> extractPath = Optional.empty();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -104,11 +106,25 @@ public class Jtags {
|
||||
System.exit(1);
|
||||
yield null;
|
||||
}
|
||||
case String output -> Path.of(output);
|
||||
case String output -> Path.of(output).toAbsolutePath();
|
||||
};
|
||||
logger.config("Writing tags to " + options.output.toString());
|
||||
break;
|
||||
|
||||
case "-extract-dir":
|
||||
options.extractPath =
|
||||
switch (arguments.poll()) {
|
||||
case null -> {
|
||||
logger.severe("Expected argument after " + argument);
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
yield null;
|
||||
}
|
||||
case String dir -> Optional.of(Path.of(dir).toAbsolutePath());
|
||||
};
|
||||
logger.config("Extracting JARs to " + options.extractPath.toString());
|
||||
break;
|
||||
|
||||
default:
|
||||
options.sources.add(argument);
|
||||
break;
|
||||
@@ -137,7 +153,7 @@ public class Jtags {
|
||||
Usage: jtags [options] <sources...>
|
||||
Options:
|
||||
-o, -output <file> Write tags to specified <file>
|
||||
Use - for standard output
|
||||
Use - for standard output. Default: tags
|
||||
-lib Treat sources as third-party libraries
|
||||
(alias for -no-non-public -no-anonymous)
|
||||
-no-anonymous Exclude anonymous classes
|
||||
@@ -148,6 +164,7 @@ Options:
|
||||
s static tag
|
||||
p package
|
||||
t enclosing type
|
||||
-extract-dir <dir> Extract JAR and ZIP archives to <dir>
|
||||
-h, -help Show this message
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@ import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Trees;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
@@ -20,8 +24,10 @@ public class TagCollector {
|
||||
public static AbstractQueue<Tag> collectTags(Options options) {
|
||||
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
|
||||
|
||||
Iterable<? extends JavaFileObject> compilationUnits =
|
||||
fileManager.getJavaFileObjects(options.sources.toArray(String[]::new));
|
||||
String[] sources = resolveSources(options);
|
||||
logger.finest(() -> "Collecting tags from sources: " + Arrays.toString(sources));
|
||||
|
||||
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(sources);
|
||||
|
||||
JavacTask task =
|
||||
(JavacTask) compiler.getTask(null, fileManager, null, null, null, compilationUnits);
|
||||
@@ -42,4 +48,37 @@ public class TagCollector {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String[] resolveSources(Options options) {
|
||||
return options.sources.stream()
|
||||
.flatMap(
|
||||
path ->
|
||||
switch (path) {
|
||||
case String _ when path.endsWith(".java") -> Stream.of(path);
|
||||
case String _ when path.endsWith(".jar") || path.endsWith(".zip") -> {
|
||||
if (options.extractPath.isEmpty()) {
|
||||
logger.severe("Archive file specified, but no -extract-dir");
|
||||
System.exit(1);
|
||||
}
|
||||
Path archivePath = Path.of(path);
|
||||
String archiveName = archivePath.getFileName().toString();
|
||||
yield ArchiveExtractor.extractArchive(
|
||||
archivePath,
|
||||
options
|
||||
.extractPath
|
||||
.get()
|
||||
.resolve(archiveName.substring(0, archiveName.lastIndexOf('.'))),
|
||||
"^.*\\.java$")
|
||||
.stream();
|
||||
}
|
||||
default -> {
|
||||
logger.warning("Ignoring unsupported source: " + path);
|
||||
yield Stream.of();
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
// TODO: support package-info and module-info
|
||||
.filter(it -> !it.endsWith("package-info.java") && !it.endsWith("module-info.java"))
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public record TagsWriter(Options options) {
|
||||
private static final int MAX_PATTERN_LENGTH = 96;
|
||||
|
||||
public boolean writeTagsFile(AbstractQueue<Tag> tags) {
|
||||
logger.fine("Writing tags to " + options().output.toString());
|
||||
try (var outputStream =
|
||||
options().output.toString().equals("-")
|
||||
? System.out
|
||||
|
||||
@@ -41,10 +41,9 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
|
||||
@Override
|
||||
public Void visitPackage(PackageTree node, TreeVisitorContext p) {
|
||||
Tag tag =
|
||||
new Tag(
|
||||
TagKind.PACKAGE, node.getPackageName().toString(), p.getLocation().getParent(), "");
|
||||
new Tag(TagKind.PACKAGE, node.getPackageName().toString(), p.getLocation().getParent(), "");
|
||||
|
||||
if (tags.contains(tag)) {
|
||||
if (tags.contains(tag)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ public class TreeVisitorContext {
|
||||
long offset = getOffsetInSource(compilationUnitTree, node);
|
||||
try (var reader = new BufferedReader(compilationUnitTree.getSourceFile().openReader(true))) {
|
||||
long skipped = 0;
|
||||
String prevLine = "";
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) throw new RuntimeException("Reached the end of the stream");
|
||||
if (line == null) return prevLine;
|
||||
skipped += line.length() + 1;
|
||||
prevLine = line;
|
||||
if (skipped >= offset) return line;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in New Issue
Block a user