All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.denwav.hypo.model.ClassProviderRoot Maven / Gradle / Ivy

/*
 * Hypo, an extensible and pluggable Java bytecode analytical model.
 *
 * Copyright (C) 2023  Kyle Wood (DenWav)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License only.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package dev.denwav.hypo.model;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Source of raw {@code byte[]} data for class files based on the class name when normalized with
 * {@link HypoModelUtil#normalizedClassName(String)}. This is used by the default {@link ClassDataProvider} abstract
 * implementation {@link AbstractClassDataProvider}, but is not a requirement for other implementations of
 * {@link ClassDataProvider} to use.
 *
 * 

Unlike {@link ClassDataProvider}, this class has no expectation of caching. Implementations should return a new * {@code byte[]} whenever a class name is requested, it is up to the provider to cache objects. * *

The purpose of separating the roots from the providers is to more generally handle the 2 separate problems of * parsing Java class files independently, the 2 problems being: * *

    *
  1. The location and format the class files are stored in
  2. *
  3. The format of the actual class files themselves and the method the class files are to be parsed
  4. *
* *

Roots handle the first concern, while providers handle the second. For example, Java classes are typically going * to either be in a zip archive such as a jar, or in a directory. Separate root implementations are needed to properly * handle these different sources of class files, but the process of actually parsing {@code .class} files is * independent of their source location. * *

By default 3 distinct types of roots are provided: * *

    *
  1. {@link #fromDir(Path) Directory-based roots}
  2. *
  3. {@link #fromJar(Path) Jar-based roots}
  4. *
  5. {@link #ofJdk() The system JDK root}
  6. *
* *

The system JDK root will use the currently running JVM to provide class file data for core JVM classes. Specific * implementations compatible with and tested against Java 8 through Java 17 will be returned based on the version of * the currently running JVM. Later versions of Java may also be supported, but this cannot be guaranteed. * *

Roots are {@link AutoCloseable} as they operate over file system resources. Roots passed into an * {@link AbstractClassDataProvider} will also be closed when the provider is closed. */ public interface ClassProviderRoot extends AutoCloseable { /** * Retrieve the raw binary data corresponding to the class file of the given name. Returns {@code null} if the class * file cannot be found. * * @param fileName The class file name to find. * @return The raw binary data corresponding to the given name. * @throws IOException If an IO error occurs while trying to read the file. */ byte @Nullable [] getClassData(final @NotNull String fileName) throws IOException; /** * Find all of the classes available to this root and return {@link FileDataReference references} to them, without * actually loading and returning the whole file itself. This allows providers to enumerate the full list of all * class files while still being able to lazily load the class data itself. * * @return A list of {@link FileDataReference} corresponding to every class file available to this root. * @throws IOException If an IO error occurs while enumerating the file list. */ @NotNull List getAllClasses() throws IOException; /** * Get a {@link Stream} which walks the full file tree of this root, including all non-class files. This is an * experimental API which is not guaranteed to be implemented. If not implemented, it will simply return an empty * stream. * *

The returned {@link Stream} must be closed, as it will contain references to open file handles. * * @return A {@link Stream} which walks the full file tree of this root, include all non-class files. * @throws IOException If an IO error occurs creating the stream. */ @ApiStatus.Experimental default @NotNull Stream walkAllFiles() throws IOException { return Stream.of(); } @Override void close() throws IOException; /** * Return the system root, which allows reading class data for the currently running JVM. This method will return * different implementations depending on the version of the JVM currently running, it is compatible with and has * been tested against at least Java 8 through Java 17. Later version of Java may also be compatible as well, but * that cannot be guaranteed. * * @return The root corresponding to the currently running JVM's system class files. * @throws IOException If an IO error occurs while reading system classes. */ static @NotNull ClassProviderRoot ofJdk() throws IOException { return SystemClassProviderRoot.newInstance(); } /** * Create a new root from the given directory. * * @param path The {@link Path path} to use as the root directory. * @return A new root from the given directory. */ static @NotNull ClassProviderRoot fromDir(final @NotNull Path path) { return new DirClassProviderRoot(path); } /** * Create multiple roots from multiple root directories. This method is simply a convenience method for creating * multiple roots individually. * * @param paths An array of {@link Path paths} to use as root directories for multiple roots. * @return A new list of roots corresponding to the array of directories. */ @SuppressWarnings("resource") static @NotNull List<@NotNull ClassProviderRoot> fromDirs(final @NotNull Path @NotNull ... paths) { final ClassProviderRoot[] roots = new ClassProviderRoot[paths.length]; for (int i = 0; i < paths.length; i++) { roots[i] = new DirClassProviderRoot(paths[i]); } return Arrays.asList(roots); } /** * Create a new root from the given jar file. This should generally work for any zip file, not just files with the * {@code .jar} extension. * * @param path The {@link Path path} to use as the jar file. * @return A new root for the given jar file. * @throws IOException If an IO error occurs while trying to open the jar file. */ static @NotNull ClassProviderRoot fromJar(final @NotNull Path path) throws IOException { return new JarClassProviderRoot(path); } /** * Create multiple roots from multiple jars. This method is simply a convenience method for creating multiple roots * individually. * * @param paths An array of {@link Path paths} to use as jars for multiple roots. * @return A new list of roots corresponding to the array of jars. * @throws IOException If an IO error occurs while trying to read one of the jar files. */ @SuppressWarnings("resource") static @NotNull List<@NotNull ClassProviderRoot> fromJars(final @NotNull Path @NotNull ... paths) throws IOException { final ClassProviderRoot[] roots = new ClassProviderRoot[paths.length]; for (int i = 0; i < paths.length; i++) { roots[i] = new JarClassProviderRoot(paths[i]); } return Arrays.asList(roots); } /** * A reference to a class file, with the ability to later load the data for the referenced class via the * {@link #readData()} method. * * @see #getAllClasses() */ interface FileDataReference { /** * The name of the class, in the internal JVM format. * * @return The name of the class, in the internal JVM format. */ @NotNull String name(); /** * Load the data for the reference class file. The actual reading of the class file does not occur until this * method is called. This method can return {@code null} in the unlikely case that the file no longer exists * by the time this method is called. * * @return The raw binary data for the class file this is referencing, or {@code null} if the class file cannot * be found anymore. * @throws IOException If an IO error occurs while reading the file. */ byte @Nullable [] readData() throws IOException; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy