tuddylib but smol
This commit is contained in:
parent
e65e1e798d
commit
e9a912c3db
35 changed files with 3517 additions and 7 deletions
|
@ -29,11 +29,11 @@ import java.util.ArrayList;
|
|||
@Mod(modid = "baseband")
|
||||
public class BaseBand {
|
||||
public static int majorVersion = 1;
|
||||
public static int buildNumber = 244;
|
||||
public static String hash = "7e7800a2711492fc";
|
||||
public static int buildNumber = 269;
|
||||
public static String hash = "8c1ab03377ef905c";
|
||||
|
||||
public static String name = "BaseBand";
|
||||
public long timeOfCompile = 1695856587011L;
|
||||
public long timeOfCompile = 1695869277858L;
|
||||
public CommandManager commandRegistry;
|
||||
public EventBus eventBus;
|
||||
public ArrayList<Module> modules = new ArrayList<>();
|
||||
|
|
|
@ -70,8 +70,6 @@ dependencies {
|
|||
exclude module: 'log4j-core'
|
||||
}
|
||||
|
||||
jarLibs files('libs/TuddyLIB.jar')
|
||||
|
||||
annotationProcessor('org.spongepowered:mixin:0.8.5:processor') {
|
||||
exclude module: 'gson'
|
||||
}
|
||||
|
@ -119,6 +117,5 @@ jar {
|
|||
}
|
||||
} {
|
||||
exclude("mcmod.info")
|
||||
exclude("de/tudbut/mcregistry/DoNothingMod.class")
|
||||
}
|
||||
}
|
Binary file not shown.
61
Loader/src/main/java/de/tudbut/io/CLSPrintWriter.java
Normal file
61
Loader/src/main/java/de/tudbut/io/CLSPrintWriter.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.tools.ReflectUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class CLSPrintWriter extends PrintWriter {
|
||||
|
||||
public String customLineSeparator = "\n";
|
||||
|
||||
public CLSPrintWriter(Writer writer) {
|
||||
super(writer);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(Writer writer, boolean b) {
|
||||
super(writer, b);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(OutputStream outputStream) {
|
||||
super(outputStream);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(OutputStream outputStream, boolean b) {
|
||||
super(outputStream, b);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(String s) throws FileNotFoundException {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(String s, String s1) throws FileNotFoundException, UnsupportedEncodingException {
|
||||
super(s, s1);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(File file) throws FileNotFoundException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public CLSPrintWriter(File file, String s) throws FileNotFoundException, UnsupportedEncodingException {
|
||||
super(file, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() {
|
||||
try {
|
||||
synchronized(this.lock) {
|
||||
if (this.out == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
this.out.write(customLineSeparator);
|
||||
if (ReflectUtil.getPrivateFieldByTypeIndex(PrintWriter.class, this, boolean.class, 0)) {
|
||||
this.out.flush();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException var4) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException var5) {
|
||||
ReflectUtil.setPrivateFieldByTypeIndex(PrintWriter.class, this, boolean.class, 1, true);
|
||||
}
|
||||
}
|
||||
}
|
156
Loader/src/main/java/de/tudbut/io/StreamReader.java
Normal file
156
Loader/src/main/java/de/tudbut/io/StreamReader.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
package de.tudbut.io;
|
||||
|
||||
import de.tudbut.type.IntArrayList;
|
||||
import de.tudbut.obj.CarrierException;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
/**
|
||||
* Helper for reading {@link java.io.InputStream}
|
||||
*/
|
||||
public class StreamReader {
|
||||
|
||||
/**
|
||||
* Buffer size for reading. Directly affects speed.
|
||||
*/
|
||||
public static int BUFFER_SIZE = 4096;
|
||||
|
||||
/**
|
||||
* The stream to read from
|
||||
*/
|
||||
public final InputStream inputStream;
|
||||
|
||||
private boolean endReached = false;
|
||||
|
||||
/**
|
||||
* Constructs a StreamReader for an InputStream
|
||||
* @param stream The stream to read
|
||||
*/
|
||||
public StreamReader(InputStream stream) {
|
||||
this.inputStream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the input stream. Unaffected by {@link StreamReader#BUFFER_SIZE}
|
||||
* @return read byte
|
||||
* @throws IOException Inherited from {@link InputStream#read}
|
||||
* @throws ArrayIndexOutOfBoundsException When the stream end was reached
|
||||
*/
|
||||
public byte readNextByte() throws IOException, ArrayIndexOutOfBoundsException {
|
||||
int got = inputStream.read();
|
||||
if (got < 0) {
|
||||
endReached = true;
|
||||
throw new ArrayIndexOutOfBoundsException("Stream end reached");
|
||||
}
|
||||
return (byte) got;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the input stream. Unaffected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* Byte is converted to int, being unsigned in the process.
|
||||
* @return read unsigned bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextByte()}
|
||||
* @throws ArrayIndexOutOfBoundsException Inherited from {@link StreamReader#readNextByte()}
|
||||
*/
|
||||
public int readNextUnsignedByte() throws IOException, ArrayIndexOutOfBoundsException {
|
||||
return Byte.toUnsignedInt(readNextByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the input stream until end is reached. Unaffected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* Byte is converted to int, being unsigned in the process.
|
||||
* @return read unsigned bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextUnsignedByte()}
|
||||
*/
|
||||
public int[] readAllAsUnsignedBytes() throws IOException {
|
||||
IntArrayList bytes = new IntArrayList();
|
||||
int currentByte;
|
||||
while (!endReached) {
|
||||
try {
|
||||
currentByte = readNextUnsignedByte();
|
||||
bytes.add(currentByte);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException ignore) {
|
||||
}
|
||||
}
|
||||
return bytes.toIntArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the input stream until end is reached. Affected by {@link StreamReader#BUFFER_SIZE}.
|
||||
* @return read bytes
|
||||
* @throws IOException Inherited from {@link StreamReader#readNextByte()}
|
||||
*/
|
||||
public byte[] readAllAsBytes() throws IOException {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte[] currentBytes = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
try {
|
||||
while ((len = inputStream.read(currentBytes)) > 0) {
|
||||
bytes.write(currentBytes, 0, len);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new CarrierException(e, bytes.toByteArray());
|
||||
}
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all bytes in the stream and converts them to a char[]
|
||||
* @return {@link Character} array (native)
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public char[] readAllAsChars() throws IOException {
|
||||
return new String(readAllAsBytes()).toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all bytes in the stream and converts them to a char[]
|
||||
* @param encoding The encoding to use for conversion
|
||||
* @return {@link Character} array (native)
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public char[] readAllAsChars(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding).toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link StreamReader#readAllAsChars()}, but returns a string instead.
|
||||
* @return The string
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String readAllAsString() throws IOException {
|
||||
return new String(readAllAsBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lines in the file. All line ending are supported (\r\n, \n, \r).
|
||||
* @return The lines
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String[] readAllAsLines() throws IOException {
|
||||
return new String(readAllAsBytes()).replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link StreamReader#readAllAsChars()}, but returns a string instead.
|
||||
* @param encoding The encoding to use
|
||||
* @return The string
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String readAllAsString(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lines in the file. All line ending are supported (\r\n, \n, \r).
|
||||
* @param encoding The encoding to use
|
||||
* @return The lines
|
||||
* @throws IOException Inherited from {@link StreamReader#readAllAsBytes()}
|
||||
*/
|
||||
public String[] readAllAsLines(String encoding) throws IOException {
|
||||
return new String(readAllAsBytes(), encoding).replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
||||
}
|
||||
|
||||
}
|
11
Loader/src/main/java/de/tudbut/obj/CarrierException.java
Normal file
11
Loader/src/main/java/de/tudbut/obj/CarrierException.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package de.tudbut.obj;
|
||||
|
||||
public class CarrierException extends RuntimeException {
|
||||
|
||||
public final Object carried;
|
||||
|
||||
public CarrierException(Exception e, Object o) {
|
||||
super(e);
|
||||
carried = o;
|
||||
}
|
||||
}
|
32
Loader/src/main/java/de/tudbut/obj/DoubleTypedObject.java
Normal file
32
Loader/src/main/java/de/tudbut/obj/DoubleTypedObject.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package de.tudbut.obj;
|
||||
|
||||
import de.tudbut.tools.ReflectUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DoubleTypedObject<O, T> implements Cloneable {
|
||||
|
||||
public O o;
|
||||
public T t;
|
||||
|
||||
public DoubleTypedObject(O o, T t) {
|
||||
this.o = o;
|
||||
this.t = t;
|
||||
}
|
||||
public DoubleTypedObject() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o1) {
|
||||
if (this == o1) return true;
|
||||
if (!(o1 instanceof DoubleTypedObject)) return false;
|
||||
DoubleTypedObject<?, ?> that = (DoubleTypedObject<?, ?>) o1;
|
||||
return Objects.equals(o, that.o) && Objects.equals(t, that.t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@Override
|
||||
public DoubleTypedObject<O, T> clone() {
|
||||
return new DoubleTypedObject<>(ReflectUtil.forceClone(o), ReflectUtil.forceClone(t));
|
||||
}
|
||||
}
|
199
Loader/src/main/java/de/tudbut/obj/TLMap.java
Normal file
199
Loader/src/main/java/de/tudbut/obj/TLMap.java
Normal file
|
@ -0,0 +1,199 @@
|
|||
package de.tudbut.obj;
|
||||
|
||||
import de.tudbut.tools.Retriever;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TLMap<K, V> {
|
||||
protected Binding<K, V> binding = new Binding<>();
|
||||
|
||||
public static TLMap<String, String> stringToMap(String mapStringParsable) {
|
||||
TLMap<String, String> map = new TLMap<>();
|
||||
|
||||
String[] splitTiles = mapStringParsable.split(";");
|
||||
for (int i = 0; i < splitTiles.length; i++) {
|
||||
String tile = splitTiles[i];
|
||||
String[] splitTile = tile.split(":");
|
||||
if (tile.contains(":")) {
|
||||
if (splitTile.length == 2)
|
||||
map.set(
|
||||
splitTile[0].replaceAll("%I", ":").replaceAll("%B", ";").replaceAll("%P", "%"),
|
||||
splitTile[1].equals("%N") ? null : splitTile[1].replaceAll("%I", ":").replaceAll("%B", ";").replaceAll("%P", "%")
|
||||
);
|
||||
else
|
||||
map.set(splitTile[0].replaceAll("%I", ":").replaceAll("%B", ";").replaceAll("%P", "%"), "");
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String mapToString(TLMap<String, String> map) {
|
||||
StringBuilder r = new StringBuilder();
|
||||
|
||||
for (String key : map.keys().toArray(new String[0])) {
|
||||
|
||||
r
|
||||
.append(key.replaceAll("%", "%P").replaceAll(";", "%B").replaceAll(":", "%I"))
|
||||
.append(":")
|
||||
.append(map.get(key) == null ? "%N" : map.get(key).replaceAll("%", "%P").replaceAll(";", "%B").replaceAll(":", "%I"))
|
||||
.append(";")
|
||||
;
|
||||
}
|
||||
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
public void set(K key, V value) {
|
||||
binding.set(key, value);
|
||||
}
|
||||
|
||||
public void setIfNull(K key, V value) {
|
||||
if(binding.get(key) == null) {
|
||||
binding.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
return binding.get(key);
|
||||
}
|
||||
|
||||
public V get(K key, V def) {
|
||||
V v = binding.get(key);
|
||||
return v == null ? def : v;
|
||||
}
|
||||
public V get(K key, Retriever<V> def) {
|
||||
V v = binding.get(key);
|
||||
return v == null ? def.retrieve() : v;
|
||||
}
|
||||
|
||||
public Set<K> keys() {
|
||||
return binding.keys();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return binding.size();
|
||||
}
|
||||
|
||||
public Set<V> values() {
|
||||
return binding.values();
|
||||
}
|
||||
|
||||
public ArrayList<Entry<K, V>> entries() {
|
||||
return binding.entries();
|
||||
}
|
||||
|
||||
public TLMap<V, K> flip() {
|
||||
TLMap<V, K> map = new TLMap<>();
|
||||
map.binding = binding.flip();
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TLMap<?, ?> tlMap = (TLMap<?, ?>) o;
|
||||
return tlMap.binding.entries.equals(binding.entries);
|
||||
}
|
||||
|
||||
protected static class Binding<K, V> {
|
||||
protected ArrayList<Entry<K, V>> entries = new ArrayList<>();
|
||||
|
||||
protected void set(K key, V value) {
|
||||
boolean exists = false;
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
Entry<K, V> entry = entries.get(i);
|
||||
if (key == entry.key || entry.key.equals(key)) {
|
||||
exists = true;
|
||||
if(value == null) {
|
||||
entries.remove(i);
|
||||
break;
|
||||
}
|
||||
entry.val = value;
|
||||
}
|
||||
}
|
||||
if(!exists && value != null && key != null) {
|
||||
this.entries.add(new Entry<>(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
protected V get(K key) {
|
||||
ArrayList<Entry<K, V>> entries = (ArrayList<Entry<K, V>>) this.entries.clone();
|
||||
for (Entry<K, V> entry : entries) {
|
||||
if (key == entry.key || entry.key.equals(key))
|
||||
return entry.val;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Set<K> keys() {
|
||||
Set<K> keys = new LinkedHashSet<>();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
keys.add(entries.get(i).key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
protected Set<V> values() {
|
||||
Set<V> vals = new LinkedHashSet<>();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
vals.add(entries.get(i).val);
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
protected ArrayList<Entry<K, V>> entries() {
|
||||
ArrayList<Entry<K, V>> vals = new ArrayList<>();
|
||||
vals.addAll(entries);
|
||||
return vals;
|
||||
}
|
||||
|
||||
protected Binding<V, K> flip() {
|
||||
Binding<V, K> binding = new Binding<>();
|
||||
for (int i = 0 ; i < entries.size() ; i++) {
|
||||
Entry<K, V> entry = entries.get(i);
|
||||
binding.entries.add(new Entry<>(entry.val, entry.key));
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Entry<K, V> {
|
||||
public K key;
|
||||
public V val;
|
||||
|
||||
protected Entry() {
|
||||
}
|
||||
|
||||
protected Entry(K key, V val) {
|
||||
this.key = key;
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Entry<?, ?> entry = (Entry<?, ?>) o;
|
||||
if (!Objects.equals(key, entry.key)) return false;
|
||||
return Objects.equals(val, entry.val);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TLMap<K, V> clone() {
|
||||
TLMap<K, V> n = new TLMap<>();
|
||||
Object[] keys = keys().toArray();
|
||||
Object[] vals = values().toArray();
|
||||
for (int i = 0, arrayLength = keys.length ; i < arrayLength ; i++) {
|
||||
Object key = keys[i];
|
||||
n.set((K)key, (V)vals[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
424
Loader/src/main/java/de/tudbut/parsing/JSON.java
Normal file
424
Loader/src/main/java/de/tudbut/parsing/JSON.java
Normal file
|
@ -0,0 +1,424 @@
|
|||
package de.tudbut.parsing;
|
||||
|
||||
import de.tudbut.tools.Stack;
|
||||
import de.tudbut.tools.StringTools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Interconverting JSON and TCN
|
||||
*/
|
||||
public class JSON {
|
||||
|
||||
/**
|
||||
* Converts a JSON string to a TCN object
|
||||
* @param string The JSON string, supports most compact and readable formats
|
||||
* @return The parsed TCN object
|
||||
* @throws JSONFormatException If a format error is found
|
||||
*/
|
||||
public static TCN read(String string) throws JSONFormatException {
|
||||
while (string.startsWith(" ")) {
|
||||
string = string.substring(1);
|
||||
}
|
||||
if(!string.startsWith("{") && !string.startsWith("[")) {
|
||||
throw new JSONFormatException("Expected: { or [ at 0 (String is '" + string + "')");
|
||||
}
|
||||
boolean array = string.startsWith("[");
|
||||
TCN tcn = new TCN("JSON", array);
|
||||
boolean escape = false;
|
||||
int pos = 1;
|
||||
char[] a = string.toCharArray();
|
||||
char c = a[pos];
|
||||
int arrayPos = 0;
|
||||
boolean inString = false;
|
||||
boolean startString = false;
|
||||
StringBuilder theString = new StringBuilder();
|
||||
boolean kv = false;
|
||||
String key = "";
|
||||
boolean inStringKV = false;
|
||||
boolean inObjectKV = false;
|
||||
TCN sub = null;
|
||||
|
||||
try {
|
||||
while (inString || (c != '}' && c != ']')) {
|
||||
if(array) {
|
||||
kv = true;
|
||||
}
|
||||
|
||||
if (startString) {
|
||||
inString = true;
|
||||
startString = false;
|
||||
}
|
||||
if (c == '\\') {
|
||||
escape = !escape;
|
||||
}
|
||||
if (!escape && c == '"') {
|
||||
startString = !inString;
|
||||
if (startString) {
|
||||
if (kv)
|
||||
inStringKV = true;
|
||||
theString = new StringBuilder();
|
||||
}
|
||||
else {
|
||||
inString = false;
|
||||
if (!kv) { // Key
|
||||
key = theString.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inString) {
|
||||
if (!escape)
|
||||
theString.append(c);
|
||||
else {
|
||||
a:
|
||||
{
|
||||
// Make escapes work
|
||||
if (c == 'n')
|
||||
theString.append('\n');
|
||||
if (c == 'r')
|
||||
theString.append('\r');
|
||||
if (c == 'u') {
|
||||
String e = "";
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 16));
|
||||
break a;
|
||||
}
|
||||
if (c == '0') {
|
||||
String e = "";
|
||||
e += c = a[pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 8));
|
||||
break a;
|
||||
}
|
||||
if (c == '1') {
|
||||
String e = "";
|
||||
e += c = a[pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 8));
|
||||
break a;
|
||||
}
|
||||
if (c == '2') {
|
||||
String e = "";
|
||||
e += c = a[pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 8));
|
||||
break a;
|
||||
}
|
||||
if (c == '3') {
|
||||
String e = "";
|
||||
e += c = a[pos];
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 8));
|
||||
break a;
|
||||
}
|
||||
if (c == 'x') {
|
||||
String e = "";
|
||||
e += c = a[++pos];
|
||||
e += c = a[++pos];
|
||||
theString.append((char) Integer.parseInt(e, 16));
|
||||
break a;
|
||||
}
|
||||
if (c == '"') {
|
||||
theString.append("\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Booleans, ints, etc
|
||||
else if (kv && !startString && !inStringKV && c != ',' && (Character.isLetterOrDigit(c) || c == '.' || c == '-')) {
|
||||
theString.append(c);
|
||||
}
|
||||
|
||||
// SubObjects
|
||||
if (!inString && c == '{') {
|
||||
inObjectKV = true;
|
||||
escape = false;
|
||||
theString = new StringBuilder("{");
|
||||
int layer = 1;
|
||||
while (layer > 0) {
|
||||
c = a[++pos];
|
||||
theString.append(c);
|
||||
if(c == '{' && !inString) {
|
||||
layer++;
|
||||
}
|
||||
if(c == '}' && !inString) {
|
||||
layer--;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
escape = !escape;
|
||||
}
|
||||
if (c == '\"' && !escape) {
|
||||
inString = !inString;
|
||||
}
|
||||
if (c != '\\') {
|
||||
escape = false;
|
||||
}
|
||||
}
|
||||
theString.append("}");
|
||||
sub = read(theString.toString());
|
||||
theString = new StringBuilder();
|
||||
}
|
||||
// Arrays
|
||||
if (!inString && c == '[') {
|
||||
inObjectKV = true;
|
||||
escape = false;
|
||||
theString = new StringBuilder("[");
|
||||
int layer = 1;
|
||||
while (layer != 0) {
|
||||
c = a[++pos];
|
||||
theString.append(c);
|
||||
if(c == '[' && !inString) {
|
||||
layer++;
|
||||
}
|
||||
if(c == ']' && !inString) {
|
||||
layer--;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
escape = !escape;
|
||||
}
|
||||
if (c == '\"' && !escape) {
|
||||
inString = !inString;
|
||||
}
|
||||
if (c != '\\') {
|
||||
escape = false;
|
||||
}
|
||||
}
|
||||
theString.append(']');
|
||||
sub = read(theString.toString());
|
||||
theString = new StringBuilder();
|
||||
}
|
||||
|
||||
|
||||
if(array) {
|
||||
kv = true;
|
||||
}
|
||||
// Key vs Value parsing
|
||||
if (!inString && c == ':') {
|
||||
theString = new StringBuilder();
|
||||
if (!kv)
|
||||
kv = true;
|
||||
else
|
||||
throw new JSONFormatException("Unexpected: '" + c + "' at " + pos + " - Should be ','");
|
||||
}
|
||||
if (!inString && c == ',') {
|
||||
if(array)
|
||||
key = String.valueOf(arrayPos++);
|
||||
|
||||
if (inObjectKV) {
|
||||
tcn.set(key, sub);
|
||||
} else if(inStringKV || !theString.toString().equals("null")) {
|
||||
tcn.set(key, theString.toString());
|
||||
}
|
||||
inObjectKV = false;
|
||||
inStringKV = false;
|
||||
theString = new StringBuilder();
|
||||
if (kv)
|
||||
kv = false;
|
||||
else
|
||||
throw new JSONFormatException("Unexpected: '" + c + "' at " + pos + " - Should be ':'");
|
||||
|
||||
}
|
||||
|
||||
if (c != '\\') {
|
||||
escape = false;
|
||||
}
|
||||
|
||||
c = a[++pos];
|
||||
}
|
||||
if(kv) {
|
||||
if (array)
|
||||
key = String.valueOf(arrayPos);
|
||||
if (inObjectKV)
|
||||
tcn.set(key, sub);
|
||||
else if(inStringKV || !theString.toString().equals("null"))
|
||||
tcn.set(key, theString.toString());
|
||||
}
|
||||
|
||||
|
||||
for (String theKey : tcn.map.keys()) {
|
||||
TCN.deepConvert(theKey, tcn.get(theKey), tcn);
|
||||
}
|
||||
return tcn;
|
||||
} catch (JSONFormatException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new JSONFormatException("At " + pos + " in " + string + " (Debug: " + inString + " " + kv + " " + theString + " " + key + " " + array + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String indent(boolean b, int i, int il) {
|
||||
if(b)
|
||||
return StringTools.multiply(StringTools.multiply(" ", il), i);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string, uses the most compact form
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String write(TCN tcn) {
|
||||
return write(tcn, false, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @param spaces If the JSON string should have spaces after : and ,
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String write(TCN tcn, boolean spaces) {
|
||||
return write(tcn, false, spaces, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string, uses newlines with selected indent and without spaces
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @param indent The indent to use
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String write(TCN tcn, int indent) {
|
||||
return write(tcn, true, false, indent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string, uses newlines with an indent of 2 and spaces
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String writeReadable(TCN tcn) {
|
||||
return write(tcn, true, true, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string, uses newlines with selected indent and spaces
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @param indent The indent to use
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String writeReadable(TCN tcn, int indent) {
|
||||
return write(tcn, true, true, indent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN object to a JSON string
|
||||
* @param tcn The TCN to write to JSON
|
||||
* @param newlines If the JSON string should have newlines and indents
|
||||
* @param spaces If the JSON string should have spaces after : and ,
|
||||
* @param indentLength The indent to use
|
||||
* @return The JSON string
|
||||
*/
|
||||
public static String write(TCN tcn, boolean newlines, boolean spaces, int indentLength) {
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(tcn.isArray ? "[" : "{").append(newlines ? "\n" : "");
|
||||
int i = 1;
|
||||
|
||||
ArrayList<Stack<String>> paths = new ArrayList<>();
|
||||
Stack<TCN> tcnStack = new Stack<>();
|
||||
Stack<String> path = new Stack<>();
|
||||
tcnStack.add(tcn);
|
||||
path.add("");
|
||||
while (tcnStack.size() > 0) {
|
||||
boolean b = false;
|
||||
for(String key : tcnStack.peek().map.keys()) {
|
||||
Object o = tcnStack.peek().map.get(key);
|
||||
|
||||
if(o == null)
|
||||
continue;
|
||||
|
||||
String k = key.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n").replaceAll("\"", "\\\\\"");
|
||||
if(o.getClass() == TCN.class) {
|
||||
path.add(key);
|
||||
if(!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
TCN theTCN = tcnStack.peek();
|
||||
tcnStack.add((TCN) o);
|
||||
if(theTCN.isArray) {
|
||||
s.append(indent(newlines, i, indentLength)).append("{").append(newlines ? "\n" : "");
|
||||
}
|
||||
else
|
||||
s.append(indent(newlines, i, indentLength)).append("\"").append(k).append("\":").append(spaces ? " " : "").append("{").append(newlines ? "\n" : "");
|
||||
b = true;
|
||||
i++;
|
||||
} else
|
||||
path.next();
|
||||
} else if(o.getClass() == TCNArray.class) {
|
||||
path.add(key);
|
||||
if(!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
TCN theTCN = tcnStack.peek();
|
||||
tcnStack.add(((TCNArray) o).toTCN());
|
||||
if(theTCN.isArray) {
|
||||
s.append(indent(newlines, i, indentLength)).append("[").append(newlines ? "\n" : "");
|
||||
}
|
||||
else
|
||||
s.append(indent(newlines, i, indentLength)).append("\"").append(k).append("\":").append(spaces ? " " : "").append("[").append(newlines ? "\n" : "");
|
||||
i++;
|
||||
b = true;
|
||||
} else
|
||||
path.next();
|
||||
} else if (o instanceof String) {
|
||||
path.add(key);
|
||||
if(!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
String val = o.toString().replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r").replaceAll("\"", "\\\\\"");
|
||||
if(tcnStack.peek().isArray) {
|
||||
s.append(indent(newlines, i, indentLength)).append("\"").append(val).append("\",").append(spaces ? " " : "").append(newlines ? "\n" : "");
|
||||
}
|
||||
else
|
||||
s.append(indent(newlines, i, indentLength)).append("\"").append(k).append("\":").append(spaces ? " \"" : "\"").append(val).append("\",").append(spaces ? " " : "").append(newlines ? "\n" : "");
|
||||
b = true;
|
||||
}
|
||||
path.next();
|
||||
}
|
||||
else {
|
||||
path.add(key);
|
||||
if(!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
String val = o.toString();
|
||||
if(tcnStack.peek().isArray) {
|
||||
s.append(indent(newlines, i, indentLength)).append(val).append(",").append(spaces ? " " : "").append(newlines ? "\n" : "");
|
||||
}
|
||||
else
|
||||
s.append(indent(newlines, i, indentLength)).append("\"").append(k).append("\":").append(spaces ? " " : "").append(val).append(",").append(spaces ? " " : "").append(newlines ? "\n" : "");
|
||||
b = true;
|
||||
}
|
||||
path.next();
|
||||
}
|
||||
}
|
||||
if(!b) {
|
||||
TCN theTCN = tcnStack.next();
|
||||
path.next();
|
||||
i--;
|
||||
if(theTCN.map.keys().isEmpty()) {
|
||||
s.append(",").append(spaces ? " " : "");
|
||||
}
|
||||
s.delete(s.length() - ((newlines ? 2 : 1) + (spaces ? 1 : 0)), s.length());
|
||||
s.append(newlines ? "\n" : "").append(indent(newlines, i, indentLength)).append(theTCN.isArray ? "]" : "}").append(",").append(spaces ? " " : "").append(newlines ? "\n" : "");
|
||||
}
|
||||
}
|
||||
s.delete(s.length() - ((newlines ? 2 : 1) + (spaces ? 1 : 0)), s.length());
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static class JSONFormatException extends Exception {
|
||||
public JSONFormatException(String s, Throwable e) {
|
||||
super(s, e);
|
||||
}
|
||||
public JSONFormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
}
|
513
Loader/src/main/java/de/tudbut/parsing/TCN.java
Normal file
513
Loader/src/main/java/de/tudbut/parsing/TCN.java
Normal file
|
@ -0,0 +1,513 @@
|
|||
package de.tudbut.parsing;
|
||||
|
||||
|
||||
import de.tudbut.tools.Tools;
|
||||
import de.tudbut.obj.DoubleTypedObject;
|
||||
import de.tudbut.obj.TLMap;
|
||||
import de.tudbut.tools.Stack;
|
||||
import de.tudbut.tools.StringTools;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* T udbuT<br>
|
||||
* C onfig<br>
|
||||
* N otation<br>
|
||||
*/
|
||||
public class TCN {
|
||||
|
||||
/**
|
||||
* The map
|
||||
*/
|
||||
public TLMap<String, Object> map = new TLMap<>();
|
||||
public final boolean isArray;
|
||||
public String type;
|
||||
|
||||
/**
|
||||
* Creates a new, empty TCN
|
||||
*/
|
||||
public TCN() {
|
||||
isArray = false;
|
||||
this.type = "TCN";
|
||||
}
|
||||
|
||||
TCN(boolean array) {
|
||||
isArray = array;
|
||||
this.type = "TCN";
|
||||
}
|
||||
|
||||
public TCN(String type) {
|
||||
isArray = false;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
TCN(String type, boolean array) {
|
||||
isArray = array;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets something in the map
|
||||
* @param key Key
|
||||
* @param o Object, can be a native type, string, or another TCN (or TCNArray)
|
||||
*/
|
||||
public void set(String key, Object o) {
|
||||
/*TLMap<String, Object> map = this.map;
|
||||
ArrayList<String> path = new ArrayList<>(Arrays.asList(key.split("#")));
|
||||
|
||||
while (path.size() > 1) {
|
||||
map = ((TCN) map.get(path.remove(0))).map;
|
||||
}
|
||||
*/
|
||||
map.set(key, o);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
Object o = map.get(key);
|
||||
if(o != null)
|
||||
return get(key).toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Short getShort(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Short.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getInteger(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Integer.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Boolean.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Float getFloat(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Float.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLong(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Long.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Double getDouble(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Double.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public TCN getSub(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null && o.getClass() == TCN.class)
|
||||
return (TCN) map.get(key);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public TCNArray getArray(String key) {
|
||||
Object o = get(key);
|
||||
if(o != null && o.getClass() == TCNArray.class)
|
||||
return (TCNArray) map.get(key);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object get(String key) {
|
||||
TLMap<String, Object> map = this.map;
|
||||
ArrayList<String> path = new ArrayList<>(Collections.singletonList(key));
|
||||
|
||||
while (path.size() > 1) {
|
||||
map = ((TCN) map.get(path.remove(0))).map;
|
||||
}
|
||||
|
||||
return map.get(path.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Map to a TCN
|
||||
* @param map The map to convert
|
||||
* @return The converted TCN
|
||||
*/
|
||||
public static TCN readMap(Map<String, String> map) {
|
||||
TCN tcn = new TCN(map.containsKey("TCN%isArray") && Boolean.parseBoolean(map.get("TCN%isArray")));
|
||||
|
||||
map.remove("TCN%isArray");
|
||||
|
||||
String[] array = map.keySet().toArray(new String[0]);
|
||||
for (int i = 0, arrayLength = array.length; i < arrayLength; i++) {
|
||||
String key = array[i];
|
||||
String s = map.get(key);
|
||||
|
||||
if(s.contains(":")) {
|
||||
tcn.map.set(key, TCN.readMap(Tools.stringToMap(s)));
|
||||
}
|
||||
else {
|
||||
tcn.map.set(key, s.replaceAll("%C", ":").replaceAll("%P", "%"));
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : tcn.map.keys()) {
|
||||
deepConvert(key, tcn.get(key), tcn);
|
||||
}
|
||||
|
||||
return tcn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this TCN object to a Map
|
||||
* {@link #readMap}
|
||||
* @return The converted Map
|
||||
*/
|
||||
public Map<String, String> toMap() {
|
||||
Map<String, String> r = new LinkedHashMap<>();
|
||||
|
||||
if(isArray)
|
||||
r.put("TCN%isArray", "true");
|
||||
|
||||
String[] array = map.keys().toArray(new String[0]);
|
||||
for (int i = 0, arrayLength = array.length; i < arrayLength; i++) {
|
||||
String key = array[i];
|
||||
Object o = map.get(key);
|
||||
|
||||
if(o == null)
|
||||
continue;
|
||||
|
||||
if(o.getClass() == TCN.class) {
|
||||
r.put(key, Tools.mapToString(((TCN) o).toMap()));
|
||||
}
|
||||
else if(o.getClass() == TCNArray.class) {
|
||||
r.put(key, Tools.mapToString(((TCNArray) o).toMap()));
|
||||
}
|
||||
else
|
||||
r.put(key, o.toString().replaceAll("%", "%P").replaceAll(":", "%C"));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this TCN to a reversible string
|
||||
* @return The converted string
|
||||
*/
|
||||
public String toString() {
|
||||
if(type.equalsIgnoreCase("TCN")) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int i = 0;
|
||||
|
||||
ArrayList<Stack<String>> paths = new ArrayList<>();
|
||||
Stack<TCN> tcnStack = new Stack<>();
|
||||
Stack<String> path = new Stack<>();
|
||||
tcnStack.add(this);
|
||||
path.add("");
|
||||
while (tcnStack.size() > 0) {
|
||||
boolean b = false;
|
||||
for (String key : tcnStack.peek().map.keys()) {
|
||||
Object o = tcnStack.peek().map.get(key);
|
||||
|
||||
if (o == null)
|
||||
continue;
|
||||
|
||||
String k = key.replaceAll("%", "%P").replaceAll(":", "%C").replaceAll("\n", "%N");
|
||||
if (k.startsWith(" ")) {
|
||||
if (!k.equals(" ")) {
|
||||
k = "%S" + k.substring(1);
|
||||
} else
|
||||
k = "%S";
|
||||
}
|
||||
if (k.startsWith("#")) {
|
||||
if (!k.equals("#")) {
|
||||
k = "%H" + k.substring(1);
|
||||
} else
|
||||
k = "%H";
|
||||
}
|
||||
if (o.getClass() == TCN.class) {
|
||||
path.add(key);
|
||||
if (!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
TCN tcn = tcnStack.peek();
|
||||
tcnStack.add((TCN) o);
|
||||
String indent = StringTools.multiply(" ", i);
|
||||
if (tcn.isArray) {
|
||||
s.append("\n").append(indent).append(";").append(((TCN) o).isArray ? " [\n" : " {\n");
|
||||
} else
|
||||
s.append("\n").append(indent).append(k).append(((TCN) o).isArray ? " [\n" : " {\n");
|
||||
i++;
|
||||
b = true;
|
||||
break;
|
||||
} else
|
||||
path.next();
|
||||
} else if (o.getClass() == TCNArray.class) {
|
||||
path.add(key);
|
||||
if (!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
TCN tcn = tcnStack.peek();
|
||||
tcnStack.add(((TCNArray) o).toTCN());
|
||||
String indent = StringTools.multiply(" ", i);
|
||||
if (tcn.isArray) {
|
||||
s.append("\n").append(indent).append(";").append(" [\n");
|
||||
} else
|
||||
s.append("\n").append(indent).append(k).append(" [\n");
|
||||
i++;
|
||||
b = true;
|
||||
break;
|
||||
} else
|
||||
path.next();
|
||||
} else {
|
||||
path.add(key);
|
||||
if (!paths.contains(path)) {
|
||||
paths.add(path.clone());
|
||||
String indent = StringTools.multiply(" ", i);
|
||||
String val = o.toString().replaceAll("%", "%P").replaceAll("\n", "%N");
|
||||
if (tcnStack.peek().isArray) {
|
||||
s.append(indent).append("; ").append(val).append("\n");
|
||||
} else
|
||||
s.append(indent).append(k).append(": ").append(val).append("\n");
|
||||
b = true;
|
||||
}
|
||||
path.next();
|
||||
}
|
||||
}
|
||||
if (!b) {
|
||||
TCN tcn = tcnStack.next();
|
||||
path.next();
|
||||
i--;
|
||||
String indent = StringTools.multiply(" ", i);
|
||||
s.append(indent).append(tcn.isArray ? "]\n\n" : "}\n\n");
|
||||
}
|
||||
}
|
||||
try {
|
||||
s.setLength(s.length() - "\n#\n\n".length());
|
||||
s.trimToSize();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
}
|
||||
else if(type.equalsIgnoreCase("JSON")) {
|
||||
return JSON.write(this);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string {@link #toString()} to a TCN object
|
||||
* @param s The string
|
||||
* @return The converted TCN
|
||||
* @throws TCNException If a format error occurs
|
||||
*/
|
||||
public static TCN read(String s) throws TCNException {
|
||||
TCN tcn = new TCN();
|
||||
|
||||
Map<DoubleTypedObject<Stack<String>, Stack<Integer>>, String> scanned = deepScan(s);
|
||||
Set<DoubleTypedObject<Stack<String>, Stack<Integer>>> keys = scanned.keySet();
|
||||
for (DoubleTypedObject<Stack<String>, Stack<Integer>> path : keys) {
|
||||
deepPut(path.clone(), tcn, scanned.get(path));
|
||||
}
|
||||
for (String key : tcn.map.keys()) {
|
||||
deepConvert(key, tcn.get(key), tcn);
|
||||
}
|
||||
|
||||
return tcn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts stray arrays to TCNArray objects, recursive.<br>
|
||||
* <pre>Example:{@code
|
||||
* for (String key : tcn.map.keys()) {
|
||||
* deepConvert(key, tcn.get(key), tcn);
|
||||
* }
|
||||
* }</pre>
|
||||
* @param key The key of o
|
||||
* @param o Any value in a TCN
|
||||
* @param top The TCN that o is embedded in
|
||||
*/
|
||||
public static void deepConvert(String key, Object o, TCN top) {
|
||||
if(!(o instanceof TCN) && !(o instanceof TCNArray))
|
||||
return;
|
||||
TCN tcn;
|
||||
if(o instanceof TCN) {
|
||||
tcn = (TCN) o;
|
||||
}
|
||||
else
|
||||
tcn = ((TCNArray) o).toTCN();
|
||||
|
||||
for (String theKey : tcn.map.keys()) {
|
||||
deepConvert(theKey, tcn.get(theKey), tcn);
|
||||
}
|
||||
|
||||
if (tcn.isArray) {
|
||||
top.set(key, TCNArray.fromTCN(tcn));
|
||||
}
|
||||
}
|
||||
|
||||
private static void deepPut(DoubleTypedObject<Stack<String>, Stack<Integer>> path, TCN tcn, String value) throws TCNException {
|
||||
try {
|
||||
if (path.o.size() == 1) {
|
||||
tcn.map.set(path.o.next(), value);
|
||||
}
|
||||
else {
|
||||
int arrayPos = path.t.popBottom();
|
||||
TCN toPut = (TCN) tcn.map.get(path.o.getBottom(), () -> new TCN(arrayPos != -1));
|
||||
tcn.map.set(path.o.popBottom(), toPut);
|
||||
deepPut(path, toPut, value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new TCNException(null, pathToString(path.o) + ": " + value, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<DoubleTypedObject<Stack<String>, Stack<Integer>>, String> deepScan(String s) throws TCNException {
|
||||
String[] lines = s.split("\n");
|
||||
Map<DoubleTypedObject<Stack<String>, Stack<Integer>>, String> map = new LinkedHashMap<>();
|
||||
|
||||
Stack<Boolean> array = new Stack<>();
|
||||
array.add(false);
|
||||
Stack<Integer> arrayPos = new Stack<>();
|
||||
Stack<String> path = new Stack<>();
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
try {
|
||||
String line = removePrefixSpaces(lines[i]);
|
||||
if (!line.isEmpty() && !line.startsWith("#")) {
|
||||
if (line.equals("}") || line.equals("]")) {
|
||||
path.next();
|
||||
array.next();
|
||||
arrayPos.next();
|
||||
}
|
||||
else if (line.endsWith(" {") && !line.contains(": ")) {
|
||||
String k = line.split(" \\{")[0].replaceAll("%C", ":").replaceAll("%N", "\n");
|
||||
if (k.startsWith("%S")) {
|
||||
if (!k.equals("%S"))
|
||||
k = " " + k.substring(2);
|
||||
else
|
||||
k = " ";
|
||||
}
|
||||
if (k.startsWith("%H")) {
|
||||
if (!k.equals("%H"))
|
||||
k = "#" + k.substring(2);
|
||||
else
|
||||
k = "#";
|
||||
}
|
||||
if(!array.peek())
|
||||
path.add(k.replaceAll("%P", "%"));
|
||||
else {
|
||||
path.add(arrayPos.peek() + "");
|
||||
arrayPos.add(arrayPos.next() + 1);
|
||||
}
|
||||
array.add(false);
|
||||
arrayPos.add(-1);
|
||||
}
|
||||
else if (line.endsWith(" [") && !line.contains(": ")) {
|
||||
String k = line.split(" \\[")[0].replaceAll("%C", ":").replaceAll("%N", "\n");
|
||||
if (k.startsWith("%S")) {
|
||||
if (!k.equals("%S"))
|
||||
k = " " + k.substring(2);
|
||||
else
|
||||
k = " ";
|
||||
}
|
||||
if (k.startsWith("%H")) {
|
||||
if (!k.equals("%H"))
|
||||
k = "#" + k.substring(2);
|
||||
else
|
||||
k = "#";
|
||||
}
|
||||
if(!array.peek())
|
||||
path.add(k.replaceAll("%P", "%"));
|
||||
else {
|
||||
path.add(arrayPos.peek() + "");
|
||||
arrayPos.add(arrayPos.next() + 1);
|
||||
}
|
||||
array.add(true);
|
||||
arrayPos.add(0);
|
||||
}
|
||||
else {
|
||||
Stack<String> p = path.clone();
|
||||
if(array.peek()) {
|
||||
p.add(String.valueOf(arrayPos.peek()));
|
||||
if(line.equals(";"))
|
||||
line = "; ";
|
||||
map.put(new DoubleTypedObject<>(p, arrayPos.clone()), line.substring(2).replaceAll("%N", "\n").replaceAll("%P", "%"));
|
||||
arrayPos.add(arrayPos.next() + 1);
|
||||
}
|
||||
else {
|
||||
String rawk = line.split(": ")[0].replaceAll("%C", ":").replaceAll("%N", "\n");
|
||||
String k = rawk;
|
||||
if (k.startsWith("%S")) {
|
||||
if (!k.equals("%S"))
|
||||
k = " " + k.substring(2);
|
||||
else
|
||||
k = " ";
|
||||
}
|
||||
if (k.startsWith("%H")) {
|
||||
if (!k.equals("%H"))
|
||||
k = "#" + k.substring(2);
|
||||
else
|
||||
k = "#";
|
||||
}
|
||||
p.add(k.replaceAll("%P", "%"));
|
||||
map.put(new DoubleTypedObject<>(p, arrayPos.clone()), line.substring(rawk.length() + 2).replaceAll("%N", "\n").replaceAll("%P", "%"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new TCNException(i, lines[i], e);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static String pathToString(Stack<String> path) {
|
||||
StringBuilder s = new StringBuilder("/");
|
||||
path = path.clone();
|
||||
while (path.hasNext()) {
|
||||
s.append(path.popBottom()).append("/");
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private static String removePrefixSpaces(String s) {
|
||||
while (s.startsWith(" "))
|
||||
s = s.substring(1);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, empty TCN object
|
||||
* @deprecated Use {@code new} {@link #TCN()}
|
||||
* @return The new TCN
|
||||
*/
|
||||
@Deprecated
|
||||
public static TCN getEmpty() {
|
||||
return new TCN();
|
||||
}
|
||||
|
||||
public static class TCNException extends Exception {
|
||||
|
||||
public TCNException(Integer line, String lineString, Exception e) {
|
||||
super("Error in line " + line + " (" + lineString + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
127
Loader/src/main/java/de/tudbut/parsing/TCNArray.java
Normal file
127
Loader/src/main/java/de/tudbut/parsing/TCNArray.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package de.tudbut.parsing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TCN-Compatible Arrays
|
||||
*/
|
||||
public class TCNArray extends ArrayList<Object> {
|
||||
|
||||
/**
|
||||
* Creates a new, empty TCNArray
|
||||
*/
|
||||
public TCNArray() { }
|
||||
/**
|
||||
* Creates a new TCNArray from a collection
|
||||
*/
|
||||
public TCNArray(Collection<?> collection) {
|
||||
addAll(collection);
|
||||
}
|
||||
|
||||
public String getString(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return o.toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Short getShort(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Short.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getInteger(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Integer.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Boolean.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Float getFloat(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Float.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLong(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Long.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public Double getDouble(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null)
|
||||
return Double.valueOf(String.valueOf(o));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public TCN getSub(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null && o.getClass() == TCN.class)
|
||||
return (TCN) o;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public TCNArray getArray(int key) {
|
||||
Object o = get(key);
|
||||
if(o != null && o.getClass() == TCNArray.class)
|
||||
return (TCNArray) o;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return a TCN object from this TCNArray, this is the only way to get a TCN with {@link TCN#isArray} true
|
||||
*/
|
||||
public TCN toTCN() {
|
||||
TCN tcn = new TCN(true);
|
||||
for (int i = 0 ; i < this.size() ; i++) {
|
||||
tcn.set(String.valueOf(i), get(i));
|
||||
}
|
||||
return tcn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TCN to a TCNArray
|
||||
* @param tcn The TCN to convert from
|
||||
* @return The created TCNArray
|
||||
*/
|
||||
public static TCNArray fromTCN(TCN tcn) {
|
||||
TCNArray array = new TCNArray();
|
||||
for (String key : tcn.map.keys()) {
|
||||
array.add(tcn.get(key));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the TCNArray to a TCN, then maps it
|
||||
* @return the created map
|
||||
*/
|
||||
public Map<String, String> toMap() {
|
||||
return toTCN().toMap();
|
||||
}
|
||||
}
|
113
Loader/src/main/java/de/tudbut/security/AccessKiller.java
Normal file
113
Loader/src/main/java/de/tudbut/security/AccessKiller.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AccessKiller {
|
||||
|
||||
private static Field getField(Field f) {
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
}
|
||||
|
||||
private static Object getReflectionData(Class<?> clazz) throws ReflectiveOperationException {
|
||||
// make sure ReflectionData is populated
|
||||
clazz.getDeclaredMethods();
|
||||
clazz.getDeclaredFields();
|
||||
clazz.getDeclaredConstructors();
|
||||
clazz.getInterfaces();
|
||||
|
||||
SoftReference<?> data = (SoftReference<?>) getField(Class.class.getDeclaredField("reflectionData")).get(clazz);
|
||||
Object reflectionData = data.get();
|
||||
assert reflectionData != null;
|
||||
return reflectionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING!!! Can only erase private fields!!
|
||||
* @param clazz Class to kill fields of
|
||||
* @param fieldsToKill Field names to kill, or empty to kill all.
|
||||
*/
|
||||
public static void killFieldAccess(Class<?> clazz, String... fieldsToKill) {
|
||||
List<String> toKill = Arrays.asList(fieldsToKill);
|
||||
try {
|
||||
Object reflectionData = getReflectionData(clazz);
|
||||
Field data = getField(reflectionData.getClass().getDeclaredField("declaredFields"));
|
||||
List<Field> fields = new ArrayList<>(Arrays.asList((Field[]) data.get(reflectionData)));
|
||||
if(!toKill.isEmpty()) {
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
if (toKill.contains(fields.get(i).getName()))
|
||||
fields.remove(i--);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fields.clear();
|
||||
}
|
||||
data.set(reflectionData, fields.toArray(new Field[0]));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING!!! Can only erase private methods!!
|
||||
* @param clazz Class to kill methods of
|
||||
* @param methodsToKill Method names to kill, or empty to kill all.
|
||||
*/
|
||||
public static void killMethodAccess(Class<?> clazz, String... methodsToKill) {
|
||||
List<String> toKill = Arrays.asList(methodsToKill);
|
||||
try {
|
||||
Object reflectionData = getReflectionData(clazz);
|
||||
Field data = getField(reflectionData.getClass().getDeclaredField("declaredMethods"));
|
||||
List<Method> methods = new ArrayList<>(Arrays.asList((Method[]) data.get(reflectionData)));
|
||||
if(!toKill.isEmpty()) {
|
||||
for (int i = 0; i < methods.size(); i++) {
|
||||
if (toKill.contains(methods.get(i).getName()))
|
||||
methods.remove(i--);
|
||||
}
|
||||
}
|
||||
else {
|
||||
methods.clear();
|
||||
}
|
||||
data.set(reflectionData, methods.toArray(new Method[0]));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING!!! Can only erase private constructors!!
|
||||
* @param clazz Class to kill constructors of
|
||||
*/
|
||||
public static void killConstructorAccess(Class<?> clazz) {
|
||||
try {
|
||||
Object reflectionData = getReflectionData(clazz);
|
||||
Field data = getField(reflectionData.getClass().getDeclaredField("declaredConstructors"));
|
||||
data.set(reflectionData, new Constructor<?>[0]);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void killReflectionFor(Class<?>... classes) {
|
||||
for (Class<?> clazz : classes) {
|
||||
killConstructorAccess(clazz);
|
||||
killMethodAccess(clazz);
|
||||
killFieldAccess(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any code from making further changes to reflectionData.
|
||||
* This also stops any further AccessKiller calls. <br>
|
||||
* Use with EXTREME caution!!
|
||||
*/
|
||||
public static void killClassReflection() {
|
||||
killReflectionFor(Class.class);
|
||||
}
|
||||
}
|
120
Loader/src/main/java/de/tudbut/security/DataKeeper.java
Normal file
120
Loader/src/main/java/de/tudbut/security/DataKeeper.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
import de.tudbut.obj.DoubleTypedObject;
|
||||
import de.tudbut.tools.Lock;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
// Keeps some data as safe as possible, unable to be accessed even by reflection
|
||||
public class DataKeeper<T> {
|
||||
public static boolean forgetAll = false;
|
||||
|
||||
|
||||
private final PermissionManager permissionManager;
|
||||
private Supplier<T> dataInsertion;
|
||||
private final Strictness strictness;
|
||||
private final Lock lock = new Lock(true);
|
||||
private final Queue<DoubleTypedObject<Consumer<Accessor<T>>, Lock>> nextFunctionToRun = new LinkedList<>();
|
||||
private final Thread keeper = new Thread(this::keep, "DataKeeper"); { keeper.start(); }
|
||||
|
||||
static { initSecurity(); }
|
||||
|
||||
private static void initSecurity() {
|
||||
// this should prevent any reflection, but is not a 100% guarantee!
|
||||
AccessKiller.killReflectionFor(DataKeeper.class, Accessor.class);
|
||||
}
|
||||
|
||||
public DataKeeper(PermissionManager permissionManager, Strictness strictness, T toKeep) {
|
||||
// make sure reflection is killed for it
|
||||
permissionManager.killReflection();
|
||||
|
||||
this.permissionManager = permissionManager;
|
||||
dataInsertion = () -> toKeep;
|
||||
this.strictness = strictness;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public Strictness getStrictness() {
|
||||
return strictness;
|
||||
}
|
||||
|
||||
public void access(Consumer<Accessor<T>> accessor) {
|
||||
if(!permissionManager.checkCaller(strictness)) {
|
||||
if(permissionManager.showErrors())
|
||||
throw new IllegalAccessError("The active PermissionManager does not allow you to access this DataKeeper.");
|
||||
}
|
||||
Lock waitLock = new Lock(true);
|
||||
nextFunctionToRun.add(new DoubleTypedObject<>(accessor, waitLock));
|
||||
lock.unlock();
|
||||
waitLock.waitHere(500);
|
||||
}
|
||||
|
||||
private void keep() {
|
||||
lock.waitHere();
|
||||
lock.lock();
|
||||
PermissionManager permissionManager = this.permissionManager;
|
||||
AtomicReference<T> data = new AtomicReference<>(dataInsertion.get());
|
||||
Strictness strictness = this.strictness;
|
||||
dataInsertion = null;
|
||||
while(!forgetAll) {
|
||||
lock.waitHere();
|
||||
lock.lock(500);
|
||||
|
||||
DoubleTypedObject<Consumer<Accessor<T>>, Lock> itm = nextFunctionToRun.poll();
|
||||
if(itm == null)
|
||||
continue;
|
||||
Consumer<Accessor<T>> toRun = itm.o;
|
||||
Lock lock = itm.t;
|
||||
// second layer of protection, crashes this time.
|
||||
if(!permissionManager.checkLambda(strictness, toRun))
|
||||
permissionManager.crash(strictness);
|
||||
|
||||
toRun.accept(new Accessor<>(permissionManager, strictness, data));
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// A very last, third layer of protection, not actually that necessary.
|
||||
public static class Accessor<T> {
|
||||
// The accessor will only ever be in local variables, so it does
|
||||
// not have to be reflection-safe. But it is anyway due to AccessKiller.
|
||||
private final PermissionManager permissionManager;
|
||||
private final AtomicReference<T> value;
|
||||
private final Strictness strictness;
|
||||
|
||||
public Accessor(PermissionManager permissionManager, Strictness strictness, AtomicReference<T> data) {
|
||||
this.permissionManager = permissionManager;
|
||||
this.strictness = strictness;
|
||||
value = data;
|
||||
}
|
||||
|
||||
public T setValue(T newValue) {
|
||||
// check is in getValue
|
||||
T old = getValue();
|
||||
value.set(newValue);
|
||||
return old;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
if(permissionManager.checkCaller(strictness))
|
||||
return value.get();
|
||||
else {
|
||||
// crash soon
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
permissionManager.crash(strictness);
|
||||
}).start();
|
||||
// generate a weird error
|
||||
return (T) value.get().getClass().cast(new Object());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
public class ExtendedStrictness implements Strictness {
|
||||
static {
|
||||
AccessKiller.killReflectionFor(ExtendedStrictness.class);
|
||||
}
|
||||
private final Strictness primary, secondary;
|
||||
|
||||
public ExtendedStrictness(Strictness primary, Strictness secondary) {
|
||||
this.primary = primary;
|
||||
this.secondary = secondary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRawProperty(String name) {
|
||||
if(primary.hasProperty(name))
|
||||
return primary.getRawProperty(name);
|
||||
return secondary.getRawProperty(name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface PermissionManager {
|
||||
boolean checkCaller(Strictness strictnessLevel);
|
||||
|
||||
<T> boolean checkLambda(Strictness strictnessLevel, T lambda);
|
||||
|
||||
default void crash(Strictness strictnessLevel) {
|
||||
DataKeeper.forgetAll = true;
|
||||
try {
|
||||
Class<?> shutdownClass = Class.forName("java.lang.Shutdown");
|
||||
Method exitMethod = shutdownClass.getDeclaredMethod("exit", int.class);
|
||||
exitMethod.setAccessible(true);
|
||||
exitMethod.invoke(null, 1);
|
||||
} catch (Exception ignored) {}
|
||||
System.exit(1);
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
default boolean showErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void killReflection() {
|
||||
Class<?> clazz = getClass();
|
||||
while(Arrays.stream(clazz.getInterfaces()).anyMatch(x -> x == PermissionManager.class)) {
|
||||
AccessKiller.killReflectionFor(clazz);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
}
|
30
Loader/src/main/java/de/tudbut/security/Strictness.java
Normal file
30
Loader/src/main/java/de/tudbut/security/Strictness.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
import de.tudbut.parsing.TCN;
|
||||
|
||||
public interface Strictness {
|
||||
|
||||
Object getRawProperty(String name);
|
||||
|
||||
default <T> T getProperty(String name) {
|
||||
return (T) getRawProperty(name);
|
||||
}
|
||||
default boolean getBoolProperty(String name) {
|
||||
Boolean b = getProperty(name);
|
||||
if(b == null)
|
||||
return false;
|
||||
return b;
|
||||
}
|
||||
default String getStringProperty(String name) {
|
||||
return getProperty(name);
|
||||
}
|
||||
default int getIntProperty(String name) {
|
||||
return getProperty(name);
|
||||
}
|
||||
default boolean hasProperty(String name) {
|
||||
return getRawProperty(name) != null;
|
||||
}
|
||||
default Strictness extend(Strictness newPrimary) {
|
||||
return new ExtendedStrictness(newPrimary, this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package de.tudbut.security;
|
||||
|
||||
import de.tudbut.parsing.TCN;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class StrictnessBuilder {
|
||||
static {
|
||||
AccessKiller.killReflectionFor(StrictnessBuilder.class);
|
||||
}
|
||||
|
||||
private final HashMap<String, Object> properties = new HashMap<>();
|
||||
|
||||
public static StrictnessBuilder create() {
|
||||
return new StrictnessBuilder();
|
||||
}
|
||||
|
||||
public static Strictness empty() {
|
||||
return new StrictnessBuilder().build();
|
||||
}
|
||||
|
||||
public StrictnessBuilder property(String s, Object o) {
|
||||
properties.put(s, o);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StrictnessBuilder fromTCN(TCN tcn) {
|
||||
properties.putAll(tcn.toMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
public StrictnessBuilder tryFromStrictness(Strictness strictness) {
|
||||
if(strictness instanceof StrictnessImpl) {
|
||||
properties.putAll((((StrictnessImpl) strictness).properties));
|
||||
return this;
|
||||
}
|
||||
// error
|
||||
return null;
|
||||
}
|
||||
|
||||
public Strictness build() {
|
||||
return new StrictnessImpl((HashMap<String, Object>) properties.clone());
|
||||
}
|
||||
|
||||
private static class StrictnessImpl implements Strictness {
|
||||
static {
|
||||
AccessKiller.killReflectionFor(StrictnessImpl.class);
|
||||
}
|
||||
|
||||
private final HashMap<String, Object> properties;
|
||||
|
||||
public StrictnessImpl(HashMap<String, Object> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRawProperty(String name) {
|
||||
return properties.get(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
import de.tudbut.security.Strictness;
|
||||
|
||||
public class AllowAllRestriction implements PermissionManager {
|
||||
@Override
|
||||
public boolean checkCaller(Strictness strictnessLevel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkLambda(Strictness strictnessLevel, T lambda) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
import de.tudbut.security.Strictness;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Supported strictness properties:
|
||||
* - Restriction.CallClass.MaxDistance (int): How far down the stack trace should the restriction look until it fails
|
||||
* - Restriction.CallClass.RestrictLambda (bool): If the restriction should apply to lambdas. If true, ONLY classes in the
|
||||
* allowlist pass, instead of allowing the allowed classes to call "through" others.
|
||||
*/
|
||||
public class CallClassRestriction extends Restriction {
|
||||
|
||||
private final Set<String> allow;
|
||||
|
||||
public CallClassRestriction(PermissionManager parent, Class<?>... allowFromClasses) {
|
||||
super(parent);
|
||||
allow = Collections.unmodifiableSet(Arrays.stream(allowFromClasses).map(Class::getName).collect(Collectors.toSet()));
|
||||
}
|
||||
public CallClassRestriction(Class<?>... allowFromClasses) {
|
||||
this(null, allowFromClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCaller(Strictness strictnessLevel) {
|
||||
StackTraceElement[] st = Thread.currentThread().getStackTrace();
|
||||
|
||||
if(strictnessLevel.hasProperty("Restriction.CallClass.MaxDistance")) {
|
||||
int maxDist = strictnessLevel.getIntProperty("Restriction.CallClass.MaxDistance");
|
||||
if(st.length > maxDist) {
|
||||
StackTraceElement[] elements = new StackTraceElement[maxDist];
|
||||
System.arraycopy(st, 0, elements, 0, maxDist);
|
||||
st = elements;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isCalledByAllowed = false;
|
||||
for (StackTraceElement element : st) {
|
||||
if(allow.contains(element.getClassName())) {
|
||||
isCalledByAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isCalledByAllowed && super.checkCaller(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkLambda(Strictness strictnessLevel, T lambda) {
|
||||
boolean b = true;
|
||||
if(strictnessLevel.getBoolProperty("Restriction.CallClass.RestrictLambda")) {
|
||||
// might get more complex soon.
|
||||
// is class, inner class of it, loaded by it, or lambda in it?
|
||||
Class<?> enclosingClass = lambda.getClass().getEnclosingClass();
|
||||
b = allow.contains(lambda.getClass().getName())
|
||||
|| allow.contains(lambda.getClass().getName().replaceAll("\\$\\$Lambda.*$", ""));
|
||||
if (enclosingClass != null)
|
||||
b = b || allow.contains(enclosingClass.getName());
|
||||
}
|
||||
return b && super.checkLambda(strictnessLevel, lambda);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
import de.tudbut.security.Strictness;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Only allows classes loaded by a certain class loader, and the classloader itself.
|
||||
*
|
||||
* Supported strictness properties:
|
||||
* - Restriction.ClassLoader.MaxDistance (int): How far down the stack trace should the restriction look until it fails
|
||||
* - Restriction.ClassLoader.RestrictLambda (bool): If the restriction should apply to lambdas. If true, ONLY classes in the
|
||||
* allowlist pass, instead of allowing the allowed classes to call "through" others.
|
||||
*/
|
||||
public class ClassLoaderRestriction extends Restriction {
|
||||
private final Set<Class<?>> allow;
|
||||
|
||||
public ClassLoaderRestriction(PermissionManager parent, Class<?>... allowFromClassLoaders) {
|
||||
super(parent);
|
||||
this.allow = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(allowFromClassLoaders)));
|
||||
}
|
||||
|
||||
public ClassLoaderRestriction(Class<?>... allowFromClassLoaders) {
|
||||
this(null, allowFromClassLoaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCaller(Strictness strictnessLevel) {
|
||||
StackTraceElement[] st = Thread.currentThread().getStackTrace();
|
||||
|
||||
if(strictnessLevel.hasProperty("Restriction.ClassLoader.MaxDistance")) {
|
||||
int maxDist = strictnessLevel.getIntProperty("Restriction.ClassLoader.MaxDistance");
|
||||
if(st.length > maxDist) {
|
||||
StackTraceElement[] elements = new StackTraceElement[maxDist];
|
||||
System.arraycopy(st, 0, elements, 0, maxDist);
|
||||
st = elements;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isCalledByAllowed = false;
|
||||
for (StackTraceElement element : st) {
|
||||
try {
|
||||
Class<?> cls = Class.forName(element.getClassName());
|
||||
// is the call the classloader or loaded by it?
|
||||
if(allow.contains(cls) || allow.contains(cls.getClassLoader().getClass())) {
|
||||
isCalledByAllowed = true;
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// it'll just stay false
|
||||
}
|
||||
}
|
||||
return isCalledByAllowed && super.checkCaller(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkLambda(Strictness strictnessLevel, T lambda) {
|
||||
boolean b = true;
|
||||
if(strictnessLevel.getBoolProperty("Restriction.ClassLoader.RestrictLambda")) {
|
||||
// might get more complex soon.
|
||||
// is classloader, inner class of it, or loaded by it?
|
||||
b = allow.contains(lambda.getClass())
|
||||
|| allow.contains(lambda.getClass().getClassLoader().getClass());
|
||||
Class<?> enclosingClass = lambda.getClass().getEnclosingClass();
|
||||
if (enclosingClass != null)
|
||||
b = b || allow.contains(enclosingClass);
|
||||
// is lambda in allowed class?
|
||||
String name = lambda.getClass().getName().replaceAll("\\$\\$Lambda.*$", "");
|
||||
for (Class<?> clazz : allow) {
|
||||
if (clazz.getName().equals(name)) {
|
||||
b = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
b = b || allow.contains(Class.forName(name).getClassLoader().getClass());
|
||||
} catch (Exception e) {
|
||||
// it'll just stay false
|
||||
}
|
||||
}
|
||||
return b && super.checkLambda(strictnessLevel, lambda);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
|
||||
public class HideErrorRestriction extends Restriction {
|
||||
public HideErrorRestriction(PermissionManager parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showErrors() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
import de.tudbut.security.Strictness;
|
||||
|
||||
public class PermissionOR implements PermissionManager {
|
||||
|
||||
private final PermissionManager primary, secondary;
|
||||
|
||||
public PermissionOR(PermissionManager primary, PermissionManager secondary) {
|
||||
this.primary = primary;
|
||||
this.secondary = secondary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCaller(Strictness strictnessLevel) {
|
||||
return primary.checkCaller(strictnessLevel) || secondary.checkCaller(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkLambda(Strictness strictnessLevel, T lambda) {
|
||||
return primary.checkLambda(strictnessLevel, lambda) || secondary.checkLambda(strictnessLevel, lambda);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void crash(Strictness strictnessLevel) {
|
||||
primary.crash(strictnessLevel);
|
||||
secondary.crash(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showErrors() {
|
||||
return primary.showErrors() || secondary.showErrors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killReflection() {
|
||||
PermissionManager.super.killReflection();
|
||||
primary.killReflection();
|
||||
secondary.killReflection();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package de.tudbut.security.permissionmanager;
|
||||
|
||||
import de.tudbut.security.PermissionManager;
|
||||
import de.tudbut.security.Strictness;
|
||||
|
||||
public abstract class Restriction implements PermissionManager {
|
||||
|
||||
protected final PermissionManager parent;
|
||||
|
||||
public Restriction(PermissionManager parent) {
|
||||
if(parent == null)
|
||||
parent = new AllowAllRestriction();
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCaller(Strictness strictnessLevel) {
|
||||
return parent.checkCaller(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkLambda(Strictness strictnessLevel, T lambda) {
|
||||
return parent.checkLambda(strictnessLevel, lambda);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void crash(Strictness strictnessLevel) {
|
||||
parent.crash(strictnessLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killReflection() {
|
||||
parent.killReflection();
|
||||
PermissionManager.super.killReflection();
|
||||
}
|
||||
}
|
255
Loader/src/main/java/de/tudbut/tools/ExtendedMath.java
Normal file
255
Loader/src/main/java/de/tudbut/tools/ExtendedMath.java
Normal file
|
@ -0,0 +1,255 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
public class ExtendedMath {
|
||||
public static long[] solveSimple(String eq, char toSolveChar, int maxResults) throws InterruptedException {
|
||||
eq = eq.replaceAll("=", "==").replaceAll("======", "==").replaceAll("====", "==");
|
||||
|
||||
|
||||
final boolean[] stop = {false};
|
||||
final long[] result = new long[maxResults];
|
||||
final char[] current = {0};
|
||||
|
||||
String finalEq = eq;
|
||||
new Thread(() -> {
|
||||
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
|
||||
for (long i = -1; i > Long.MIN_VALUE; i--) {
|
||||
if (stop[0])
|
||||
break;
|
||||
try {
|
||||
if (engine.eval("(" + finalEq.replaceAll(String.valueOf(toSolveChar), "(" + i + ")") + ")").equals(true)) {
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
else {
|
||||
result[current[0]] = i;
|
||||
current[0]++;
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
}
|
||||
if (stop[0])
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ScriptException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
String finalEq1 = eq;
|
||||
new Thread(() -> {
|
||||
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
|
||||
for (long i = 0; i < Long.MAX_VALUE; i++) {
|
||||
if (stop[0])
|
||||
break;
|
||||
try {
|
||||
if (engine.eval("(" + finalEq1.replaceAll(String.valueOf(toSolveChar), "(" + i + ")") + ")").equals(true)) {
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
else {
|
||||
result[current[0]] = i;
|
||||
current[0]++;
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
}
|
||||
if (stop[0])
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ScriptException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
while (!stop[0]) {
|
||||
Thread.sleep(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long solveSimple(String eq, char toSolveChar) throws InterruptedException {
|
||||
return solveSimple(eq, toSolveChar, 1)[0];
|
||||
}
|
||||
|
||||
public static double[] solveDouble(String eq, char toSolveChar, int precision, int maxResults) throws InterruptedException {
|
||||
eq = eq.replaceAll("=", "==").replaceAll("======", "==").replaceAll("====", "==");
|
||||
|
||||
|
||||
final boolean[] stop = {false};
|
||||
final double[] result = new double[maxResults];
|
||||
final char[] current = {0};
|
||||
|
||||
String finalEq = eq;
|
||||
new Thread(() -> {
|
||||
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
|
||||
for (long i = -1; i > Long.MIN_VALUE; i--) {
|
||||
if (stop[0])
|
||||
break;
|
||||
try {
|
||||
if (engine.eval(finalEq.replaceAll(String.valueOf(toSolveChar), "(" + (double) i / (double) precision + ")")).equals(true)) {
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
else {
|
||||
result[current[0]] = (double) i / (double) precision;
|
||||
current[0]++;
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
}
|
||||
if (stop[0])
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ScriptException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
String finalEq1 = eq;
|
||||
new Thread(() -> {
|
||||
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
|
||||
for (long i = 0; i < Long.MAX_VALUE; i++) {
|
||||
if (stop[0])
|
||||
break;
|
||||
try {
|
||||
if (engine.eval(finalEq1.replaceAll(String.valueOf(toSolveChar), "(" + (double) i / (double) precision + ")")).equals(true)) {
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
else {
|
||||
result[current[0]] = (double) i / (double) precision;
|
||||
current[0]++;
|
||||
if (current[0] == result.length)
|
||||
stop[0] = true;
|
||||
}
|
||||
if (stop[0])
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ScriptException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
while (!stop[0]) {
|
||||
Thread.sleep(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static double solveDouble(String eq, char toSolveChar, int precision) throws InterruptedException {
|
||||
return solveDouble(eq, toSolveChar, precision, 1)[0];
|
||||
}
|
||||
|
||||
public static double solveDouble(String eq, char toSolveChar) throws InterruptedException {
|
||||
return solveDouble(eq, toSolveChar, 100, 1)[0];
|
||||
}
|
||||
|
||||
public static double[] solveDouble(String eq, char toSolveChar, long maxResults) throws InterruptedException {
|
||||
return solveDouble(eq, toSolveChar, 100, (int) maxResults);
|
||||
}
|
||||
|
||||
public static int random(int lower, int upper) {
|
||||
return (int) randomLong(lower, upper);
|
||||
}
|
||||
|
||||
public static long randomLong(long lower, long upper) {
|
||||
upper ++;
|
||||
return (long) (Math.floor(Math.random() * (upper - lower)) + lower);
|
||||
}
|
||||
|
||||
public static float randomFloat(float lower, float upper, int precision) {
|
||||
return (float) randomLong((int) (lower * (precision)), (int) (upper * (precision))) / (precision);
|
||||
}
|
||||
|
||||
public static double min(double... doubles) {
|
||||
double currentMin = doubles[0];
|
||||
|
||||
for (int i = 1; i < doubles.length; i++) {
|
||||
currentMin = Math.min(currentMin, doubles[i]);
|
||||
}
|
||||
|
||||
return currentMin;
|
||||
}
|
||||
|
||||
public static int min(int... ints) {
|
||||
int currentMin = ints[0];
|
||||
|
||||
for (int i = 1; i < ints.length; i++) {
|
||||
currentMin = Math.min(currentMin, ints[i]);
|
||||
}
|
||||
|
||||
return currentMin;
|
||||
}
|
||||
|
||||
public static double max(double... doubles) {
|
||||
double currentMax = doubles[0];
|
||||
|
||||
for (int i = 1; i < doubles.length; i++) {
|
||||
currentMax = Math.max(currentMax, doubles[i]);
|
||||
}
|
||||
|
||||
return currentMax;
|
||||
}
|
||||
|
||||
public static int max(int... ints) {
|
||||
int currentMax = ints[0];
|
||||
|
||||
for (int i = 1; i < ints.length; i++) {
|
||||
currentMax = Math.max(currentMax, ints[i]);
|
||||
}
|
||||
|
||||
return currentMax;
|
||||
}
|
||||
|
||||
public static double highestMinusLowest(double d1, double d2) {
|
||||
if (d1 > d2) {
|
||||
return d1 - d2;
|
||||
}
|
||||
if (d2 > d1) {
|
||||
return d2 - d1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int highestMinusLowest(int i1, int i2) {
|
||||
if (i1 > i2) {
|
||||
return i1 - i2;
|
||||
}
|
||||
if (i2 > i1) {
|
||||
return i2 - i1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static <T> T[] flipArray(T[] array) {
|
||||
T[] oldArray = array.clone();
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[-i + array.length - 1] = oldArray[i];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static double removeSign(double d) {
|
||||
return d < 0 ? -d : d;
|
||||
}
|
||||
|
||||
public static long fastRound(double d) {
|
||||
return d - (long) d < 0.5 ? (long) d : (long) d + 1;
|
||||
}
|
||||
|
||||
public static int fastIntRound(double d) {
|
||||
return d - (int) d < 0.5 ? (int) d : (int) d + 1;
|
||||
}
|
||||
}
|
216
Loader/src/main/java/de/tudbut/tools/Lock.java
Normal file
216
Loader/src/main/java/de/tudbut/tools/Lock.java
Normal file
|
@ -0,0 +1,216 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Helper for synchronization and timing
|
||||
*/
|
||||
public class Lock {
|
||||
|
||||
private final Locker locker = new Locker();
|
||||
private boolean locked = false;
|
||||
private int t = 0;
|
||||
private long ts = 0;
|
||||
private final AtomicInteger waiting = new AtomicInteger();
|
||||
private volatile boolean[] waiterLocker = null;
|
||||
|
||||
/**
|
||||
* Object to handle thread locking
|
||||
*/
|
||||
private static class Locker {
|
||||
|
||||
/**
|
||||
* Make the thread wait until {@link Locker#unlock()} is called
|
||||
* @throws InterruptedException Inherited from {@link Object#wait}
|
||||
*/
|
||||
public synchronized void lockHere() throws InterruptedException {
|
||||
wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the thread wait until {@link Locker#unlock()} is called or the timeout runs out
|
||||
* @throws InterruptedException Inherited from {@link Object#wait}
|
||||
* @param timeout Maximal wait time
|
||||
*/
|
||||
public synchronized void lockHere(long timeout) throws InterruptedException {
|
||||
wait(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop locking
|
||||
*/
|
||||
public synchronized void unlock() {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Lock without default state
|
||||
*/
|
||||
public Lock() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Lock with default state
|
||||
* @param locked Default state
|
||||
*/
|
||||
public Lock(boolean locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The time left
|
||||
*/
|
||||
public long timeLeft() {
|
||||
updateLocked();
|
||||
return locked ? (ts + t) - new Date().getTime() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate timeout
|
||||
* @param timeout Timeout to override time
|
||||
* @return Time left
|
||||
*/
|
||||
protected int checkTime(int timeout) {
|
||||
return locked ? checkNegative(Math.min((int) (t - (new Date().getTime() - ts ) ), timeout <= 0 ? Integer.MAX_VALUE : timeout), timeout) : timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns alt if i is negative, otherwise i
|
||||
* @param i The integer to check
|
||||
* @param alt The alternative for if its negative
|
||||
* @return The checked or overridden value
|
||||
*/
|
||||
protected int checkNegative(int i, int alt) {
|
||||
if(i <= 0)
|
||||
return alt;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is still locked?
|
||||
*/
|
||||
protected void updateLocked() {
|
||||
if(new Date().getTime() - ts >= t && ts != 0)
|
||||
locked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until unlocked, either by a timer or manually
|
||||
*/
|
||||
public void waitHere() {
|
||||
updateLocked();
|
||||
if(locked) {
|
||||
try {
|
||||
locker.lockHere(checkTime(0));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
updateLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until unlocked, either by a timer, manually, or when it waited for timeout
|
||||
* @param timeout Timeout
|
||||
*/
|
||||
public void waitHere(int timeout) {
|
||||
updateLocked();
|
||||
if(locked) {
|
||||
try {
|
||||
locker.lockHere(checkTime(timeout));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
updateLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock manually
|
||||
*/
|
||||
public synchronized void unlock() {
|
||||
locker.unlock();
|
||||
locked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock until manually unlocked
|
||||
*/
|
||||
public synchronized void lock() {
|
||||
t = 0;
|
||||
ts = 0;
|
||||
locked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock for a specific amount of time. Timer is passive.
|
||||
* @param time The time to lock for
|
||||
*/
|
||||
public synchronized void lock(int time) {
|
||||
if(time < 0)
|
||||
time = 0;
|
||||
locked = true;
|
||||
t = time;
|
||||
ts = new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return If the lock is locked
|
||||
*/
|
||||
public synchronized boolean isLocked() {
|
||||
updateLocked();
|
||||
return locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize multiple threads on this lock
|
||||
* @param amount The amount of threads to synchronize
|
||||
*/
|
||||
public void synchronize(int amount) {
|
||||
this.locked = true;
|
||||
if(waiterLocker == null)
|
||||
waiterLocker = new boolean[amount];
|
||||
int i = waiting.get();
|
||||
waiting.getAndIncrement();
|
||||
locker.unlock();
|
||||
while (amount > waiting.get()) {
|
||||
try {
|
||||
locker.lockHere();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
locker.unlock();
|
||||
boolean b;
|
||||
waiterLocker[i] = true;
|
||||
b = true;
|
||||
try {
|
||||
while (b) {
|
||||
b = false;
|
||||
for (int j = 0 ; j < waiterLocker.length ; j++) {
|
||||
if (!waiterLocker[j]) {
|
||||
b = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) { }
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
waiting.getAndDecrement();
|
||||
waiterLocker = null;
|
||||
this.locked = false;
|
||||
}
|
||||
}
|
86
Loader/src/main/java/de/tudbut/tools/Queue.java
Normal file
86
Loader/src/main/java/de/tudbut/tools/Queue.java
Normal file
|
@ -0,0 +1,86 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Queue<T> {
|
||||
|
||||
private ArrayList<T> ts = new ArrayList<>();
|
||||
|
||||
public Queue() { }
|
||||
|
||||
protected Queue(Queue<T> queue) {
|
||||
ts = (ArrayList<T>) queue.ts.clone();
|
||||
}
|
||||
|
||||
public synchronized T pushTop(T t) {
|
||||
ts.add(t);
|
||||
notifyAll();
|
||||
return t;
|
||||
}
|
||||
|
||||
public synchronized T pushBottom(T t) {
|
||||
ts.add(0, t);
|
||||
notifyAll();
|
||||
return t;
|
||||
}
|
||||
|
||||
public synchronized T getTop() {
|
||||
return ts.get(ts.size() - 1);
|
||||
}
|
||||
|
||||
public synchronized T getBottom() {
|
||||
return ts.get(0);
|
||||
}
|
||||
|
||||
public synchronized T popBottom() {
|
||||
T t = ts.get(0);
|
||||
ts.remove(0);
|
||||
notifyAll();
|
||||
return t;
|
||||
}
|
||||
|
||||
public synchronized T popTop() {
|
||||
T t = ts.get(ts.size() - 1);
|
||||
ts.remove(ts.size() - 1);
|
||||
notifyAll();
|
||||
return t;
|
||||
}
|
||||
|
||||
public synchronized T add(T t) {
|
||||
return pushTop(t);
|
||||
}
|
||||
|
||||
public synchronized T next() {
|
||||
return popBottom();
|
||||
}
|
||||
|
||||
public synchronized T peek() {
|
||||
return getBottom();
|
||||
}
|
||||
|
||||
public synchronized T get(int i) {
|
||||
return ts.get(i);
|
||||
}
|
||||
|
||||
public synchronized int size() {
|
||||
return ts.size();
|
||||
}
|
||||
|
||||
public synchronized boolean hasNext() {
|
||||
return ts.size() > 0;
|
||||
}
|
||||
|
||||
public synchronized ArrayList<T> toList() {
|
||||
return (ArrayList<T>) ts.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o == this || (o instanceof Queue && ((Queue<?>) o).ts.equals(ts));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<T> clone() {
|
||||
return new Queue<>(this);
|
||||
}
|
||||
}
|
63
Loader/src/main/java/de/tudbut/tools/ReflectUtil.java
Normal file
63
Loader/src/main/java/de/tudbut/tools/ReflectUtil.java
Normal file
|
@ -0,0 +1,63 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import de.tudbut.io.CLSPrintWriter;
|
||||
import de.tudbut.parsing.TCN;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ReflectUtil {
|
||||
|
||||
public static boolean hasAnnotation(Field field, Class<? extends Annotation> clazz) {
|
||||
return field.getDeclaredAnnotation(clazz) != null;
|
||||
}
|
||||
|
||||
public static <T> T getPrivateFieldByTypeIndex(Class<?> clazz, CLSPrintWriter o, Class<? extends T> type, int index) {
|
||||
int idx = 0;
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if(field.getType() == type) {
|
||||
if(idx++ == index) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
return (T) field.get(o);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NullPointerException();
|
||||
}
|
||||
public static <T> T setPrivateFieldByTypeIndex(Class<?> clazz, CLSPrintWriter o, Class<? extends T> type, int index, T t) {
|
||||
int idx = 0;
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if(field.getType() == type) {
|
||||
if(idx++ == index) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(o, t);
|
||||
return t;
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static <T> T forceClone(T t) {
|
||||
if(t.getClass() != Object.class) {
|
||||
try {
|
||||
return (T) t.getClass().getDeclaredMethod("clone").invoke(t);
|
||||
}
|
||||
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
throw new RuntimeException(ignored);
|
||||
}
|
||||
}
|
||||
else
|
||||
return (T) new Object();
|
||||
}
|
||||
}
|
74
Loader/src/main/java/de/tudbut/tools/Registry.java
Normal file
74
Loader/src/main/java/de/tudbut/tools/Registry.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import de.tudbut.io.StreamReader;
|
||||
import de.tudbut.parsing.TCN;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Registry {
|
||||
|
||||
private TCN dataStore;
|
||||
private final Set<String> givenOut = new HashSet<>();
|
||||
private String fileName;
|
||||
|
||||
public Registry(String fileName) throws IOException {
|
||||
try {
|
||||
FileInputStream reader = new FileInputStream(fileName);
|
||||
String s = new StreamReader(reader).readAllAsString();
|
||||
dataStore = TCN.readMap(Tools.stringToMap(s));
|
||||
reader.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
dataStore = new TCN();
|
||||
}
|
||||
this.fileName = fileName;
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::save, "Registry shutdown hook"));
|
||||
}
|
||||
|
||||
public Registry(TCN dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
public TCN register(String keyName) throws IllegalAccessException {
|
||||
if(givenOut.contains(keyName) && !keyName.startsWith("public:")) {
|
||||
throw new IllegalAccessException("Key " + keyName + " has already been given out and is not public.");
|
||||
}
|
||||
givenOut.add(keyName);
|
||||
TCN key = dataStore.getSub(keyName);
|
||||
if(key == null) {
|
||||
dataStore.set(keyName, key = new TCN());
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public void unregister(String keyName, TCN key) throws IllegalAccessException {
|
||||
if(dataStore.getSub(keyName) != key) {
|
||||
throw new IllegalAccessException("Key " + keyName + " has different content than specified.");
|
||||
}
|
||||
givenOut.remove(keyName);
|
||||
}
|
||||
|
||||
public TCN leak() throws IllegalStateException {
|
||||
if(!givenOut.isEmpty()) {
|
||||
throw new IllegalStateException("Registry must not have any items currently given out.");
|
||||
}
|
||||
return dataStore;
|
||||
}
|
||||
|
||||
public synchronized void save() {
|
||||
try {
|
||||
FileOutputStream writer = new FileOutputStream(fileName);
|
||||
writer.write(Tools.mapToString(dataStore.toMap()).getBytes(StandardCharsets.UTF_8));
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println(Tools.mapToString(dataStore.toMap()));
|
||||
throw new RuntimeException("Unable to save registry! Dumped it to stdout instead.", e);
|
||||
}
|
||||
}
|
||||
}
|
6
Loader/src/main/java/de/tudbut/tools/Retriever.java
Normal file
6
Loader/src/main/java/de/tudbut/tools/Retriever.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
public interface Retriever<T> {
|
||||
|
||||
T retrieve();
|
||||
}
|
25
Loader/src/main/java/de/tudbut/tools/Stack.java
Normal file
25
Loader/src/main/java/de/tudbut/tools/Stack.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
public class Stack<T> extends Queue<T> {
|
||||
|
||||
public Stack() { }
|
||||
|
||||
protected Stack(Stack<T> stack) {
|
||||
super(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T next() {
|
||||
return popTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T peek() {
|
||||
return getTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stack<T> clone() {
|
||||
return new Stack<>(this);
|
||||
}
|
||||
}
|
30
Loader/src/main/java/de/tudbut/tools/StringTools.java
Normal file
30
Loader/src/main/java/de/tudbut/tools/StringTools.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
public class StringTools {
|
||||
|
||||
public static String removeIndents(String s) {
|
||||
while(s.contains("\n ")) {
|
||||
s = s.replaceAll("\n ", "\n");
|
||||
}
|
||||
while(s.contains("\n\t")) {
|
||||
s = s.replaceAll("\n\t", "\n");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static String multiply(String s, int i) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int j = 0; j < i; j++) {
|
||||
builder.append(s);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String lengthify(String s, String m, int i) {
|
||||
return s + multiply(m, (i - s.length()) / m.length());
|
||||
}
|
||||
|
||||
public static String lengthifyStart(String s, String m, int i) {
|
||||
return multiply(m, (i - s.length()) / m.length()) + s;
|
||||
}
|
||||
}
|
527
Loader/src/main/java/de/tudbut/tools/Tools.java
Normal file
527
Loader/src/main/java/de/tudbut/tools/Tools.java
Normal file
|
@ -0,0 +1,527 @@
|
|||
package de.tudbut.tools;
|
||||
|
||||
import de.tudbut.type.StringArray;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Time;
|
||||
import java.util.*;
|
||||
|
||||
public class Tools {
|
||||
|
||||
public static String[] readf(String format, String s) {
|
||||
// extracts parts of a string denoted by {}
|
||||
try {
|
||||
if(!format.contains("{}"))
|
||||
return format.equals(s) ? new String[]{} : null;
|
||||
if(format.contains("{}{}")) throw new IllegalArgumentException("Ambiguous argument: '{}{}' found in format string");
|
||||
String f = format;
|
||||
int occurences = 0;
|
||||
for(; f.indexOf("{}") != -1; occurences++) {
|
||||
f = f.substring(f.indexOf("{}") + 2);
|
||||
}
|
||||
String[] result = new String[occurences];
|
||||
|
||||
String originalFormat = format;
|
||||
for(int n = 0; n <= occurences; n++) { // This may throw if it doesn't match, but that's the same outcome
|
||||
// shave off blanking space
|
||||
int i = format.indexOf("{}");
|
||||
if(i == -1) i = format.length();
|
||||
if(!format.substring(0, i).equals(s.substring(0, i))) return null; // If the previous part didn't match, we can forget about it.
|
||||
if(n == occurences) {
|
||||
break;
|
||||
}
|
||||
format = format.substring(i + 2);
|
||||
s = s.substring(i);
|
||||
// populate braces
|
||||
int x = format.indexOf("{}");
|
||||
if(x != -1) {
|
||||
result[n] = s.substring(0, s.indexOf(format.substring(0, x)));
|
||||
}
|
||||
else {
|
||||
result[n] = s.substring(0, s.length() - (originalFormat.length() - originalFormat.lastIndexOf("{}") - 2));
|
||||
}
|
||||
s = s.substring(result[n].length());
|
||||
}
|
||||
if(result[occurences - 1] == null) // this happens when a later part doesnt match;
|
||||
return null;
|
||||
|
||||
return result;
|
||||
} catch(Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readf1(String format, String s) {
|
||||
// extracts parts of a string denoted by {}
|
||||
String[] r = readf(format, s);
|
||||
if(r == null) return null;
|
||||
if(r.length == 0) return "";
|
||||
return r[0];
|
||||
}
|
||||
|
||||
public static BufferedReader getStdInput() {
|
||||
return new BufferedReader(new InputStreamReader(System.in));
|
||||
}
|
||||
|
||||
public static String randomOutOfArray(StringArray stringArray) {
|
||||
return stringArray.asArray()[(int) Math.floor(Math.random() * stringArray.asArray().length)];
|
||||
}
|
||||
|
||||
public static <T> T randomOutOfArray(T[] array) {
|
||||
return array[(int) Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
public static String randomString(int length, String pool) {
|
||||
StringBuilder r = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
r.append(pool.charAt(ExtendedMath.random(0, pool.length() - 1)));
|
||||
}
|
||||
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
public static void copyArray(Object array1, Object array2, int copyLength) {
|
||||
System.arraycopy(array1, 0, array2, 0, copyLength);
|
||||
}
|
||||
|
||||
public static String randomAlphanumericString(int length) {
|
||||
String alphabet = "abcdefghijklmnopqrstuvwxyz";
|
||||
String pool = alphabet + alphabet.toUpperCase() + "0123456789";
|
||||
|
||||
return randomString(length, pool);
|
||||
}
|
||||
|
||||
public static String randomReadableString(int length) {
|
||||
String pool = "bcdfghjklmnpqrstvwxyz";
|
||||
String readablePool = "aeiou";
|
||||
|
||||
StringBuilder r = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
r.append(pool.charAt(ExtendedMath.random(0, pool.length() - 1)))
|
||||
.append(readablePool.charAt(ExtendedMath.random(0, readablePool.length() - 1)));
|
||||
}
|
||||
return r.substring(0, length);
|
||||
}
|
||||
|
||||
public static String getTime() {
|
||||
return new Time(new Date().getTime()).toString();
|
||||
}
|
||||
|
||||
public static String stringSwitch(Map<String, String> switchMap, String value) {
|
||||
if (switchMap.get(value) != null) {
|
||||
return switchMap.get(value);
|
||||
}
|
||||
|
||||
return switchMap.get("__default");
|
||||
}
|
||||
|
||||
public static Map<String, String> toSwitchMap(Map<String, String> alreadyExisting, String newKey, String newVal) {
|
||||
alreadyExisting.put(newKey, newVal);
|
||||
alreadyExisting.putIfAbsent("__default", "");
|
||||
return alreadyExisting;
|
||||
}
|
||||
|
||||
public static Map<String, String> newSwitchMap(String defaultVal) {
|
||||
HashMap<String, String> r = new HashMap<>();
|
||||
|
||||
r.put("__default", defaultVal);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Map<String, String> stringToMap(String mapStringParsable) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
|
||||
String[] splitTiles = mapStringParsable.split(";");
|
||||
for (int i = 0; i < splitTiles.length; i++) {
|
||||
String tile = splitTiles[i];
|
||||
String[] splitTile = tile.split(":");
|
||||
if (tile.contains(":")) {
|
||||
if (splitTile.length == 2)
|
||||
map.put(
|
||||
splitTile[0]
|
||||
.replaceAll("%I", ":")
|
||||
.replaceAll("%B", ";")
|
||||
.replaceAll("%P", "%"),
|
||||
splitTile[1].equals("%N")
|
||||
? null
|
||||
: splitTile[1]
|
||||
.replaceAll("%I", ":")
|
||||
.replaceAll("%B", ";")
|
||||
.replaceAll("%P", "%"));
|
||||
else
|
||||
map.put(
|
||||
splitTile[0]
|
||||
.replaceAll("%I", ":")
|
||||
.replaceAll("%B", ";")
|
||||
.replaceAll("%P", "%"),
|
||||
"");
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String mapToString(Map<String, String> map) {
|
||||
StringBuilder r = new StringBuilder();
|
||||
|
||||
for (String key : map.keySet().toArray(new String[0])) {
|
||||
|
||||
r.append(key.replaceAll("%", "%P").replaceAll(";", "%B").replaceAll(":", "%I"))
|
||||
.append(":")
|
||||
.append(
|
||||
map.get(key) == null
|
||||
? "%N"
|
||||
: map.get(key)
|
||||
.replaceAll("%", "%P")
|
||||
.replaceAll(";", "%B")
|
||||
.replaceAll(":", "%I"))
|
||||
.append(";");
|
||||
}
|
||||
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
public static byte[] charArrayToByteArray(char[] chars) {
|
||||
byte[] bytes = new byte[chars.length];
|
||||
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = (byte) (int) chars[i];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static int[] charArrayToIntArray(char[] chars) {
|
||||
int[] ints = new int[chars.length];
|
||||
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = chars[i];
|
||||
}
|
||||
|
||||
return ints;
|
||||
}
|
||||
|
||||
public static char[] intArrayToCharArray(int[] ints) {
|
||||
char[] chars = new char[ints.length];
|
||||
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
chars[i] = (char) ints[i];
|
||||
}
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
public static int[] byteArrayToIntArray(byte[] bytes) {
|
||||
int[] ints = new int[bytes.length];
|
||||
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = Byte.toUnsignedInt(bytes[i]);
|
||||
}
|
||||
|
||||
return ints;
|
||||
}
|
||||
|
||||
public static int[] byteArrayToUnsignedIntArray(byte[] bytes) {
|
||||
int[] ints = new int[bytes.length];
|
||||
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
ints[i] = Byte.toUnsignedInt(bytes[i]);
|
||||
}
|
||||
|
||||
return ints;
|
||||
}
|
||||
|
||||
public static String wildcardToRegex(String s) {
|
||||
String r = "";
|
||||
char[] charArray = s.toCharArray();
|
||||
for (int i = 0, charArrayLength = charArray.length; i < charArrayLength; i++) {
|
||||
char c = charArray[i];
|
||||
r += ("[" + c + "]").replaceAll("\\^", "\\^");
|
||||
}
|
||||
return "^"
|
||||
+ r.replaceAll("\\[\\\\]", "[\\\\}")
|
||||
.replaceAll("\\[\\*]", "(.|\n)*")
|
||||
.replaceAll("\\[\\?]", "[.\n]") + "$";
|
||||
}
|
||||
|
||||
public static <T> T firstNonNull(T... objects) {
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
if(objects[i] != null)
|
||||
return objects[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class TFS {
|
||||
|
||||
public static String createTFS(String sep) {
|
||||
Map<String, String> mainMap = new HashMap<>();
|
||||
mainMap.put("head", "\u0000\u0001" + sep + "\u0000\u0002\u0020\u0000\u0003/\u0000");
|
||||
return mapToString(mainMap);
|
||||
}
|
||||
|
||||
public static String getFromHead(String tfs, String key) {
|
||||
Map<String, String> tfsMap = stringToMap(tfs);
|
||||
|
||||
for (String val : tfsMap.get("head").split("\\x{0000}")) {
|
||||
if (val.startsWith(key)) return val.substring(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getPath(String tfs, String path) {
|
||||
String p = Objects.requireNonNull(getFromHead(tfs, "\u0003"));
|
||||
return path.startsWith("/") ? path : (p.equals("/") ? p : p + "/") + path;
|
||||
}
|
||||
|
||||
public static String getFile(String tfs, String path) {
|
||||
Map<String, String> tfsMap = stringToMap(tfs);
|
||||
|
||||
return tfsMap.get(getPath(tfs, path));
|
||||
}
|
||||
|
||||
public static String getFileContent(String file) {
|
||||
return stringToMap(file).get("content");
|
||||
}
|
||||
|
||||
public static String createFile(String tfs, String path, String content)
|
||||
throws TFSException.TFSFileAlreadyExistsException {
|
||||
if (getFile(tfs, path) == null) {
|
||||
Map<String, String> tfsMap = stringToMap(tfs);
|
||||
Map<String, String> fileMap = new HashMap<>();
|
||||
|
||||
fileMap.put("head", "\u0000");
|
||||
fileMap.put("content", content);
|
||||
fileMap.put("mods", String.valueOf(new Date().getTime()));
|
||||
fileMap.put("lastMod", String.valueOf(new Date().getTime()));
|
||||
|
||||
tfsMap.put(getPath(tfs, path), mapToString(fileMap));
|
||||
return mapToString(tfsMap);
|
||||
} else throw new TFSException.TFSFileAlreadyExistsException();
|
||||
}
|
||||
|
||||
public static String modFile(String tfs, String path, String newContent)
|
||||
throws TFSException.TFSFileNotFoundException {
|
||||
if (getFile(tfs, path) != null) {
|
||||
Map<String, String> tfsMap = stringToMap(tfs);
|
||||
Map<String, String> fileMap = stringToMap(tfsMap.get(getPath(tfs, path)));
|
||||
|
||||
fileMap.put("content", newContent);
|
||||
fileMap.put("mods", fileMap.get("mods") + ";" + new Date().getTime());
|
||||
fileMap.put("lastMod", String.valueOf(new Date().getTime()));
|
||||
|
||||
tfsMap.put(getPath(tfs, path), mapToString(fileMap));
|
||||
return mapToString(tfsMap);
|
||||
} else throw new TFSException.TFSFileNotFoundException();
|
||||
}
|
||||
|
||||
public static String cd(String tfs, String path) throws TFSException.TFSPathNotFromRootException {
|
||||
if (path.startsWith(Objects.requireNonNull(getFromHead(tfs, "\u0001")))) {
|
||||
Map<String, String> tfsMap = stringToMap(tfs);
|
||||
|
||||
StringBuilder newHead = new StringBuilder();
|
||||
for (String val : tfsMap.get("head").split("\\x{0000}")) {
|
||||
if (val.startsWith("\u0003")) val = "\u0003" + path;
|
||||
newHead.append(val).append("\u0000");
|
||||
}
|
||||
tfsMap.put("head", newHead.toString());
|
||||
|
||||
return mapToString(tfsMap);
|
||||
} else throw new TFSException.TFSPathNotFromRootException();
|
||||
}
|
||||
|
||||
public static class TFSException extends Exception {
|
||||
|
||||
public static class TFSFileAlreadyExistsException extends TFSException {}
|
||||
|
||||
public static class TFSFileNotFoundException extends TFSException {}
|
||||
|
||||
public static class TFSPathNotFromRootException extends TFSException {}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ObjectMapping {
|
||||
public static Map<String, String> objectToMap(Object o) throws IllegalAccessException {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
||||
Class<?> c = o.getClass();
|
||||
for (Field field : c.getFields()) {
|
||||
if (field.getType() == String.class) {
|
||||
map.put(
|
||||
field.getName(),
|
||||
"str\u0000"
|
||||
+ ((String) field.get(new Object()))
|
||||
.replaceAll("\\x{0000}", "\u00010")
|
||||
.replaceAll("\\x{0001}", "\u00011"));
|
||||
}
|
||||
|
||||
if (field.getType().isInstance(new HashMap<String, String>())) {
|
||||
map.put(
|
||||
field.getName(),
|
||||
"map\u0000"
|
||||
+ mapToString((Map<String, String>) field.get(new Object()))
|
||||
.replaceAll("\\x{0000}", "\u00010")
|
||||
.replaceAll("\\x{0001}", "\u00011"));
|
||||
}
|
||||
|
||||
if (field.getType() == int.class) {
|
||||
map.put(field.getName(), "int\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == long.class) {
|
||||
map.put(field.getName(), "lon\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == double.class) {
|
||||
map.put(field.getName(), "dou\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == float.class) {
|
||||
map.put(field.getName(), "flo\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == boolean.class) {
|
||||
map.put(field.getName(), "boo\u0000" + field.get(new Object()));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void mapToObject(Object o, Map<String, String> map) throws IllegalAccessException {
|
||||
Class<?> c = o.getClass();
|
||||
for (String key : map.keySet()) {
|
||||
String type = map.get(key).split("\\x{0000}")[0];
|
||||
String val = map.get(key)
|
||||
.split("\\x{0000}")[1]
|
||||
.replaceAll("\\x{0001}0", "\u0000")
|
||||
.replaceAll("\\x{0001}1", "\u0001");
|
||||
|
||||
try {
|
||||
Field field = c.getField(key);
|
||||
|
||||
if (type.equals("str")) {
|
||||
field.set(new Object(), val);
|
||||
}
|
||||
|
||||
if (type.equals("map")) {
|
||||
field.set(new Object(), stringToMap(val));
|
||||
}
|
||||
|
||||
if (type.equals("int")) {
|
||||
field.set(new Object(), Integer.parseInt(val));
|
||||
}
|
||||
|
||||
if (type.equals("lon")) {
|
||||
field.set(new Object(), Long.parseLong(val));
|
||||
}
|
||||
|
||||
if (type.equals("dou")) {
|
||||
field.set(new Object(), Double.parseDouble(val));
|
||||
}
|
||||
|
||||
if (type.equals("flo")) {
|
||||
field.set(new Object(), Float.parseFloat(val));
|
||||
}
|
||||
|
||||
if (type.equals("boo")) {
|
||||
field.set(new Object(), Boolean.parseBoolean(val));
|
||||
}
|
||||
} catch (NoSuchFieldException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> staticObjectToMap(Class<?> c) throws IllegalAccessException {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
||||
for (Field field : c.getFields()) {
|
||||
if (field.getType() == String.class && field.get(new Object()) != null) {
|
||||
map.put(
|
||||
field.getName(),
|
||||
"str\u0000"
|
||||
+ ((String) field.get(new Object()))
|
||||
.replaceAll("\\x{0000}", "\u00010")
|
||||
.replaceAll("\\x{0001}", "\u00011"));
|
||||
}
|
||||
|
||||
if (field.getType().isInstance(new HashMap<String, String>()) && field.get(new Object()) != null) {
|
||||
map.put(
|
||||
field.getName(),
|
||||
"map\u0000"
|
||||
+ mapToString((Map<String, String>) field.get(new Object()))
|
||||
.replaceAll("\\x{0000}", "\u00010")
|
||||
.replaceAll("\\x{0001}", "\u00011"));
|
||||
}
|
||||
|
||||
if (field.getType() == int.class) {
|
||||
map.put(field.getName(), "int\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == long.class) {
|
||||
map.put(field.getName(), "lon\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == double.class) {
|
||||
map.put(field.getName(), "dou\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == float.class) {
|
||||
map.put(field.getName(), "flo\u0000" + field.get(new Object()));
|
||||
}
|
||||
|
||||
if (field.getType() == boolean.class) {
|
||||
map.put(field.getName(), "boo\u0000" + field.get(new Object()));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void mapToStaticObject(Class<?> c, Map<String, String> map) throws IllegalAccessException {
|
||||
for (String key : map.keySet()) {
|
||||
String type = map.get(key).split("\\x{0000}")[0];
|
||||
String val = map.get(key)
|
||||
.split("\\x{0000}")[1]
|
||||
.replaceAll("\\x{0001}0", "\u0000")
|
||||
.replaceAll("\\x{0001}1", "\u0001");
|
||||
|
||||
try {
|
||||
Field field = c.getField(key);
|
||||
|
||||
if (type.equals("str")) {
|
||||
field.set(new Object(), val);
|
||||
}
|
||||
|
||||
if (type.equals("map")) {
|
||||
field.set(new Object(), stringToMap(val));
|
||||
}
|
||||
|
||||
if (type.equals("int")) {
|
||||
field.set(new Object(), Integer.parseInt(val));
|
||||
}
|
||||
|
||||
if (type.equals("lon")) {
|
||||
field.set(new Object(), Long.parseLong(val));
|
||||
}
|
||||
|
||||
if (type.equals("dou")) {
|
||||
field.set(new Object(), Double.parseDouble(val));
|
||||
}
|
||||
|
||||
if (type.equals("flo")) {
|
||||
field.set(new Object(), Float.parseFloat(val));
|
||||
}
|
||||
|
||||
if (type.equals("boo")) {
|
||||
field.set(new Object(), Boolean.parseBoolean(val));
|
||||
}
|
||||
} catch (NoSuchFieldException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
Loader/src/main/java/de/tudbut/type/IntArrayList.java
Normal file
17
Loader/src/main/java/de/tudbut/type/IntArrayList.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package de.tudbut.type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class IntArrayList extends ArrayList<Integer> {
|
||||
|
||||
public int[] toIntArray() {
|
||||
Integer[] a = toArray(new Integer[0]);
|
||||
int[] b = new int[size()];
|
||||
|
||||
for (int i = 0; i < size(); i++) {
|
||||
b[i] = a[i];
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
55
Loader/src/main/java/de/tudbut/type/StringArray.java
Normal file
55
Loader/src/main/java/de/tudbut/type/StringArray.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
package de.tudbut.type;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class StringArray {
|
||||
private String[] array;
|
||||
|
||||
public StringArray(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public StringArray() {
|
||||
this.array = new String[]{};
|
||||
}
|
||||
|
||||
public String join(String s) {
|
||||
StringBuilder joinedString = null;
|
||||
for (String arrayItem : this.array) {
|
||||
if (joinedString == null)
|
||||
joinedString = new StringBuilder(arrayItem);
|
||||
else
|
||||
joinedString.append(s).append(arrayItem);
|
||||
}
|
||||
if (joinedString != null) {
|
||||
return joinedString.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String[] asArray() {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
public List<String> asList() {
|
||||
return Arrays.asList(this.array);
|
||||
}
|
||||
|
||||
public void add(String s) {
|
||||
String[] old = this.array.clone();
|
||||
this.array = new String[this.array.length + 1];
|
||||
|
||||
System.arraycopy(old, 0, this.array, 0, old.length);
|
||||
|
||||
this.array[this.array.length - 1] = s;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.array = new String[]{};
|
||||
}
|
||||
|
||||
public void set(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import org.baseband.launcher.util.BBPermissionManager;
|
|||
import org.baseband.launcher.classloader.CustomClassloader;
|
||||
import org.baseband.launcher.util.Key;
|
||||
import sun.misc.Unsafe;
|
||||
import de.tudbut.io.StreamRedirect;
|
||||
import de.tudbut.parsing.TCN;
|
||||
|
||||
import javax.swing.*;
|
||||
|
|
Loading…
Add table
Reference in a new issue