tuddylib but smol

This commit is contained in:
Daniella / Tove 2023-09-28 03:02:10 +02:00
parent e65e1e798d
commit e9a912c3db
Signed by: TudbuT
GPG key ID: 7D63D5634B7C417F
35 changed files with 3517 additions and 7 deletions

View file

@ -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<>();

View file

@ -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.

View 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);
}
}
}

View 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");
}
}

View 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;
}
}

View 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));
}
}

View 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;
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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();
}
}

View 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);
}
}

View 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());
}
}
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View 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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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();
}
}

View 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);
}
}
}

View file

@ -0,0 +1,6 @@
package de.tudbut.tools;
public interface Retriever<T> {
T retrieve();
}

View 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);
}
}

View 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;
}
}

View 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) {
}
}
}
}
}

View 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;
}
}

View 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;
}
}

View file

@ -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.*;