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()
+ }
+}