diff --git a/pom.xml b/pom.xml index 1a52342..411da5f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xyz.cliserkad smp jar - 0.0.19 + 0.0.20 Simple Data Format UTF-8 @@ -35,7 +35,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.4.0 + 3.5.0 true diff --git a/src/main/antlr4/SimpleLexer.g4 b/src/main/antlr4/SimpleLexer.g4 index ffeca7b..fb5a887 100644 --- a/src/main/antlr4/SimpleLexer.g4 +++ b/src/main/antlr4/SimpleLexer.g4 @@ -38,4 +38,7 @@ fragment LETTER : UPLETTER | DNLETTER; fragment ALPHANUM : LETTER | DIGIT; fragment UNDERSCORE : '_'; CHAR_LIT: '\'' . '\''; -KEYNAME : .+; + +// the specification says you can use any character that is not : but this is much more restrictive +// this might be changed later to be more permissive, but eliminating more characters makes parsing less ambiguous +KEY_FRAGMENT: ~[-+_:; \n\t\r{}[\],.\\/*'"]; diff --git a/src/main/antlr4/SimpleParser.g4 b/src/main/antlr4/SimpleParser.g4 index 15415a0..1396e5c 100644 --- a/src/main/antlr4/SimpleParser.g4 +++ b/src/main/antlr4/SimpleParser.g4 @@ -14,7 +14,8 @@ decimal: NEGATIVE? DIGIT+ DOT DIGIT+; value: object | list | bool | integer | decimal | STRING_LIT | CHAR_LIT; list: BRACE_OPEN (value SEPARATOR)* BRACE_CLOSE; -pair: KEYNAME (ASSIGN value) PAIR_END; +key: KEY_FRAGMENT+?; +pair: key ASSIGN value PAIR_END; object: BODY_OPEN pair* BODY_CLOSE; root: object*; diff --git a/src/test/java/EncodeTest.java b/src/test/java/EncodeTest.java index 452aba8..40c2c79 100644 --- a/src/test/java/EncodeTest.java +++ b/src/test/java/EncodeTest.java @@ -4,6 +4,7 @@ import xyz.cliserkad.smp.Verifier; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static xyz.cliserkad.smp.SimpleEncoder.encode; public class EncodeTest { @@ -13,7 +14,7 @@ public class EncodeTest { @Test public void testCar() throws IllegalAccessException { assertEquals(EXPECTED, encode(new Car())); - assert Verifier.verifyOrPrint(encode(new Car())); + assertTrue(Verifier.verifyOrPrint(encode(new Car()))); } } diff --git a/src/test/java/MathTest.java b/src/test/java/MathTest.java new file mode 100644 index 0000000..6f912dc --- /dev/null +++ b/src/test/java/MathTest.java @@ -0,0 +1,65 @@ +package test.java; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static xyz.cliserkad.util.Math.pow; + +public class MathTest { + + @Test + public void test0RaisedTo0Is1() { + assertEquals(1, pow(0, 0)); + } + + @Test + public void test0RaisedTo1Is0() { + assertEquals(0, pow(0, 1)); + } + + @Test + public void test1RaisedTo0Is1() { + assertEquals(1, pow(1, 0)); + } + + @Test + public void test1RaisedTo1Is1() { + assertEquals(1, pow(1, 1)); + } + + @Test + public void test1RaisedTo2Is1() { + assertEquals(1, pow(1, 2)); + } + + @Test + public void test2RaisedTo0Is1() { + assertEquals(1, pow(2, 0)); + } + + @Test + public void test2RaisedTo1Is2() { + assertEquals(2, pow(2, 1)); + } + + @Test + public void test2RaisedTo2Is4() { + assertEquals(4, pow(2, 2)); + } + + @Test + public void test2RaisedTo3Is8() { + assertEquals(8, pow(2, 3)); + } + + @Test + public void test2RaisedTo4Is16() { + assertEquals(16, pow(2, 4)); + } + + @Test + public void testLargeExponentDoesntCauseStackOverflow() { + assertEquals(-2659239065858430293L, pow(3, Integer.MAX_VALUE)); + } + +} diff --git a/src/test/java/RangeIterationTest.java b/src/test/java/RangeIterationTest.java index 57cfa55..d590fb7 100644 --- a/src/test/java/RangeIterationTest.java +++ b/src/test/java/RangeIterationTest.java @@ -15,7 +15,7 @@ public class RangeIterationTest { @Test public void testRange() { Range range = new Range(0, 10); - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(EXPECTED_OUTPUT_1.length()); for(int i : range) { sb.append(i); } diff --git a/src/test/java/TestDuo.java b/src/test/java/TestDuo.java new file mode 100644 index 0000000..d77472d --- /dev/null +++ b/src/test/java/TestDuo.java @@ -0,0 +1,37 @@ +package test.java; + +import org.junit.jupiter.api.Test; +import xyz.cliserkad.util.Duo; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestDuo { + + @Test + public void testPrinting() { + Duo duo = new Duo<>("Hello", "World"); + assertEquals(duo.toString(), "{\n a: \"Hello\";\n b: \"World\";\n}"); + } + + @Test + public void testEquality() { + Duo duo1 = new Duo<>("Hello", "World"); + Duo duo2 = new Duo<>("Hello", "World"); + assertEquals(duo1, duo2); + } + + @Test + public void testInequality() { + Duo duo1 = new Duo<>("Hello", "World"); + Duo duo2 = new Duo<>("Hello", "World!"); + assertEquals(duo1.equals(duo2), false); + } + + @Test + public void testHashing() { + Duo duo1 = new Duo<>("Hello", "World"); + Duo duo2 = new Duo<>("Hello", "World"); + assertEquals(duo1.hashCode(), duo2.hashCode()); + } + +} diff --git a/src/test/java/TestSerialVersionUIDGenerator.java b/src/test/java/TestSerialVersionUIDGenerator.java new file mode 100644 index 0000000..10299f0 --- /dev/null +++ b/src/test/java/TestSerialVersionUIDGenerator.java @@ -0,0 +1,60 @@ +package test.java; + +import org.junit.jupiter.api.Test; +import xyz.cliserkad.util.*; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestSerialVersionUIDGenerator { + + @Test + public void testUIDsAreUnique() { + BestList> classesToTest = new BestList<>(); + + classesToTest.add(BaseConverter.class); + classesToTest.add(BestList.class); + classesToTest.add(CheckedFunction.class); + classesToTest.add(Copier.class); + classesToTest.add(DiskLoc.class); + classesToTest.add(Duo.class); + classesToTest.add(ElapseTimer.class); + classesToTest.add(ExceptionPack.class); + classesToTest.add(Levenshtein.class); + classesToTest.add(MergeSort.class); + classesToTest.add(NamedObject.class); + classesToTest.add(NamedObjectList.class); + classesToTest.add(Path.class); + classesToTest.add(PlaceHolder.class); + classesToTest.add(Range.class); + classesToTest.add(SerialCopier.class); + classesToTest.add(SerialVersionUIDGenerator.class); + classesToTest.add(Sizes.class); + classesToTest.add(StringList.class); + classesToTest.add(Text.class); + classesToTest.add(TrackedMap.class); + classesToTest.add(Trio.class); + classesToTest.add(Tuple.class); + classesToTest.add(TupleBase.class); + classesToTest.add(UnimplementedException.class); + classesToTest.add(Union.class); + classesToTest.add(Union2.class); + classesToTest.add(Union2Extendable.class); + classesToTest.add(Union3.class); + classesToTest.add(Union3Extendable.class); + classesToTest.add(UnionMember.class); + classesToTest.add(Vector2i.class); + classesToTest.add(Zipper.class); + + Set serialUIDs = new HashSet<>(); + for(Class clazz : classesToTest) { + long serialUID = SerialVersionUIDGenerator.generateSerialVersionUID(clazz); + assertTrue(serialUIDs.add(serialUID)); + } + assertEquals(serialUIDs.size(), classesToTest.size()); + } + +} diff --git a/src/test/java/TestTrio.java b/src/test/java/TestTrio.java new file mode 100644 index 0000000..ba874c2 --- /dev/null +++ b/src/test/java/TestTrio.java @@ -0,0 +1,38 @@ +package test.java; + +import org.junit.jupiter.api.Test; +import xyz.cliserkad.util.Trio; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class TestTrio { + + @Test + public void testPrinting() { + Trio trio = new Trio<>("Hello", "World", "!"); + assertEquals(trio.toString(), "{\n a: \"Hello\";\n b: \"World\";\n c: \"!\";\n}"); + } + + @Test + public void testEquality() { + Trio trio1 = new Trio<>("Hello", "World", "!"); + Trio trio2 = new Trio<>("Hello", "World", "!"); + assertEquals(trio1, trio2); + } + + @Test + public void testInequality() { + Trio trio1 = new Trio<>("Hello", "World", "!"); + Trio trio2 = new Trio<>("Hello", "World", "?"); + assertFalse(trio1.equals(trio2)); + } + + @Test + public void testHashing() { + Trio trio1 = new Trio<>("Hello", "World", "!"); + Trio trio2 = new Trio<>("Hello", "World", "!"); + assertEquals(trio1.hashCode(), trio2.hashCode()); + } + +} diff --git a/src/test/java/TestUnion3.java b/src/test/java/TestUnion3.java new file mode 100644 index 0000000..24518db --- /dev/null +++ b/src/test/java/TestUnion3.java @@ -0,0 +1,57 @@ +package test.java; + +import org.junit.jupiter.api.Test; +import xyz.cliserkad.util.Union3; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestUnion3 { + + public Union3 data; + + public String lambdaMatch() { + return data.match((str -> { + return str; + }), (i -> { + return "Integers get multiplied: " + i * 5; + }), (dbl -> { + return "Doubles get addition: " + (dbl + 3.14); + })); + } + + public String switchMatch() { + return switch(data) { + case Union3.A a -> a.getValue(); + case Union3.B b -> "Integers get multiplied: " + b.getValue() * 5; + case Union3.C c -> "Doubles get addition: " + (c.getValue() + 3.14); + }; + } + + @Test + public void testWithString() { + TestUnion3 example = new TestUnion3(); + example.data = new Union3.A<>("Hello, World!"); + assertEquals(example.lambdaMatch(), example.switchMatch()); + assertEquals(example.lambdaMatch(), "Hello, World!"); + assertEquals(example.switchMatch(), "Hello, World!"); + } + + @Test + public void testWithInteger() { + TestUnion3 example = new TestUnion3(); + example.data = new Union3.B<>(2); + assertEquals(example.lambdaMatch(), example.switchMatch()); + assertEquals(example.lambdaMatch(), "Integers get multiplied: 10"); + assertEquals(example.switchMatch(), "Integers get multiplied: 10"); + } + + @Test + public void testWithDouble() { + TestUnion3 example = new TestUnion3(); + example.data = new Union3.C<>(3.14); + assertEquals(example.lambdaMatch(), example.switchMatch()); + assertEquals(example.lambdaMatch(), "Doubles get addition: 6.28"); + assertEquals(example.switchMatch(), "Doubles get addition: 6.28"); + } + +} diff --git a/src/xyz/cliserkad/smp/BadTemplateException.java b/src/xyz/cliserkad/smp/BadTemplateException.java index 07a863b..b0f9445 100644 --- a/src/xyz/cliserkad/smp/BadTemplateException.java +++ b/src/xyz/cliserkad/smp/BadTemplateException.java @@ -1,8 +1,13 @@ package xyz.cliserkad.smp; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class BadTemplateException extends Exception { - private static final long serialVersionUID = -7085178496879726407L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(BadTemplateException.class); private final Class template; private final ReflectiveOperationException cause; diff --git a/src/xyz/cliserkad/smp/ParseData.java b/src/xyz/cliserkad/smp/ParseData.java index 9ad3cc4..a5bfc1e 100644 --- a/src/xyz/cliserkad/smp/ParseData.java +++ b/src/xyz/cliserkad/smp/ParseData.java @@ -2,12 +2,14 @@ import xyz.cliserkad.util.Path; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class ParseData extends PathMap { - /** - * - */ - private static final long serialVersionUID = -4121653623739479034L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(ParseData.class); public static final String DEFAULT_STRING = ""; public static final char DEFAULT_CHAR = ' '; public static final int DEFAULT_INT = 0; diff --git a/src/xyz/cliserkad/smp/PathMap.java b/src/xyz/cliserkad/smp/PathMap.java index 1db2743..d4ef4fa 100644 --- a/src/xyz/cliserkad/smp/PathMap.java +++ b/src/xyz/cliserkad/smp/PathMap.java @@ -2,11 +2,15 @@ import xyz.cliserkad.util.Path; +import java.io.Serial; import java.util.HashMap; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class PathMap extends HashMap { - private static final long serialVersionUID = 1504298304584057564L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(PathMap.class); /** * Retrieves the V related to specified Path. Returns null if no V is related. Will auto-cast String to Path. @@ -16,15 +20,12 @@ public class PathMap extends HashMap { */ @Override public V get(final Object key) { - if(key instanceof String) { - return super.get(new Path((String) key)); - } else if(key instanceof Path) { - return super.get(key); - } else if(key instanceof String[]) { - return super.get(new Path((String[]) key)); - } else { - return null; - } + return switch(key) { + case String s -> super.get(new Path(s)); + case Path ignored -> super.get(key); + case String[] strings -> super.get(new Path(strings)); + case null, default -> null; + }; } /** diff --git a/src/xyz/cliserkad/smp/SyntaxException.java b/src/xyz/cliserkad/smp/SyntaxException.java index 89c2a6c..d802c54 100644 --- a/src/xyz/cliserkad/smp/SyntaxException.java +++ b/src/xyz/cliserkad/smp/SyntaxException.java @@ -1,9 +1,14 @@ package xyz.cliserkad.smp; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class SyntaxException extends Exception { // increment when this file is modified - public static final long serialVersionUID = 1; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(SyntaxException.class); public final String msg; public final int line; diff --git a/src/xyz/cliserkad/smp/UnsafeException.java b/src/xyz/cliserkad/smp/UnsafeException.java index 4e6e817..8e53c01 100644 --- a/src/xyz/cliserkad/smp/UnsafeException.java +++ b/src/xyz/cliserkad/smp/UnsafeException.java @@ -1,11 +1,13 @@ package xyz.cliserkad.smp; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class UnsafeException extends Exception { - /** - * - */ - private static final long serialVersionUID = 7717159791855060651L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(UnsafeException.class); private final Exception cause; public UnsafeException(final Exception cause) { diff --git a/src/xyz/cliserkad/util/BaseConverter.java b/src/xyz/cliserkad/util/BaseConverter.java index bbd408f..c33d00a 100644 --- a/src/xyz/cliserkad/util/BaseConverter.java +++ b/src/xyz/cliserkad/util/BaseConverter.java @@ -1,8 +1,6 @@ package xyz.cliserkad.util; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -23,7 +21,7 @@ public class BaseConverter { public static final String UNSUPPORTED_BASE = " is not within " + MIN_SUPPORTED_BASE + " & " + MAX_SUPPORTED_BASE; - private static Map charValues = new HashMap<>(); + private static final char[] charValues = characterArray(); public static void main(final String[] args) { System.out.println("Checking conversions..."); @@ -69,9 +67,9 @@ public static void checkBase(final int base) { } /** - * Converts a base 64 number (represented as a String) to a base 10 number (represented as an int). Example: E5; 873 + * Converts a Base64 String to an int. * - * @param input String representing a base 64 number + * @param input Base64 String * @return An int equal to the input */ public static int toNumber(final String input) { @@ -79,10 +77,11 @@ public static int toNumber(final String input) { } /** - * Converts a String representing a number in the specified base to an int. Can convert from Binary, Octal, Hex, or even Base64. For use in conjunction with toString to convert numbers back and forth. + * Converts a String in the specified base to an int. Can convert from Binary, Octal, Hex, Base64. + * For use in conjunction with toString to convert numbers back and forth. * * @param input String representing a number - * @param base base of number in input; [0, 64] + * @param base base of number in input; [2..64] * @return An int * @see BaseConverter#toString(int, int) */ @@ -111,10 +110,10 @@ public static int toNumber(final String input, final int base) { } /** - * Constructs the Map that holds the values for each valid char in a Base64 number String + * Constructs the array of valid chars for a Base64 number String */ - private static void buildCharacterMap() { - final var builtValues = new HashMap(); + private static char[] characterArray() { + final char[] builtValues = new char[MAX_SUPPORTED_BASE]; var i = 0; int booster = START_OF_DIGITS; while(i < MAX_SUPPORTED_BASE) { @@ -127,27 +126,17 @@ private static void buildCharacterMap() { } else if(i == 63) { booster = '_' - 63; } - builtValues.put(i, (char) (i + booster)); + builtValues[i] = (char) (i + booster); i++; } - charValues = builtValues; + return builtValues; } /** - * @return built charValues - */ - public static Map getCharValues() { - if(charValues.isEmpty()) { - buildCharacterMap(); - } - return charValues; - } - - /** - * Converts an input number to a string of the desired base. Can convert to Binary, Octal, Hex, or even Base64. + * Converts an int to a string of the desired base. Can convert to Binary, Octal, Hex, Base64. * - * @param input any int >= 0 - * @param base [2, 64] + * @param input [1..2^31-1] + * @param base [2..64] * @return A String representing the input in the requested base */ public static String toString(int input, final int base) { @@ -160,16 +149,16 @@ public static String toString(int input, final int base) { } while(input > 0); for(var i = digits.size() - 1; i >= 0; i--) { - output.append(getCharValues().get(digits.get(i))); + output.append(charValues[digits.get(i)]); } return output.toString(); } /** - * Converts a base 10 number (represented as an int) to a Base64 number (represented as an String). Example: 873; E5 + * Converts an int to a Base64 String. * - * @param input any int >= 0 - * @return A String equal to the input + * @param input [1..2^31-1] + * @return Base64 representation of input */ public static String toString(final int input) { return toString(input, MAX_SUPPORTED_BASE); diff --git a/src/xyz/cliserkad/util/BestList.java b/src/xyz/cliserkad/util/BestList.java index 5741fdb..a639841 100644 --- a/src/xyz/cliserkad/util/BestList.java +++ b/src/xyz/cliserkad/util/BestList.java @@ -1,17 +1,18 @@ package xyz.cliserkad.util; +import java.io.Serial; import java.util.ArrayList; import java.util.List; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + /** - * Improved version of ArrayList that implements XML - * - * @param - * @author Bryan Johnson + * Improved version of ArrayList */ public class BestList extends ArrayList { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(BestList.class); @SafeVarargs public BestList(final E... elements) { @@ -29,6 +30,14 @@ public static BestList list(final E... element) { return new BestList<>(element); } + public static BestList nonNullList(final List in) { + if(in == null) { + return new BestList<>(); + } else { + return new BestList<>(in); + } + } + // unchecked because parent add() function in ArrayList is also unchecked @SafeVarargs public final boolean add(final E... elements) { @@ -96,12 +105,4 @@ public String toString() { return out.toString(); } - public static BestList nonNullList(final List in) { - if(in == null) { - return new BestList<>(); - } else { - return new BestList<>(in); - } - } - } diff --git a/src/xyz/cliserkad/util/CheckedFunction.java b/src/xyz/cliserkad/util/CheckedFunction.java new file mode 100644 index 0000000..9d97aa3 --- /dev/null +++ b/src/xyz/cliserkad/util/CheckedFunction.java @@ -0,0 +1,27 @@ +package xyz.cliserkad.util; + +import java.util.function.Function; + +@FunctionalInterface +public interface CheckedFunction { + + /** + * Returns a function that always returns its input argument. + * + * @param the type of the input and output objects to the function + * @return a function that always returns its input argument + */ + static Function identity() { + return t -> t; + } + + /** + * Applies this function to the given argument. This may throw an Exception. + * + * @param t the function argument + * @return the function result + * @throws Exception if an error occurs + */ + R apply(T t) throws Exception; + +} diff --git a/src/xyz/cliserkad/util/Copier.java b/src/xyz/cliserkad/util/Copier.java index 0ef72da..569278b 100644 --- a/src/xyz/cliserkad/util/Copier.java +++ b/src/xyz/cliserkad/util/Copier.java @@ -4,7 +4,6 @@ * Implementers will have copy() to allow for copying the values of an object while having a different reference. Implementers must have equals() defined, otherwise checkClone will always fail and throw an IllegalStateException. * * @param - * @author Bryan Johnson */ public interface Copier { @@ -52,13 +51,6 @@ static boolean checkCopyVerbose(final Any original, final Any copy) throws */ Copy copy(); - /** - * Provides access to the object that is implementing Copies. Should be defined as return this;. - * - * @return Implementation - */ - Type self(); - /** * Container class that holds the data from Copies.copy(). * diff --git a/src/xyz/cliserkad/util/DiskLoc.java b/src/xyz/cliserkad/util/DiskLoc.java index 746a4f1..81b66f8 100644 --- a/src/xyz/cliserkad/util/DiskLoc.java +++ b/src/xyz/cliserkad/util/DiskLoc.java @@ -3,6 +3,9 @@ import xyz.cliserkad.smp.SimpleEncoder; import java.io.File; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; /** * Class that makes Files easier to work with @@ -10,7 +13,8 @@ public class DiskLoc extends File implements Copier { public static final String DOT = "."; - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(DiskLoc.class); // Not sure if forward slash is 100% foolproof public DiskLoc(final String... pathParts) { @@ -97,9 +101,4 @@ public Copy copy() { return new Copy<>(this, dl); } - @Override - public DiskLoc self() { - return this; - } - } diff --git a/src/xyz/cliserkad/util/Duo.java b/src/xyz/cliserkad/util/Duo.java new file mode 100644 index 0000000..1c886ce --- /dev/null +++ b/src/xyz/cliserkad/util/Duo.java @@ -0,0 +1,18 @@ +package xyz.cliserkad.util; + +public class Duo extends TupleBase implements Tuple { + + public final TypeA a; + public final TypeB b; + + public Duo(final TypeA a, final TypeB b) { + this.a = a; + this.b = b; + } + + @Override + public Object[] getValues() { + return new Object[] { a, b }; + } + +} diff --git a/src/xyz/cliserkad/util/ExceptionPack.java b/src/xyz/cliserkad/util/ExceptionPack.java index 2dd23da..37f74a1 100644 --- a/src/xyz/cliserkad/util/ExceptionPack.java +++ b/src/xyz/cliserkad/util/ExceptionPack.java @@ -3,10 +3,12 @@ import java.io.Serial; import java.util.List; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class ExceptionPack extends Exception { @Serial - private static final long serialVersionUID = 1939447964468120636L; + private static final long serialVersionUID = generateSerialVersionUID(ExceptionPack.class); public static final String NO_MSG = "No message provided"; diff --git a/src/xyz/cliserkad/util/Math.java b/src/xyz/cliserkad/util/Math.java index f001bc6..4386ecb 100644 --- a/src/xyz/cliserkad/util/Math.java +++ b/src/xyz/cliserkad/util/Math.java @@ -59,20 +59,19 @@ public static double avg_d(final double... input) { } /** - * Custom "to the power of" method for determining exponential values. For whatever reason Math.pow() doesn't work properly for certain use cases. The output might suffer from an integer overflow if the arguments are too large. + * Custom "to the power of" method for determining exponential values. + * For whatever reason Math.pow() doesn't work properly for certain use cases. + * The output might suffer from an integer overflow if the arguments are too large. * - * @param number The base number - * @param power The exponent - * @return number^power + * @return base^exponent */ - public static long pow(final int number, int power) { - if(power == 0 || number == 0) { + public static long pow(final int base, int exponent) { + if(exponent == 0) return 1; - } - long output = number; - while(power > 1) { - output *= number; - power--; + long output = base; + while(exponent > 1) { + output *= base; + exponent--; } return output; } diff --git a/src/xyz/cliserkad/util/NamedObjectList.java b/src/xyz/cliserkad/util/NamedObjectList.java index 5b22725..f405458 100644 --- a/src/xyz/cliserkad/util/NamedObjectList.java +++ b/src/xyz/cliserkad/util/NamedObjectList.java @@ -1,8 +1,13 @@ package xyz.cliserkad.util; +import java.io.Serial; + +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class NamedObjectList extends BestList { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(NamedObjectList.class); private StringList names; diff --git a/src/xyz/cliserkad/util/Path.java b/src/xyz/cliserkad/util/Path.java index 8080b17..55f91df 100644 --- a/src/xyz/cliserkad/util/Path.java +++ b/src/xyz/cliserkad/util/Path.java @@ -1,11 +1,15 @@ package xyz.cliserkad.util; +import java.io.Serial; import java.io.Serializable; import java.util.Arrays; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class Path implements Serializable { - private static final long serialVersionUID = 5596584287964181971L; + @Serial + private static final long serialVersionUID = generateSerialVersionUID(Path.class); public static final char PATH_SEPARATOR = '/'; public static void checkArray(final String[] array) { diff --git a/src/xyz/cliserkad/util/Range.java b/src/xyz/cliserkad/util/Range.java index 0c5c8d7..4e809ff 100644 --- a/src/xyz/cliserkad/util/Range.java +++ b/src/xyz/cliserkad/util/Range.java @@ -1,5 +1,7 @@ package xyz.cliserkad.util; +import java.io.Serial; +import java.io.Serializable; import java.util.Iterator; import java.util.Random; @@ -9,7 +11,10 @@ /** * Represents a range. The range is inclusive for its minimum but exclusive for its maximum. */ -public final class Range implements Copier, Iterable { +public final class Range implements Copier, Iterable, Serializable { + + @Serial + private static final long serialVersionUID = SerialVersionUIDGenerator.generateSerialVersionUID(Range.class); public final int min; public final int max; @@ -46,8 +51,8 @@ public int size() { /** * Determines if this range contains the input, including the start but excluding the end * - * @param n A number - * @return if n is in range + * @param n The number to check + * @return n >= min && n < max */ public boolean has(final int n) { return n >= min && n < max; @@ -106,11 +111,6 @@ public Copy copy() { return new Copy<>(this, r); } - @Override - public Range self() { - return this; - } - @Override public Iterator iterator() { return new Iterator<>() { diff --git a/src/xyz/cliserkad/util/SerialCopier.java b/src/xyz/cliserkad/util/SerialCopier.java index 12e798e..b53a9b1 100644 --- a/src/xyz/cliserkad/util/SerialCopier.java +++ b/src/xyz/cliserkad/util/SerialCopier.java @@ -54,9 +54,4 @@ public Copy copy() { } } - @Override - public T self() { - return object; - } - } diff --git a/src/xyz/cliserkad/util/SerialVersionUIDGenerator.java b/src/xyz/cliserkad/util/SerialVersionUIDGenerator.java new file mode 100644 index 0000000..5b78934 --- /dev/null +++ b/src/xyz/cliserkad/util/SerialVersionUIDGenerator.java @@ -0,0 +1,14 @@ +package xyz.cliserkad.util; + +import java.util.Objects; + +public class SerialVersionUIDGenerator { + + public static long generateSerialVersionUID(final Class clazz) { + BestList differentiators = new BestList<>(); + differentiators.add(clazz.getName()); + differentiators.add(clazz.getFields()); + return Objects.hash(differentiators.toArray()); + } + +} diff --git a/src/xyz/cliserkad/util/StringList.java b/src/xyz/cliserkad/util/StringList.java index 84c5ee7..458e38a 100644 --- a/src/xyz/cliserkad/util/StringList.java +++ b/src/xyz/cliserkad/util/StringList.java @@ -2,10 +2,12 @@ import java.io.Serial; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class StringList extends BestList { @Serial - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = generateSerialVersionUID(StringList.class); public StringList(final String... strings) { super(strings); diff --git a/src/xyz/cliserkad/util/Trio.java b/src/xyz/cliserkad/util/Trio.java new file mode 100644 index 0000000..fc1e0f4 --- /dev/null +++ b/src/xyz/cliserkad/util/Trio.java @@ -0,0 +1,20 @@ +package xyz.cliserkad.util; + +public class Trio extends TupleBase implements Tuple { + + public final TypeA a; + public final TypeB b; + public final TypeC c; + + public Trio(final TypeA a, final TypeB b, final TypeC c) { + this.a = a; + this.b = b; + this.c = c; + } + + @Override + public Object[] getValues() { + return new Object[] { a, b, c }; + } + +} diff --git a/src/xyz/cliserkad/util/Tuple.java b/src/xyz/cliserkad/util/Tuple.java new file mode 100644 index 0000000..cd80bf9 --- /dev/null +++ b/src/xyz/cliserkad/util/Tuple.java @@ -0,0 +1,7 @@ +package xyz.cliserkad.util; + +public interface Tuple { + + Object[] getValues(); + +} diff --git a/src/xyz/cliserkad/util/TupleBase.java b/src/xyz/cliserkad/util/TupleBase.java new file mode 100644 index 0000000..02432a1 --- /dev/null +++ b/src/xyz/cliserkad/util/TupleBase.java @@ -0,0 +1,49 @@ +package xyz.cliserkad.util; + +import java.util.Objects; + +public abstract class TupleBase implements Tuple { + + /** + * Compares all corresponding values in the two tuples. Returns false when the two tuples are of different sizes. + */ + @Override + public boolean equals(Object o) { + if(this == o) + return true; + if(!(o instanceof Tuple other)) + return false; + Object[] myValues = getValues(); + Object[] otherValues = other.getValues(); + if(myValues.length != otherValues.length) + return false; + for(int i = 0; i < myValues.length; i++) + if(!Objects.equals(myValues[i], otherValues[i])) + return false; + return true; + } + + /** + * @return Objects.hash(getValues()); + */ + @Override + public int hashCode() { + return Objects.hash(getValues()); + } + + /** + * Returns a string in the format
+ * { a: "a's value"; b: "b's value"; ... } + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{\n"); + Object[] values = getValues(); + for(int i = 0; i < values.length; i++) { + sb.append(" ").append((char) ('a' + i)).append(": \"").append(values[i]).append("\";\n"); + } + sb.append("}"); + return sb.toString(); + } + +} diff --git a/src/xyz/cliserkad/util/UnimplementedException.java b/src/xyz/cliserkad/util/UnimplementedException.java index d39e548..ac6a013 100644 --- a/src/xyz/cliserkad/util/UnimplementedException.java +++ b/src/xyz/cliserkad/util/UnimplementedException.java @@ -2,10 +2,12 @@ import java.io.Serial; +import static xyz.cliserkad.util.SerialVersionUIDGenerator.generateSerialVersionUID; + public class UnimplementedException extends Exception { @Serial - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = generateSerialVersionUID(UnimplementedException.class); public static final String SWITCH_MISS = "Missing critical branch of switch statement"; diff --git a/src/xyz/cliserkad/util/Union.java b/src/xyz/cliserkad/util/Union.java new file mode 100644 index 0000000..8027edf --- /dev/null +++ b/src/xyz/cliserkad/util/Union.java @@ -0,0 +1,7 @@ +package xyz.cliserkad.util; + +public interface Union { + + Object getValue(); + +} diff --git a/src/xyz/cliserkad/util/Union2.java b/src/xyz/cliserkad/util/Union2.java new file mode 100644 index 0000000..d5c11e3 --- /dev/null +++ b/src/xyz/cliserkad/util/Union2.java @@ -0,0 +1,40 @@ +package xyz.cliserkad.util; + +import java.util.function.Function; + +/** + * A class that can be one of 2 types. see Union3 for more details + */ +public sealed interface Union2 extends Union2Extendable { + + default ReturnType match(Function funcA, Function funcB) { + return switch(this) { + case Union2.A a -> funcA.apply(a.getValue()); + case Union2.B b -> funcB.apply(b.getValue()); + }; + } + + default ReturnType matchFallible(CheckedFunction funcA, CheckedFunction funcB) throws Exception { + return switch(this) { + case Union2.A a -> funcA.apply(a.getValue()); + case Union2.B b -> funcB.apply(b.getValue()); + }; + } + + final class A extends UnionMember implements Union2 { + + public A(TypeA value) { + super(value); + } + + } + + final class B extends UnionMember implements Union2 { + + public B(TypeB value) { + super(value); + } + + } + +} diff --git a/src/xyz/cliserkad/util/Union2Extendable.java b/src/xyz/cliserkad/util/Union2Extendable.java new file mode 100644 index 0000000..74a3965 --- /dev/null +++ b/src/xyz/cliserkad/util/Union2Extendable.java @@ -0,0 +1,5 @@ +package xyz.cliserkad.util; + +public interface Union2Extendable extends Union { + +} diff --git a/src/xyz/cliserkad/util/Union3.java b/src/xyz/cliserkad/util/Union3.java new file mode 100644 index 0000000..ee2fa17 --- /dev/null +++ b/src/xyz/cliserkad/util/Union3.java @@ -0,0 +1,58 @@ +package xyz.cliserkad.util; + +import java.util.function.Function; + +/** + * A class that can be one of three types. This is useful for three unrelated types that need to be stored in the same variable. + */ +public sealed interface Union3 extends Union3Extendable { + + /** + * apply 1 of 3 functions to the value, depending on its type. + */ + default ReturnType match(Function funcA, Function funcB, Function funcC) { + return switch(this) { + case Union3.A a -> funcA.apply(a.getValue()); + case Union3.B b -> funcB.apply(b.getValue()); + case Union3.C c -> funcC.apply(c.getValue()); + }; + } + + /** + * apply 1 of 3 functions to the value, depending on its type. May throw an exception + * + * @throws Exception if the called function throws an Exception + */ + default ReturnType matchFallible(CheckedFunction funcA, CheckedFunction funcB, CheckedFunction funcC) throws Exception { + return switch(this) { + case Union3.A a -> funcA.apply(a.getValue()); + case Union3.B b -> funcB.apply(b.getValue()); + case Union3.C c -> funcC.apply(c.getValue()); + }; + } + + final class A extends UnionMember implements Union3 { + + public A(TypeA value) { + super(value); + } + + } + + final class B extends UnionMember implements Union3 { + + public B(TypeB value) { + super(value); + } + + } + + final class C extends UnionMember implements Union3 { + + public C(TypeC value) { + super(value); + } + + } + +} diff --git a/src/xyz/cliserkad/util/Union3Extendable.java b/src/xyz/cliserkad/util/Union3Extendable.java new file mode 100644 index 0000000..29f67b7 --- /dev/null +++ b/src/xyz/cliserkad/util/Union3Extendable.java @@ -0,0 +1,5 @@ +package xyz.cliserkad.util; + +public interface Union3Extendable extends Union { + +} diff --git a/src/xyz/cliserkad/util/UnionMember.java b/src/xyz/cliserkad/util/UnionMember.java new file mode 100644 index 0000000..0480f9f --- /dev/null +++ b/src/xyz/cliserkad/util/UnionMember.java @@ -0,0 +1,49 @@ +package xyz.cliserkad.util; + +public class UnionMember implements Union { + + private final Type value; + + public UnionMember(Type value) throws NullPointerException { + this.value = failIfNull(value); + } + + public static ArgumentType failIfNull(ArgumentType argument) throws NullPointerException { + if(argument == null) + throw new NullPointerException("value cannot be null"); + return argument; + } + + @Override + public Type getValue() { + return value; + } + + /** + * Provides access to equals() method of underlying value. If the Object given is a Union, it will be unwrapped to compare underlying values. + */ + @Override + public boolean equals(Object obj) { + if(obj instanceof Union wrapper) + return getValue().equals(wrapper.getValue()); + else + return getValue().equals(obj); + } + + /** + * Provides access to hashCode() method of underlying value. + */ + @Override + public int hashCode() { + return getValue().hashCode(); + } + + /** + * Provides access to toString() method of underlying value. + */ + @Override + public String toString() { + return getValue().toString(); + } + +} diff --git a/src/xyz/cliserkad/util/Vector2i.java b/src/xyz/cliserkad/util/Vector2i.java index a8bc2f3..d2616cd 100644 --- a/src/xyz/cliserkad/util/Vector2i.java +++ b/src/xyz/cliserkad/util/Vector2i.java @@ -58,11 +58,6 @@ public Copy copy() { return new Copy<>(this, p); } - @Override - public Vector2i self() { - return this; - } - @Override public int hashCode() { return Objects.hash(x, y);