Skip to content

Commit 0b332e2

Browse files
committed
feat(jar): resource api
(cherry picked from commit 7c79ee7)
1 parent 90f6117 commit 0b332e2

6 files changed

Lines changed: 164 additions & 16 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>org.byteskript</groupId>
88
<artifactId>byteskript</artifactId>
9-
<version>1.0.40</version>
9+
<version>1.0.41</version>
1010
<name>ByteSkript</name>
1111
<description>A compiled JVM implementation of the Skript language.</description>
1212

src/main/java/org/byteskript/skript/api/Library.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import mx.kenzie.autodoc.api.note.Description;
1010
import mx.kenzie.foundation.Type;
1111
import mx.kenzie.foundation.compiler.State;
12-
import mx.kenzie.foundation.language.PostCompileClass;
12+
import org.byteskript.skript.api.resource.Resource;
1313
import org.byteskript.skript.compiler.Context;
1414
import org.byteskript.skript.runtime.type.Converter;
1515
import org.byteskript.skript.runtime.type.OperatorFunction;
@@ -61,7 +61,7 @@ public interface Library {
6161
Type[] getTypes();
6262

6363
@Description("Runtime dependencies to be included in complete archive.")
64-
Collection<PostCompileClass> getRuntime();
64+
Collection<Resource> getRuntime();
6565

6666
@Description("""
6767
Generates documentation for all available syntax to be exported to a processor.

src/main/java/org/byteskript/skript/api/ModifiableLibrary.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import mx.kenzie.autodoc.api.note.Ignore;
1111
import mx.kenzie.foundation.Type;
1212
import mx.kenzie.foundation.compiler.State;
13-
import mx.kenzie.foundation.language.PostCompileClass;
13+
import org.byteskript.skript.api.resource.Resource;
1414
import org.byteskript.skript.api.syntax.EventHolder;
1515
import org.byteskript.skript.compiler.CompileState;
1616
import org.byteskript.skript.compiler.Context;
@@ -178,7 +178,7 @@ public Type[] getTypes() {
178178

179179
@Override
180180
@Ignore
181-
public Collection<PostCompileClass> getRuntime() {
181+
public Collection<Resource> getRuntime() {
182182
return Collections.emptyList();
183183
}
184184

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package org.byteskript.skript.api.resource;
2+
3+
import mx.kenzie.foundation.language.PostCompileClass;
4+
5+
import java.io.*;
6+
import java.nio.charset.Charset;
7+
import java.util.jar.JarEntry;
8+
import java.util.zip.ZipOutputStream;
9+
10+
/**
11+
* Represents a resource, such as a file, that may be copied to a ZIP archive.
12+
* */
13+
public interface Resource {
14+
/**
15+
* Opens the source of this resource for reading.
16+
* @return A new input stream from which bytes are read when the resource is being copied.
17+
* @throws IOException If an I/O error occurs when opening the stream.
18+
* */
19+
InputStream open() throws IOException;
20+
21+
/**
22+
* The name that the resource should have in the final ZIP archive.
23+
* @return A text with the name of the resource.
24+
* */
25+
String getEntryName();
26+
27+
/**
28+
* Writes a resource to a ZIP output stream, closing any entries that were previously being written.
29+
* The resource's stream is opened, read, and closed.
30+
* @param outputStream The ZIP output stream to write to
31+
* @param resource The resource to copy
32+
* @throws IOException If an I/O error occurs when opening the stream or writing to the ZIP output stream.
33+
* */
34+
static void write(final ZipOutputStream outputStream, final Resource resource) throws IOException {
35+
try (final InputStream inputStream = resource.open()) {
36+
outputStream.putNextEntry(new JarEntry(resource.getEntryName()));
37+
inputStream.transferTo(outputStream);
38+
outputStream.closeEntry();
39+
}
40+
}
41+
42+
/**
43+
* Creates a new resource containing some bytes. The resource will maintain a reference to the byte array, so
44+
* modifications to the byte array between calls to {@link Resource#write(ZipOutputStream, Resource)} will result
45+
* in different contents being written.
46+
* @param name The name that the resource should have, were it to be added to a ZIP archive
47+
* @param bytes The byte contents of the resource.
48+
* @return A resource containing the given bytes as its contents.
49+
* */
50+
static Resource ofBytes(final String name, final byte[] bytes) {
51+
return new Resource() {
52+
@Override
53+
public InputStream open() {
54+
return new ByteArrayInputStream(bytes);
55+
}
56+
57+
@Override
58+
public String getEntryName() {
59+
return name;
60+
}
61+
};
62+
}
63+
64+
/**
65+
* Creates a new resource with the given ZIP entry name, containing the given text encoded using the JVM default character set.
66+
* Equivalent to {@link Resource#ofString(String, String, Charset)} with {@link Charset#defaultCharset()}.
67+
* @param name The name that the resource should have, were it to be added to a ZIP archive
68+
* @param text The text that the resource should contain
69+
* @return The newly created resource.
70+
* */
71+
static Resource ofString(final String name, final String text) {
72+
return ofString(name, text, Charset.defaultCharset());
73+
}
74+
75+
/**
76+
* Creates a new resource containing the given text encoded using the given character set.
77+
* @param name The name that the resource should have, were it to be added to a ZIP archive
78+
* @param text The text that the resource should contain
79+
* @param charset The character set that should be used to encode the text
80+
* @return The newly created resource.
81+
* */
82+
static Resource ofString(final String name, final String text, final Charset charset) {
83+
return ofBytes(name, text.getBytes(charset));
84+
}
85+
86+
/**
87+
* Creates a new resource with the internal file name of the compiled class (<code>com/example/Main.class</code>) containing
88+
* the compiled code of the class.
89+
* @param compiledClass The compiled class to use as the source of the resource.
90+
* @return The newly created resource.
91+
* */
92+
static Resource ofCompiledClass(final PostCompileClass compiledClass) {
93+
return ofBytes(compiledClass.internalName() + ".class", compiledClass.code());
94+
}
95+
96+
/**
97+
* Creates a new resource that will contain the contents of the given {@link File}. The file is accessed lazily, i.e.,
98+
* if the file is deleted before the resource is written to an archive, the resource will no longer be readable. To
99+
* load a file in memory and create a resource from its bytes, use {@link Resource#ofImmediateFile}
100+
* @param name The name that the resource should have, were it to be added to a ZIP archive
101+
* @param file The file that will act as the source for the contents of the resource
102+
* @return The newly created resource.
103+
* */
104+
static Resource ofFile(final String name, final File file) {
105+
return new Resource() {
106+
@Override
107+
public InputStream open() throws IOException {
108+
return new FileInputStream(file);
109+
}
110+
111+
@Override
112+
public String getEntryName() {
113+
return name;
114+
}
115+
};
116+
}
117+
118+
/**
119+
* Immediately reads all bytes in the given input stream and creates a new resource containing the read bytes.
120+
* This stores the entire stream contents in memory, so the stream may be closed and the resource
121+
* will remain readable. Equivalent to {@link Resource#ofBytes(String, byte[])} with {@link InputStream#readAllBytes()}.
122+
* @param name The name that the resource should have, were it to be added to a ZIP archive
123+
* @param stream The stream that will be read immediately.
124+
* @return The newly created resource with the contents of the stream.
125+
* */
126+
static Resource ofImmediate(final String name, final InputStream stream) throws IOException {
127+
return Resource.ofBytes(name, stream.readAllBytes());
128+
}
129+
130+
/**
131+
* Immediately reads all bytes in the given file and creates a new resource containing the read bytes.
132+
* This stores the entire file in memory, so the file may be deleted and the resource will remain readable.
133+
* Equivalent to {@link Resource#ofImmediate(String, InputStream)} with {@link FileInputStream}.
134+
* @param name The name that the resource should have, were it to be added to a ZIP archive
135+
* @param file The file that will be read immediately.
136+
* @return The newly created resource with the contents of the file.
137+
* */
138+
static Resource ofImmediateFile(final String name, final File file) throws IOException {
139+
return Resource.ofImmediate(name, new FileInputStream(file));
140+
}
141+
}

src/main/java/org/byteskript/skript/app/ScriptJarBuilder.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
import mx.kenzie.foundation.assembler.Manifest;
1111
import mx.kenzie.foundation.language.PostCompileClass;
1212
import org.byteskript.skript.api.Library;
13+
import org.byteskript.skript.api.resource.Resource;
1314
import org.byteskript.skript.runtime.Skript;
1415

1516
import java.io.File;
16-
import java.io.FileInputStream;
1717
import java.io.IOException;
1818
import java.util.ArrayList;
1919
import java.util.List;
@@ -34,22 +34,22 @@ public static void main(String... args) throws IOException {
3434

3535
static void compileResource(File jar, PostCompileClass... classes) throws IOException {
3636
if (!jar.exists()) jar.createNewFile();
37-
final List<File> resources = getFiles(new ArrayList<>(), RESOURCES.toPath());
38-
final List<PostCompileClass> runtime = new ArrayList<>();
37+
final List<Resource> runtime = new ArrayList<>();
3938
scrapeRuntimeResources(runtime);
39+
for (final File file : getFiles(new ArrayList<>(), RESOURCES.toPath())) {
40+
runtime.add(Resource.ofFile(file.getName(), file));
41+
}
4042
try (final JarBuilder builder = new JarBuilder(jar)) {
41-
builder.write(runtime.toArray(new PostCompileClass[0]));
43+
for (final Resource resource : runtime) {
44+
builder.write(resource.getEntryName(), resource.open());
45+
}
4246
builder.write(classes);
43-
for (final File resource : resources)
44-
try (final FileInputStream stream = new FileInputStream(resource)) {
45-
builder.write(resource.getName(), stream);
46-
}
4747
final String version = ScriptJarBuilder.class.getPackage().getImplementationVersion();
4848
builder.manifest(new Manifest(ScriptRunner.class.getName(), "Skript Compiler " + version, "Skript Jar Builder"));
4949
}
5050
}
5151

52-
static void scrapeRuntimeResources(final List<PostCompileClass> runtime) {
52+
static void scrapeRuntimeResources(final List<Resource> runtime) {
5353
for (final Library library : SKRIPT.getLoadedLibraries()) {
5454
runtime.addAll(library.getRuntime());
5555
}

src/main/java/org/byteskript/skript/compiler/SkriptLangSpec.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.byteskript.skript.api.LanguageElement;
1616
import org.byteskript.skript.api.Library;
1717
import org.byteskript.skript.api.ModifiableLibrary;
18+
import org.byteskript.skript.api.resource.Resource;
1819
import org.byteskript.skript.app.ScriptRunner;
1920
import org.byteskript.skript.app.SimpleThrottleController;
2021
import org.byteskript.skript.app.SkriptApp;
@@ -314,7 +315,7 @@ public String sourceFileExt() {
314315
}
315316

316317
@Override
317-
public Collection<PostCompileClass> getRuntime() {
318+
public Collection<Resource> getRuntime() {
318319
final List<PostCompileClass> runtime = new ArrayList<>();
319320
try {
320321
for (final Class<?> source : this.findClasses("org/byteskript/skript/runtime/")) {
@@ -344,7 +345,13 @@ public Collection<PostCompileClass> getRuntime() {
344345
} catch (IOException | ClassNotFoundException ex) {
345346
throw new ScriptCompileError(-1, "Unable to add runtime to compiled classes.", ex);
346347
}
347-
return runtime;
348+
349+
final List<Resource> resources = new ArrayList<>();
350+
for (final PostCompileClass compiledClass : runtime) {
351+
resources.add(Resource.ofCompiledClass(compiledClass));
352+
}
353+
354+
return resources;
348355
}
349356

350357
private Class<?>[] findClasses(final String namespace) throws IOException, ClassNotFoundException {

0 commit comments

Comments
 (0)