Skip to content

Use Reflections library rather than Jar class scanning #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ jobs:
building:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 11
java-version: 17
distribution: 'microsoft'

- name: Cache Gradle packages
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
Expand All @@ -32,7 +33,7 @@ jobs:
run: ./gradlew test

- name: Upload Nightly Build
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
if: success()
with:
name: skript-nightly
Expand Down
14 changes: 6 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,18 @@ on:
jobs:
testing:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [11, 13]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v1
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
java-version: 17
distribution: 'microsoft'

- name: Cache Gradle packages
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
Expand Down
37 changes: 26 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
plugins {
id "java"
id "application"
id 'com.gradleup.shadow' version '9.0.0-beta12'
id 'application'
id 'java'
}

mainClassName = "io.github.syst3ms.skriptparser.Parser"
compileTestJava.options.encoding = 'UTF-8'
compileJava.options.encoding = 'UTF-8'

sourceCompatibility = 1.11
mainClassName = "io.github.syst3ms.skriptparser.Parser"

repositories {
mavenCentral()
Expand All @@ -16,12 +18,25 @@ test {
}

dependencies {
implementation "org.jetbrains:annotations:15.0"
implementation group: "com.google.code.findbugs", name: "jsr305", version: "3.0.2"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:5.4.1"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.1"
testImplementation "junit:junit:4.12"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.1"
implementation (group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2')
implementation (group: 'org.jetbrains', name: 'annotations', version: '26.0.2')

shadow (group: 'org.reflections', name: 'reflections', version: '0.10.2')
shadow (group: 'org.javassist', name: 'javassist', version: '3.30.2-GA')
shadow (group: 'com.google.code.gson', name: 'gson', version: '2.13.0')

testRuntimeOnly (group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.11.4')
testRuntimeOnly (group: 'org.junit.jupiter', name:'junit-jupiter-engine', version: '5.11.4')

testImplementation (group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.11.4')
testImplementation (group: 'com.google.code.gson', name: 'gson', version: '2.13.0')
testImplementation (group: 'junit', name: 'junit', version: '4.13.2')
}

java {
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

jar {
Expand All @@ -30,4 +45,4 @@ jar {
"Implementation-Title": "skript-parser",
"Implementation-Version": "alpha")
}
}
}
94 changes: 60 additions & 34 deletions src/main/java/io/github/syst3ms/skriptparser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import io.github.syst3ms.skriptparser.registration.SkriptAddon;
import io.github.syst3ms.skriptparser.registration.SkriptRegistration;
import io.github.syst3ms.skriptparser.util.ConsoleColors;
import io.github.syst3ms.skriptparser.util.FileUtils;
import org.jetbrains.annotations.Nullable;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
Expand All @@ -23,12 +25,12 @@
import java.util.Calendar;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

public class Parser {

public static final String CONSOLE_FORMAT = "[%tT] %s: %s%n";
private static SkriptRegistration registration;

private static SkriptRegistration registration;
private static List<LogEntry> logs;

public static void main(String[] args) {
Expand Down Expand Up @@ -69,49 +71,71 @@ public static void main(String[] args) {
* @param standalone whether the parser tries to load addons (standalone) or not (library)
*/
public static void init(String[] mainPackages, String[] subPackages, String[] programArgs, boolean standalone) {
init(mainPackages, subPackages, programArgs, standalone, null);
}

/**
* Starts the parser.
* @param mainPackages packages inside which all subpackages containing classes to load may be present. Doesn't need
* to contain Skript's own main packages.
* @param subPackages the subpackages inside which classes to load may be present. Doesn't need to contain Skript's
* own subpackages.
* @param programArgs any other program arguments (typically from the command line)
* @param standalone whether the parser tries to load addons (standalone) or not (library)
* @param parserPath the main path to the parser, will be used to get the addons folder. If null, the path will be inferred from the parser's location.
*/
public static void init(String[] mainPackages, String[] subPackages, String[] programArgs, boolean standalone, @Nullable Path parserPath) {
Skript skript = new Skript(programArgs);
registration = new SkriptRegistration(skript);
DefaultRegistration.register();
// Make sure Skript loads properly no matter what
mainPackages = Arrays.copyOf(mainPackages, mainPackages.length + 1);
mainPackages[mainPackages.length - 1] = "io.github.syst3ms.skriptparser";
List<String> sub = new ArrayList<>();
sub.addAll(Arrays.asList(subPackages));
sub.addAll(Arrays.asList("expressions", "effects", "event", "lang", "sections", "tags"));
subPackages = sub.toArray(new String[0]);
try {
for (String mainPackage : mainPackages) {
FileUtils.loadClasses(FileUtils.getJarFile(Parser.class), mainPackage, subPackages);

List<String> allPackages = new ArrayList<>();

// Add default subpackages
List<String> defaultSubPackages = Arrays.asList("expressions", "effects", "event", "lang", "sections", "structures", "tags");
for (String subPackage : defaultSubPackages) {
allPackages.add("io.github.syst3ms.skriptparser." + subPackage);
}

// Add user-defined subpackages
for (String mainPackage : mainPackages) {
for (String subPackage : subPackages) {
allPackages.add(mainPackage + "." + subPackage);
}
}

try {
// Load all classes in the specified packages
new Reflections(allPackages, Scanners.SubTypes.filterResultsBy(s -> true))
.getSubTypesOf(Object.class)
.forEach(clazz -> {
try {
Class.forName(clazz.getName(), true, Parser.class.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
});

// Load addons if standalone mode is enabled
if (standalone) {
Path parserPath = Paths.get(Parser.class
.getProtectionDomain()
.getCodeSource()
.getLocation()
.toURI()
);
Path addonFolderPath = Paths.get(parserPath.getParent().toString(), "addons");
if (parserPath == null) {
parserPath = Paths.get(Parser.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
}
Path addonFolderPath = parserPath.getParent().resolve("addons");
if (Files.isDirectory(addonFolderPath)) {
Files.walk(addonFolderPath)
.filter(Files::isRegularFile)
.filter((filePath) -> filePath.toString().endsWith(".jar"))
.forEach((Path addonPath) -> {
try {
URLClassLoader child = new URLClassLoader(
new URL[]{addonPath.toUri().toURL()},
Parser.class.getClassLoader()
);
JarFile jar = new JarFile(addonPath.toString());
Manifest manifest = jar.getManifest();
String main = manifest.getMainAttributes().getValue("Main-Class");
if (main != null) {
Class<?> mainClass = Class.forName(main, true, child);
.filter(filePath -> filePath.toString().endsWith(".jar"))
.forEach(addonPath -> {
try (JarFile jar = new JarFile(addonPath.toString())) {
URLClassLoader child = new URLClassLoader(new URL[]{addonPath.toUri().toURL()}, Parser.class.getClassLoader());
String mainClassName = jar.getManifest().getMainAttributes().getValue("Main-Class");
if (mainClassName != null) {
try {
Class<?> mainClass = Class.forName(mainClassName, true, child);
Method init = mainClass.getDeclaredMethod("initAddon");
init.invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
} finally {
jar.close();
}
}
} catch (IOException | ClassNotFoundException e) {
Expand All @@ -125,6 +149,8 @@ public static void init(String[] mainPackages, String[] subPackages, String[] pr
System.err.println("Error while loading classes:");
e.printStackTrace();
}

// Log registration results
Calendar time = Calendar.getInstance();
logs = registration.register();
if (!logs.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Utility functions for file parsing
*/
public class FileUtils {

public static final Pattern LEADING_WHITESPACE_PATTERN = Pattern.compile("(\\s+)\\S.*");
public static final String MULTILINE_SYNTAX_TOKEN = "\\";
private static final String OS_SEPARATOR = FileSystems.getDefault().getSeparator();
Expand Down Expand Up @@ -134,7 +135,9 @@ public static String removeExtension(String s) {
* @param rootPackage a root package
* @param subPackages a list of all subpackages of the root package, in which classes will be loaded
* @throws IOException if an I/O error has occurred
* @deprecated use the Reflections library for classpath scanning and loading instead
*/
@Deprecated
public static void loadClasses(File jarFile, String rootPackage, String... subPackages) throws IOException {
if (jarFile.isDirectory())
throw new IllegalArgumentException("The provided file is actually a directory!");
Expand Down Expand Up @@ -174,7 +177,9 @@ public static void loadClasses(File jarFile, String rootPackage, String... subPa
* @param rootPackage a root package
* @param subPackages a list of all subpackages of the root package, in which classes will be leadied
* @throws IOException if an I/O error has occurred
* @deprecated use the Reflections library for classpath scanning and loading instead
*/
@Deprecated
public static void loadClasses(Path directory, String rootPackage, String... subPackages) throws IOException {
if (!directory.toFile().isDirectory())
throw new IllegalArgumentException("The provided file isn't a directory!");
Expand Down Expand Up @@ -209,8 +214,11 @@ public static void loadClasses(Path directory, String rootPackage, String... sub
*
* @param cla the class
* @return the JAR file containing the class
* @deprecated not all projects that uses skript-parser are JAR files, so this method may not work as expected
*/
@Deprecated
public static File getJarFile(Class<?> cla) throws URISyntaxException {
return new File(cla.getProtectionDomain().getCodeSource().getLocation().toURI());
}

}