Skip to content

Commit

Permalink
Add ModuleExplorer to explore custom controls in modules (#778)
Browse files Browse the repository at this point in the history
  • Loading branch information
jperedadnr authored Oct 25, 2024
1 parent bb3b5bb commit ecf5245
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,38 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class LibraryUtil {

public static final String FOLDERS_LIBRARY_FILENAME = "library.folders"; //NOI18N
private static Set<ResolvedModule> MODULES;

LibraryUtil() {
// no-op
}

public static Optional<ModuleReference> getModuleReference(Path path) {
if (path == null) {
return Optional.empty();
}
if (MODULES == null) {
MODULES = ModuleLayer.boot().configuration().modules();
}
return MODULES.stream()
.map(ResolvedModule::reference)
.filter(r -> path.equals(r.location().map(Path::of).orElse(null)))
.findFirst();
}

public static boolean isJarPath(Path path) {
final String pathString = path.toString().toLowerCase(Locale.ROOT);
return pathString.endsWith(".jar"); //NOI18N
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2021, Gluon and/or its affiliates.
* Copyright (c) 2017, 2024, Gluon and/or its affiliates.
* Copyright (c) 2012, 2014, Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
Expand Down Expand Up @@ -36,6 +36,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.lang.module.ModuleReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
Expand All @@ -53,6 +54,7 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand All @@ -66,6 +68,7 @@
import com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer;
import com.oracle.javafx.scenebuilder.kit.library.util.JarReport;
import com.oracle.javafx.scenebuilder.kit.library.util.JarReportEntry;
import com.oracle.javafx.scenebuilder.kit.library.util.ModuleExplorer;

/**
*
Expand Down Expand Up @@ -348,49 +351,55 @@ private LibraryItem makeLibraryItem(Path path) throws IOException {
}


private void exploreAndUpdateLibrary(Collection<Path> jarsOrFolders) throws IOException {
private void exploreAndUpdateLibrary(Collection<Path> modulesOrJarsOrFolders) throws IOException {

// 1) we create a classloader
// 2) we explore all the jars and folders
// 2) we explore all the modules, jars, and folders
// 3) we construct a list of library items
// 4) we update the user library with the class loader and items
// 5) on startup only, we allow opening files that may/may not rely on the user library

// 1)
final ClassLoader classLoader;
if (jarsOrFolders.isEmpty()) {
if (modulesOrJarsOrFolders.isEmpty()) {
classLoader = null;
} else {
classLoader = new URLClassLoader(makeURLArrayFromPaths(jarsOrFolders));
classLoader = new URLClassLoader(makeURLArrayFromPaths(modulesOrJarsOrFolders));
}

// 2)
final List<JarReport> jarOrFolderReports = new ArrayList<>();
// boolean shouldShowImportGluonJarAlert = false;
for (Path currentJarOrFolder : jarsOrFolders) {
String jarName = currentJarOrFolder.getName(currentJarOrFolder.getNameCount() - 1).toString();
final List<JarReport> moduleOrJarOrFolderReports = new ArrayList<>();
for (Path currentModuleOrJarOrFolder : modulesOrJarsOrFolders) {
String jarName = currentModuleOrJarOrFolder.getName(currentModuleOrJarOrFolder.getNameCount() - 1).toString();
if (JAVAFX_MODULES.stream().anyMatch(jarName::startsWith)) {
continue;
}

JarReport jarReport;
String resultText = "";
if (LibraryUtil.isJarPath(currentJarOrFolder)) {
LOGGER.info(I18N.getString("log.info.explore.jar", currentJarOrFolder));
final JarExplorer explorer = new JarExplorer(currentJarOrFolder);
Optional<ModuleReference> moduleReference = LibraryUtil.getModuleReference(currentModuleOrJarOrFolder);
if (moduleReference.isPresent()) {
LOGGER.info(I18N.getString("log.info.explore.module", moduleReference.get().descriptor()));
final ModuleExplorer explorer = new ModuleExplorer(moduleReference.get());
jarReport = explorer.explore();
resultText = I18N.getString("log.info.explore.module.results", jarName);
}
else if (LibraryUtil.isJarPath(currentModuleOrJarOrFolder)) {
LOGGER.info(I18N.getString("log.info.explore.jar", currentModuleOrJarOrFolder));
final JarExplorer explorer = new JarExplorer(currentModuleOrJarOrFolder);
jarReport = explorer.explore(classLoader);
resultText = I18N.getString("log.info.explore.jar.results", jarName);
}
else if (Files.isDirectory(currentJarOrFolder)) {
LOGGER.info(I18N.getString("log.info.explore.folder", currentJarOrFolder));
final FolderExplorer explorer = new FolderExplorer(currentJarOrFolder);
else if (Files.isDirectory(currentModuleOrJarOrFolder)) {
LOGGER.info(I18N.getString("log.info.explore.folder", currentModuleOrJarOrFolder));
final FolderExplorer explorer = new FolderExplorer(currentModuleOrJarOrFolder);
jarReport = explorer.explore(classLoader);
resultText = I18N.getString("log.info.explore.folder.results", jarName);
} else {
continue;
}

jarOrFolderReports.add(jarReport);
moduleOrJarOrFolderReports.add(jarReport);

StringBuilder sb = new StringBuilder(resultText).append("\n");
if (jarReport.getEntries().isEmpty()) {
Expand All @@ -400,26 +409,13 @@ else if (Files.isDirectory(currentJarOrFolder)) {
}
LOGGER.info(sb.toString());

LOGGER.info(I18N.getString("log.info.explore.end", currentJarOrFolder));

// if (jarReport.hasGluonControls()) {
// // We check if the jar has already been imported to avoid showing the import gluon jar
// // alert every time Scene Builder starts for jars that have already been imported
// if (!hasGluonJarBeenImported(jarReport.getJar().getFileName().toString())) {
// shouldShowImportGluonJarAlert = true;
// }
//
// }
LOGGER.info(I18N.getString("log.info.explore.end", currentModuleOrJarOrFolder));
}

// if (shouldShowImportGluonJarAlert && onImportingGluonControls != null) {
// onImportingGluonControls.run();
// }

// 3)
final List<LibraryItem> newItems = new ArrayList<>();
for (JarReport jarOrFolderReport : jarOrFolderReports) {
newItems.addAll(makeLibraryItems(jarOrFolderReport));
for (JarReport moduleOrJarOrFolderReport : moduleOrJarOrFolderReports) {
newItems.addAll(makeLibraryItems(moduleOrJarOrFolderReport));
}

// 4)
Expand All @@ -429,8 +425,8 @@ else if (Files.isDirectory(currentJarOrFolder)) {
.stream()
.distinct()
.collect(Collectors.toList()));
library.updateJarReports(new ArrayList<>(jarOrFolderReports));
library.getOnFinishedUpdatingJarReports().accept(jarOrFolderReports);
library.updateJarReports(new ArrayList<>(moduleOrJarOrFolderReports));
library.getOnFinishedUpdatingJarReports().accept(moduleOrJarOrFolderReports);
library.updateExplorationDate(new Date());

// 5
Expand Down Expand Up @@ -471,9 +467,9 @@ private URL[] makeURLArrayFromPaths(Collection<Path> paths) {
if (url.toString().endsWith(".jar")) {
result[i++] = new URL("jar", "", url + "!/"); // <-- jar:file/path/to/jar!/
} else {
result[i++] = url; // <-- file:/path/to/folder/
result[i++] = url; // <-- file:/path/to/folder/ or jrt:/module.name
}
} catch(MalformedURLException x) {
} catch (MalformedURLException x) {
throw new RuntimeException("Bug in " + getClass().getSimpleName(), x); //NOI18N
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2024, Gluon and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* - Neither the name of Oracle Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.javafx.scenebuilder.kit.library.util;

import com.oracle.javafx.scenebuilder.kit.library.util.JarReportEntry.Status;

import java.io.IOException;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.nio.file.Path;

public class ModuleExplorer extends ExplorerBase {

private final ModuleReference moduleReference;
private final Module module;

public ModuleExplorer(ModuleReference moduleReference) {
assert moduleReference != null;
this.moduleReference = moduleReference;
this.module = ModuleLayer.boot().findModule(moduleReference.descriptor().name()).orElseThrow();
}

public JarReport explore() throws IOException {
ClassLoader classLoader = module.getClassLoader();
final JarReport result = new JarReport(moduleReference.location().map(Path::of).orElse(null));
try (ModuleReader reader = moduleReference.open()) {
reader.list().forEach(cl -> {
if (cl.endsWith(".class")) {
String className = cl.substring(0, cl.length() - ".class".length()).replaceAll("/", ".");
JarReportEntry explored = super.exploreEntry(cl, classLoader, className);
if (explored.getStatus() != Status.IGNORED) {
result.getEntries().add(explored);
}
}
});
}

return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ label.qualifier.vertical = (vertical)
# -----------------------------------------------------------------------------
log.info.explore.end = End exploring {0}
log.info.explore.folder = Start exploring FOLDER {0}
log.info.explore.module = Start exploring MODULE {0}
log.info.explore.jar = Start exploring JAR {0}
log.info.explore.jar.results = Results of exploring JAR {0}
log.info.explore.module.results = Results of exploring MODULE {0}
log.info.explore.folder.results = Results of exploring FOLDER {0}
log.info.explore.no.results = No custom controls found
log.warning.inline.edit.internationalized.strings = Can''t inline edit internationalized strings
Expand Down

0 comments on commit ecf5245

Please # to comment.