added extra classpath manifest attribute

This commit is contained in:
2023-06-24 14:15:07 +08:00
parent d2040fb02a
commit a1139bdc1c
6 changed files with 310 additions and 8 deletions

View File

@@ -1,24 +1,27 @@
package net.woggioni.envelope;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.AbstractMap;
import java.util.Map;
import java.util.MissingFormatArgumentException;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import java.io.IOException;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Common {
@@ -114,4 +117,144 @@ public class Common {
OutputStream result = new FileOutputStream(file);
return buffered ? new BufferedOutputStream(result) : result;
}
/**
* @param template Template text containing the variables to be replaced by this method. <br>
* Variables follow the format ${variable_name}. <br>
* Example: <br>
* "This template was created by ${author}."
* @param valuesMap A hashmap with the values of the variables to be replaced. <br>
* The key is the variable name and the value is the value to be replaced in the template. <br>
* Example: <br>
* {"author" =&gt; "John Doe"}
* @return The template text (String) with the variable names replaced by the values passed in the map. <br>
* If any of the variable names is not contained in the map it will be replaced by an empty string. <br>
* Example: <br>
* "This template was created by John Doe."
*/
public static String renderTemplate(String template, Map<String, Object> valuesMap) {
return renderTemplate(template, valuesMap, null);
}
public static int indexOfWithEscape(String haystack, char needle, char escape, int begin, int end) {
int result = -1;
int cursor = begin;
if (end == 0) {
end = haystack.length();
}
int escapeCount = 0;
while (cursor < end) {
char c = haystack.charAt(cursor);
if (escapeCount > 0) {
--escapeCount;
if (c == escape) {
result = -1;
}
} else if (escapeCount == 0) {
if (c == escape) {
++escapeCount;
}
if (c == needle) {
result = cursor;
}
}
if (result >= 0 && escapeCount == 0) {
break;
}
++cursor;
}
return result;
}
public static String renderTemplate(
String template,
Map<String, Object> valuesMap,
Map<String, Map<String, Object>> dictMap) {
StringBuilder sb = new StringBuilder();
Object absent = new Object();
int cursor = 0;
TokenScanner tokenScanner = new TokenScanner(template, '$', '$');
while (cursor < template.length()) {
tokenScanner.next();
int nextPlaceHolder;
switch (tokenScanner.getTokenType()) {
case TOKEN: {
nextPlaceHolder = tokenScanner.getTokenIndex();
while (cursor < nextPlaceHolder) {
char ch = template.charAt(cursor++);
sb.append(ch);
}
if (cursor + 1 < template.length() && template.charAt(cursor + 1) == '{') {
String key;
String context = null;
String defaultValue = null;
Object value;
int end = template.indexOf('}', cursor + 1);
int colon;
if (dictMap == null)
colon = -1;
else {
colon = indexOfWithEscape(template, ':', '\\', cursor + 1, template.length());
if (colon >= end) colon = -1;
}
if (colon < 0) {
key = template.substring(cursor + 2, end);
value = valuesMap.getOrDefault(key, absent);
} else {
context = template.substring(cursor + 2, colon);
int secondColon = indexOfWithEscape(template, ':', '\\', colon + 1, end);
if (secondColon < 0) {
key = template.substring(colon + 1, end);
} else {
key = template.substring(colon + 1, secondColon);
defaultValue = template.substring(secondColon + 1, end);
}
value = Optional.ofNullable(dictMap.get(context))
.map(m -> m.get(key))
.orElse(absent);
}
if (value != absent) {
sb.append(value.toString());
} else {
if (defaultValue != null) {
sb.append(defaultValue);
} else {
throw new MissingFormatArgumentException(
String.format("Missing value for placeholder '%s'",
context == null ? key : context + ':' + key
)
);
}
}
cursor = end + 1;
}
break;
}
case ESCAPE:
nextPlaceHolder = tokenScanner.getTokenIndex();
while (cursor < nextPlaceHolder) {
char ch = template.charAt(cursor++);
sb.append(ch);
}
cursor = nextPlaceHolder + 1;
sb.append(template.charAt(cursor++));
break;
case END:
default:
nextPlaceHolder = template.length();
while (cursor < nextPlaceHolder) {
char ch = template.charAt(cursor++);
sb.append(ch);
}
break;
}
}
return sb.toString();
}
public static <T> Stream<T> opt2Stream(Optional<T> opt) {
return opt.map(Stream::of).orElse(Stream.empty());
}
}

View File

@@ -1,9 +1,10 @@
package net.woggioni.envelope;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Calendar;
import java.util.GregorianCalendar;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Constants {
@@ -16,10 +17,12 @@ public class Constants {
public static final String SYSTEM_PROPERTIES_FILE = METADATA_FOLDER + "/system.properties";
public static final String LIBRARIES_TOC = METADATA_FOLDER + "/libraries.txt";
public static final char EXTRA_CLASSPATH_ENTRY_SEPARATOR = ';';
public static class ManifestAttributes {
public static final String MAIN_MODULE = "Executable-Jar-Main-Module";
public static final String MAIN_CLASS = "Executable-Jar-Main-Class";
public static final String EXTRA_CLASSPATH = "Executable-Jar-Extra-Classpath";
public static final String ENTRY_HASH = "SHA-256-Digest";
}

View File

@@ -0,0 +1,74 @@
package net.woggioni.envelope;
import lombok.Getter;
public class TokenScanner {
public enum TokenType {
ESCAPE, TOKEN, END;
}
private final String haystack;
private final char needle;
private final char escape;
private int begin;
private final int end;
@Getter
private int tokenIndex = -1;
@Getter
private TokenType tokenType = null;
public TokenScanner(String haystack, char needle, char escape, int begin, int end) {
this.haystack = haystack;
this.needle = needle;
this.escape = escape;
this.begin = begin;
this.end = end;
}
public TokenScanner(String haystack, char needle, char escape, int begin) {
this(haystack, needle, escape, begin, haystack.length());
}
public TokenScanner(String haystack, char needle, char escape) {
this(haystack, needle, escape, 0, haystack.length());
}
public void next() {
int result = -1;
int cursor = begin;
int escapeCount = 0;
while(true) {
if(cursor < end) {
char c = haystack.charAt(cursor);
if (escapeCount > 0) {
--escapeCount;
if(c == escape || c == needle) {
tokenIndex = cursor - 1;
tokenType = TokenType.ESCAPE;
break;
}
} else if (escapeCount == 0) {
if (c == escape) {
++escapeCount;
}
if (c == needle) {
result = cursor;
}
}
if (result >= 0 && escapeCount == 0) {
tokenIndex = result;
tokenType = TokenType.TOKEN;
break;
}
++cursor;
} else {
tokenIndex = result;
tokenType = result < 0 ? TokenType.END :TokenType.TOKEN;
break;
}
}
begin = cursor + 1;
}
}