You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/02/18 21:11:53 UTC

[groovy] 01/01: Add `ClassFinder` to tweak resolving types

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch danielsun/tweak-resolving
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 9e5bf896e39b444733e1ef650333de5fa8109375
Author: Daniel Sun <su...@apache.org>
AuthorDate: Wed Feb 19 05:09:45 2020 +0800

    Add `ClassFinder` to tweak resolving types
---
 .../codehaus/groovy/vmplugin/v9/ClassFinder.java   | 125 +++++++++++++++++++++
 .../groovy/vmplugin/v9/ClassFinderTest.groovy      |  64 +++++++++++
 2 files changed, 189 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java
new file mode 100644
index 0000000..264b00a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.vmplugin.v9;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Find classes under the specified package
+ *
+ * @since 3.0.2
+ */
+public class ClassFinder {
+    public static Map<String, Set<String>> find(URI originalUri, String packageName) {
+        return find(originalUri, packageName, false);
+    }
+
+    public static Map<String, Set<String>> find(URI originalUri, String packageName, boolean recursive) {
+        URI uri;
+        String prefix;
+        if ("jrt".equals(originalUri.getScheme())) {
+            uri = URI.create("jrt:/");
+            prefix = originalUri.getPath().substring(1) + "/";
+        } else {
+            Path path = Paths.get(originalUri);
+            if (Files.isDirectory(path)) {
+                uri = URI.create("file:/");
+                prefix = path.toString();
+            } else {
+                uri = URI.create("jar:" + originalUri.toString());
+                prefix = "";
+            }
+        }
+
+        return find(uri, prefix, packageName, recursive);
+    }
+
+    private static Map<String, Set<String>> find(URI uri, String prefix, String packageName, boolean recursive) {
+        boolean wfs = "file".equals(uri.getScheme());
+        if (wfs) prefix = prefix.replace("/", File.separator);
+
+        final String sepPatten = Pattern.quote(wfs ? File.separator : "/");
+        final int prefixElemCnt = prefix.trim().isEmpty() ? 0 : prefix.split(sepPatten).length;
+
+        Map<String, Set<String>> result = new LinkedHashMap<>();
+        try (FileSystem fs = newFileSystem(uri)) {
+            Files.walkFileTree(fs.getPath(prefix + "/" + packageName), new SimpleFileVisitor<Path>() {
+                @Override
+                public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) {
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
+                    String[] pathElems = path.toString().split(sepPatten);
+                    int nameCount = pathElems.length;
+                    if (nameCount <= prefixElemCnt) {
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    String filename = pathElems[nameCount - 1];
+                    if (!filename.endsWith(".class") || "module-info.class".equals(filename) || "package-info.class".equals(filename)) {
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    String packagePathStr = Arrays.stream(Arrays.copyOfRange(pathElems, prefixElemCnt + (pathElems[0].isEmpty() ? 1 : 0), nameCount - 1)).collect(Collectors.joining("/"));
+
+                    if (recursive || packageName.equals(packagePathStr)) {
+                        Set<String> packageNameSet = result.computeIfAbsent(filename.substring(0, filename.lastIndexOf(".")), f -> new HashSet<>());
+                        packageNameSet.add(packagePathStr);
+                    }
+
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        } catch (UnsupportedOperationException ignored) {
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    private static FileSystem newFileSystem(URI uri) throws IOException {
+        try {
+            return FileSystems.newFileSystem(uri, Collections.emptyMap());
+        } catch (FileSystemAlreadyExistsException e) {
+            return FileSystems.getFileSystem(uri);
+        }
+    }
+}
diff --git a/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy b/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy
new file mode 100644
index 0000000..01de50c
--- /dev/null
+++ b/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.vmplugin.v9
+
+import org.junit.Test
+
+class ClassFinderTest {
+    @Test
+    void findGroovyClass() {
+        Map<String, Set<String>> result = ClassFinder.find(GroovySystem.location.toURI(), "groovy/lang")
+        assert ["groovy/lang"] == result.get("GString")?.toList()
+        assert null == result.get("GroovydocHolder")
+    }
+
+    @Test
+    void findGroovyClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(GroovySystem.location.toURI(), "groovy/lang", true)
+        assert ["groovy/lang"] == result.get("GString")?.toList()
+        assert ["groovy/lang/groovydoc"] == result.get("GroovydocHolder")?.toList()
+    }
+
+    @Test
+    void findJavaClass() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/lang")
+        assert ["java/lang"] == result.get("String")?.toList()
+        assert null == result.get("MethodHandle")
+    }
+
+    @Test
+    void findJavaClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/lang", true)
+        assert ["java/lang/invoke"] == result.get("MethodHandle")?.toList()
+    }
+
+    @Test
+    void findJarClass() {
+        Map<String, Set<String>> result = ClassFinder.find(org.antlr.v4.runtime.tree.ParseTree.location.toURI(), "org/antlr/v4/runtime/tree")
+        assert ["org/antlr/v4/runtime/tree"] == result.get("ParseTree")?.toList()
+        assert null == result.get("ParseTreePattern")
+    }
+
+    @Test
+    void findJarClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(org.antlr.v4.runtime.tree.ParseTree.location.toURI(), "org/antlr/v4/runtime/tree", true)
+        assert ["org/antlr/v4/runtime/tree"] == result.get("ParseTree")?.toList()
+        assert ["org/antlr/v4/runtime/tree/pattern"] == result.get("ParseTreePattern")?.toList()
+    }
+}