From 587f8bbaf62e7ca81cdb3de518e380ef1744e347 Mon Sep 17 00:00:00 2001 From: Isaac Parenteau Date: Sun, 17 Sep 2023 22:15:12 -0500 Subject: [PATCH 1/4] Upgrading to use jdk 17 --- Jenkinsfile | 4 +- pom.xml | 104 ++++++++--- .../configuration/PropertiesManager.java | 25 ++- .../net/locusworks/common/crypto/AES.java | 19 +- .../net/locusworks/common/crypto/AESKey.java | 5 +- .../net/locusworks/common/crypto/KeyFile.java | 8 +- .../common/crypto/SSHEncodedKeySpec.java | 3 +- .../exceptions/ApplicationException.java | 4 +- .../locusworks/common/immutables/Triplet.java | 8 +- .../interfaces/AutoCloseableIterator.java | 2 +- .../net/locusworks/common/io/IOUtils.java | 2 +- .../objectmapper/ObjectMapperHelper.java | 4 +- .../properties/ImmutableProperties.java | 7 +- .../common/properties/OrderedProperties.java | 12 +- .../common/utils/DataOutputStreamHelper.java | 4 +- .../common/utils/DateTimeStampSerializer.java | 3 +- .../net/locusworks/common/utils/Splitter.java | 59 ++++--- .../net/locusworks/common/utils/Utils.java | 166 +++++++----------- .../locusworks/test/AESEncryptionTest.java | 20 +-- .../java/net/locusworks/test/AllTests.java | 6 +- .../net/locusworks/test/FileReaderTest.java | 61 +++---- .../net/locusworks/test/HashSaltTest.java | 16 +- .../net/locusworks/test/HashUtilsTest.java | 21 ++- .../net/locusworks/test/ImmutablesTest.java | 30 ++-- .../test/ObjectMapperHelperTest.java | 20 +-- .../test/PropertiesManagerTest.java | 30 ++-- .../net/locusworks/test/RandomStringTest.java | 11 +- .../java/net/locusworks/test/UtilsTest.java | 91 +++++++++- 28 files changed, 414 insertions(+), 331 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3b19af3..efd463b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -109,7 +109,7 @@ def getSha1() { def mvn(args) { withMaven( - maven: 'maven-3.6.1', + maven: 'maven-3.9.4', globalMavenSettingsConfig: 'locusworks-settings' ) { @@ -183,4 +183,4 @@ def SetVersion( v ) { } } -return this \ No newline at end of file +return this diff --git a/pom.xml b/pom.xml index 5f563f8..6faff9a 100644 --- a/pom.xml +++ b/pom.xml @@ -18,11 +18,11 @@ - 2.14.1 - 1.7.32 - 2.12.5 - 1.8 - 1.8 + 2.20.0 + 2.0.9 + 2.15.2 + 17 + 17 https://nexus.locusworks.net @@ -31,28 +31,27 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 - - always - + 3.1.2 org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 ${maven.compiler.source} ${maven.compiler.target} true - - -Xlint:all - + + -Xlint:all + --add-exports + java.base/sun.security.jca=ALL-UNNAMED + org.owasp dependency-check-maven - 6.3.1 + 8.4.0 @@ -61,20 +60,77 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + + prepare-agent + + + + report + prepare-package + + report + + + + jacoco-check + + check + + + + + PACKAGE + + + LINE + COVEREDRATIO + 1.00 + + + BRANCH + COVEREDRATIO + 1.00 + + + + + + + + - junit - junit - 4.13.2 + org.junit.jupiter + junit-jupiter-api + 5.10.0 test + + org.junit.jupiter + junit-jupiter-params + 5.10.0 + test + + + org.mockito + mockito-core + 5.5.0 + test + + org.flywaydb flyway-core - 7.15.0 + 9.22.1 org.apache.logging.log4j @@ -103,14 +159,14 @@ - org.apache.httpcomponents - httpclient - 4.5.13 + org.apache.httpcomponents.client5 + httpclient5 + 5.2.1 org.apache.httpcomponents httpmime - 4.5.13 + 4.5.14 @@ -133,7 +189,7 @@ com.google.code.gson gson - 2.8.8 + 2.10.1 @@ -176,4 +232,4 @@ - \ No newline at end of file + diff --git a/src/main/java/net/locusworks/common/configuration/PropertiesManager.java b/src/main/java/net/locusworks/common/configuration/PropertiesManager.java index c99a16d..a9b8a0b 100644 --- a/src/main/java/net/locusworks/common/configuration/PropertiesManager.java +++ b/src/main/java/net/locusworks/common/configuration/PropertiesManager.java @@ -15,11 +15,13 @@ import java.util.Properties; import java.util.stream.Collectors; import net.locusworks.common.immutables.Pair; +import net.locusworks.common.immutables.Unit; + /** * Properties manager class to help load and read properties * @author Isaac Parenteau - * @version 1.0.0 - * @date 02/15/2018 + * @version 2.0.0 + * @date 09/17/2023 */ public class PropertiesManager { /** @@ -88,7 +90,8 @@ public class PropertiesManager { * @return a map containing the results of the values added */ public static Map addConfiguration(Properties to, Properties from) { - Map results = from.entrySet() + + return from.entrySet() .stream() .filter(entry -> !to.containsKey(entry.getKey())) .map(entry -> { @@ -97,9 +100,7 @@ public class PropertiesManager { to.put(key, value); return new Pair(key, value); }) - .collect(Collectors.toMap(key -> key.getValue1(), value -> value.getValue2())); - - return results; + .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); } /** @@ -109,19 +110,17 @@ public class PropertiesManager { * @return a map containing the results of the values removed */ public static Map removeConfiguration(Properties from, Properties comparedTo) { - Map results = from.keySet() + + return from.keySet() .stream() .filter(key -> !comparedTo.containsKey(key)) //only get the items that are not in the comparedTo properties .map(key -> new Pair(String.valueOf(key), String.valueOf(from.get(key)))) - .collect(Collectors.toList()) //Create a list of paired items (key value) of the items that were filtered + .toList() //Create a list of paired items (key value) of the items that were filtered .stream() - .map(pair -> { //remove those pairs from the from properties + .peek(pair -> { //remove those pairs from the from properties from.remove(pair.getValue1()); - return pair; }) - .collect(Collectors.toMap(key -> key.getValue1(), value -> value.getValue2())); //create a map of what was removed - - return results; + .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); } /** diff --git a/src/main/java/net/locusworks/common/crypto/AES.java b/src/main/java/net/locusworks/common/crypto/AES.java index 9c67983..b44355a 100644 --- a/src/main/java/net/locusworks/common/crypto/AES.java +++ b/src/main/java/net/locusworks/common/crypto/AES.java @@ -7,10 +7,7 @@ import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; +import java.security.*; import java.util.Base64; import net.locusworks.common.utils.RandomString; @@ -21,7 +18,7 @@ import static net.locusworks.common.Charsets.UTF_8; /** * AES encryption/decryption class * This class will encrypt/decrypt data. The encryption key is never known. - * Instead it is is generated by the provided seed. As long as the seed stays the same + * Instead, it is generated by the provided seed. As long as the seed stays the same * the key will remain the same and the encryption/decryption will work. This * provides and added security. * @author Isaac Parenteau @@ -62,7 +59,6 @@ public class AES { * @param seed Seed to initialize SecureRandom with * @return SecureRandom object * @throws NoSuchAlgorithmException thrown when algorithm can't be used - * @throws NoSuchProviderException thrown when the provider cant be found */ private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException { SecureRandom sr = SecureRandom.getInstance(ALGORITHM); @@ -73,7 +69,7 @@ public class AES { /** * Initialize the aes engine * @param key secret key to use - * @param sr + * @param sr the secure random class */ private void init(final byte[] key, SecureRandom sr) { try { @@ -81,8 +77,7 @@ public class AES { this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE); this.ivParamSpec = new IvParameterSpec(RandomString.getBytes(16, sr)); } catch (Exception ex) { - System.err.println(ex); - throw new IllegalArgumentException("Unable to initalize encryption:", ex); + throw new IllegalArgumentException("Unable to initialize encryption:", ex); } } @@ -122,7 +117,7 @@ public class AES { } } - public AES setSeed(String seed) { + public AES withSeed(String seed) { if (this.seed == null || !this.seed.equals(seed)) { initSecureKey(seed); } @@ -144,9 +139,7 @@ public class AES { } public static AES createInstance(String seed) { - AES aes = new AES(); - aes.setSeed(seed); - return aes; + return new AES().withSeed(seed); } public static void main(String[] args) throws NoSuchAlgorithmException { diff --git a/src/main/java/net/locusworks/common/crypto/AESKey.java b/src/main/java/net/locusworks/common/crypto/AESKey.java index b64d6b8..3bec359 100644 --- a/src/main/java/net/locusworks/common/crypto/AESKey.java +++ b/src/main/java/net/locusworks/common/crypto/AESKey.java @@ -1,13 +1,14 @@ package net.locusworks.common.crypto; +import java.io.Serial; import java.security.PrivateKey; import net.locusworks.common.Charsets; public class AESKey implements PrivateKey { - private static final long serialVersionUID = -8452357427706386362L; - private String seed; + @Serial private static final long serialVersionUID = -8452357427706386362L; + private final String seed; public AESKey(String seed) { this.seed = seed; diff --git a/src/main/java/net/locusworks/common/crypto/KeyFile.java b/src/main/java/net/locusworks/common/crypto/KeyFile.java index 9b71ce3..0ebffec 100644 --- a/src/main/java/net/locusworks/common/crypto/KeyFile.java +++ b/src/main/java/net/locusworks/common/crypto/KeyFile.java @@ -114,7 +114,7 @@ public class KeyFile implements AutoCloseable { dosh.writeInt(item.length); dosh.write(item); })); - data = String.format("ssh-rsa", dosh.base64Encoded(), this.description); + data = String.format("ssh-rsa %s %s", dosh.base64Encoded(), this.description); IOUtils.writeStringToFile(fileName, data); } break; @@ -186,9 +186,9 @@ public class KeyFile implements AutoCloseable { } private static class KeySpecHelper { - private KeySpec keySpec; - private boolean isPrivate; - private EncryptionType encryptionType; + private final KeySpec keySpec; + private final boolean isPrivate; + private final EncryptionType encryptionType; public KeySpecHelper(KeySpec keySpec, boolean isPrivate, EncryptionType encryptionType) { super(); diff --git a/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java b/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java index 0572364..94a33a2 100644 --- a/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java +++ b/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java @@ -37,8 +37,7 @@ public class SSHEncodedKeySpec extends EncodedKeySpec { checkArguments(SSH_MARKER.equals(marker), "Looking for marker %s but received %s", SSH_MARKER, marker); BigInteger publicExponent = new BigInteger(readLengthFirst(stream)); BigInteger modulus = new BigInteger(readLengthFirst(stream)); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent); - return keySpec; + return new RSAPublicKeySpec(modulus, publicExponent); } catch (Exception ex) { throw new InvalidKeySpecException(ex); } diff --git a/src/main/java/net/locusworks/common/exceptions/ApplicationException.java b/src/main/java/net/locusworks/common/exceptions/ApplicationException.java index 0ade43a..40cb230 100644 --- a/src/main/java/net/locusworks/common/exceptions/ApplicationException.java +++ b/src/main/java/net/locusworks/common/exceptions/ApplicationException.java @@ -1,5 +1,7 @@ package net.locusworks.common.exceptions; +import java.io.Serial; + /*** * Custom exception class for the patch repository * @author Isaac Parenteau @@ -8,7 +10,7 @@ package net.locusworks.common.exceptions; public class ApplicationException extends Exception { private final Integer code; boolean success = false; - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; public static ApplicationException egregiousServer() { return new ApplicationException(9001, "Something went wrong. Please see logs for details"); diff --git a/src/main/java/net/locusworks/common/immutables/Triplet.java b/src/main/java/net/locusworks/common/immutables/Triplet.java index 5ff5c83..065b7e2 100644 --- a/src/main/java/net/locusworks/common/immutables/Triplet.java +++ b/src/main/java/net/locusworks/common/immutables/Triplet.java @@ -48,10 +48,8 @@ public class Triplet extends Pair { @Override public boolean equals(Object other) { - if (!(other instanceof Triplet)) return false; - - Triplet otherTriplet = (Triplet)other; - + if (!(other instanceof Triplet otherTriplet)) return false; + return super.equals(otherTriplet) && this.getValue3().equals(otherTriplet.getValue3()); } -} \ No newline at end of file +} diff --git a/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java b/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java index d494fcb..bc02ab1 100644 --- a/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java +++ b/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java @@ -5,6 +5,6 @@ import java.util.Iterator; public interface AutoCloseableIterator extends Iterator, AutoCloseable { @Override - public void close(); + void close(); } diff --git a/src/main/java/net/locusworks/common/io/IOUtils.java b/src/main/java/net/locusworks/common/io/IOUtils.java index 7ce89f1..71c3bf4 100644 --- a/src/main/java/net/locusworks/common/io/IOUtils.java +++ b/src/main/java/net/locusworks/common/io/IOUtils.java @@ -172,7 +172,7 @@ public class IOUtils { */ public static void copy(final InputStream input, final Writer output, final Charset inputEncoding) throws IOException { - final InputStreamReader in = new InputStreamReader(input, inputEncoding.toString()); + final InputStreamReader in = new InputStreamReader(input, inputEncoding); copy(in, output); } diff --git a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java index 9ce2050..0fa5993 100644 --- a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java +++ b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java @@ -20,7 +20,7 @@ import com.google.gson.GsonBuilder; */ public class ObjectMapperHelper { - private static ObjectMapper mapper; + private static final ObjectMapper mapper; static { mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_NULL); @@ -145,4 +145,4 @@ public class ObjectMapperHelper { return new ObjectMapperListResults<>(ex); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/locusworks/common/properties/ImmutableProperties.java b/src/main/java/net/locusworks/common/properties/ImmutableProperties.java index 0884479..05a1eef 100644 --- a/src/main/java/net/locusworks/common/properties/ImmutableProperties.java +++ b/src/main/java/net/locusworks/common/properties/ImmutableProperties.java @@ -1,10 +1,11 @@ package net.locusworks.common.properties; +import java.io.Serial; import java.util.Properties; public class ImmutableProperties extends Properties { - private static final long serialVersionUID = 65942088008978137L; + @Serial private static final long serialVersionUID = 65942088008978137L; public ImmutableProperties() { super(); @@ -13,8 +14,8 @@ public class ImmutableProperties extends Properties { public ImmutableProperties(Properties props) { super(); if (props == null || props.isEmpty()) return; - - props.entrySet().forEach(item -> this.put(item.getKey(), item.getValue())); + + this.putAll(props); } @Override diff --git a/src/main/java/net/locusworks/common/properties/OrderedProperties.java b/src/main/java/net/locusworks/common/properties/OrderedProperties.java index dcefa70..410d7c9 100644 --- a/src/main/java/net/locusworks/common/properties/OrderedProperties.java +++ b/src/main/java/net/locusworks/common/properties/OrderedProperties.java @@ -24,15 +24,7 @@ package net.locusworks.common.properties; * * */ -import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.io.OutputStreamWriter; -import java.io.BufferedWriter; +import java.io.*; import java.util.Collections; import java.util.Date; import java.util.Enumeration; @@ -123,7 +115,7 @@ public class OrderedProperties extends LinkedHashMap { /** * use serialVersionUID from JDK 1.1.X for interoperability */ - private static final long serialVersionUID = 4112578634023874840L; + @Serial private static final long serialVersionUID = 4112578634023874840L; /** * A property list that contains default values for any keys not diff --git a/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java b/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java index 238c203..e45612f 100644 --- a/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java +++ b/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java @@ -8,7 +8,7 @@ import java.util.Base64; import net.locusworks.common.Charsets; -public class DataOutputStreamHelper extends DataOutputStream implements AutoCloseable{ +public class DataOutputStreamHelper extends DataOutputStream implements AutoCloseable { public DataOutputStreamHelper() { this(new ByteArrayOutputStream()); @@ -43,7 +43,7 @@ public class DataOutputStreamHelper extends DataOutputStream implements AutoClos try { super.out.close(); super.out = null; - } catch (Exception ex) {} + } catch (Exception ignored) {} } } diff --git a/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java b/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java index 2d13be6..88bef4e 100644 --- a/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java +++ b/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java @@ -1,6 +1,7 @@ package net.locusworks.common.utils; import java.io.IOException; +import java.io.Serial; import java.util.Date; import com.fasterxml.jackson.core.JsonGenerator; @@ -29,7 +30,7 @@ public class DateTimeStampSerializer extends StdSerializer { /** * */ - private static final long serialVersionUID = -4753139740916300831L; + @Serial private static final long serialVersionUID = -4753139740916300831L; public DateTimeStampSerializer() { this(null); diff --git a/src/main/java/net/locusworks/common/utils/Splitter.java b/src/main/java/net/locusworks/common/utils/Splitter.java index a29e914..d1af387 100644 --- a/src/main/java/net/locusworks/common/utils/Splitter.java +++ b/src/main/java/net/locusworks/common/utils/Splitter.java @@ -9,51 +9,51 @@ import static net.locusworks.common.utils.Checks.checkArguments; import static net.locusworks.common.utils.Checks.checkNotNull; public class Splitter { - + private String splitSeq; private boolean omitEmptyStrings = false; private int partition; private int limit; - + private static Splitter splitter; - + private Splitter(String seq) { this.splitSeq = seq; } - + private Splitter(int partition) { this.partition = partition; } - + public Splitter omitEmptyStrings() { this.omitEmptyStrings = true; return this; } - + public Splitter withLimit(int limit) { this.limit = limit; return this; } - + public MapSplitter withKeyValueSeparator(String separator) { checkArguments(!Utils.isEmptyString(separator), "Key value separator cannot be empty or null"); return new MapSplitter(this, separator); } - + public String[] splitToArray(String sentence) { List list = split(sentence); - return list.toArray(new String[list.size()]); + return list.toArray(new String[0]); } - + public List split(String sentence) { checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); List list = new ArrayList<>(); - + if (!Utils.isEmptyString(splitSeq)) populateForTrimmer(sentence, list); - else + else populateForFixedWidth(sentence, list); - + return limit > 0 ? list.subList(0, limit) : list; } @@ -66,64 +66,67 @@ public class Splitter { private void populateForTrimmer(String sentence, List list) { for (String s : sentence.split(splitSeq)) { - if (s == null || (omitEmptyStrings && s.trim().isEmpty())) continue; + if (s == null || (omitEmptyStrings && s.trim().isEmpty())) + continue; list.add(s.trim()); } } - + public static Splitter fixedLengthSplit(int partition) { checkArguments(partition > 0, "Partition has to be greater than 0"); splitter = new Splitter(partition); return splitter; } - + public static Splitter on(String split) { checkNotNull(split, "Split value provided was null"); splitter = new Splitter(split); return splitter; } - + public static Splitter onNewLine() { return on("\\r?\\n"); } - + public static Splitter onSpace() { return on(" "); } - + public static class MapSplitter { - - private Splitter splitter; + + private final Splitter splitter; private String separator; private boolean skipInvalid = false; - + private MapSplitter(Splitter splitter, String separator) { checkNotNull(splitter, "Splitter cannot be null"); - checkArguments(!Utils.isEmptyString(separator), "Key value separator cannot be empty or null"); + checkArguments(!Utils.isEmptyString(separator), + "Key value separator cannot be empty or null"); this.splitter = splitter; this.separator = separator; } - + public MapSplitter skipInvalidKeyValues() { this.skipInvalid = true; return this; } - + public Map split(String sentence) { checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); Map map = new LinkedHashMap<>(); - + for (String s : splitter.split(sentence)) { String[] keyValue = s.split(separator); try { checkArguments(keyValue.length == 2, "invalid length found for key value mapping"); } catch (IllegalArgumentException ex) { - if (!skipInvalid) throw ex; + if (!skipInvalid) + throw ex; continue; } map.put(keyValue[0], keyValue[1]); } - + return map; } } diff --git a/src/main/java/net/locusworks/common/utils/Utils.java b/src/main/java/net/locusworks/common/utils/Utils.java index 9cc887e..2257988 100644 --- a/src/main/java/net/locusworks/common/utils/Utils.java +++ b/src/main/java/net/locusworks/common/utils/Utils.java @@ -2,6 +2,7 @@ package net.locusworks.common.utils; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -47,7 +48,7 @@ public class Utils { } E firstVal = values[0]; - boolean equal = true; + boolean equal = !or; for (int i = 1; i < values.length; i++) { if (or) @@ -100,7 +101,7 @@ public class Utils { * * @param the type parameter * @param the type parameter - * @param mapClass The map class to map to i.e HashMap, TreeMap etc + * @param mapClass The map class to map to i.e. HashMap, TreeMap etc * @param mapKey the map key * @param mapValue the map value * @param data The data to place in the map @@ -109,9 +110,9 @@ public class Utils { */ @SuppressWarnings("unchecked") public static Map buildMap(Class mapClass, Class mapKey, Class mapValue, Object... data) { - Map results = new LinkedHashMap(); + Map results; try { - results = (Map) mapClass.newInstance(); + results = (Map) mapClass.getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new IllegalArgumentException("Unable to create instance of " + mapClass.getSimpleName()); } @@ -121,7 +122,7 @@ public class Utils { } Object key = null; - Integer step = -1; + int step = -1; for (Object value : data) { switch(++step % 2) { @@ -131,7 +132,7 @@ public class Utils { } if (value instanceof Class) { try { - value = ((Class)value).newInstance(); + value = ((Class)value).getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new IllegalArgumentException("Unable to create new instance of " + value); } @@ -150,7 +151,7 @@ public class Utils { results.put((K) key, (V) value); break; default: - throw new IllegalAccessError("Caculation for step was not a multiple of two. This shouldn't have happened"); + throw new IllegalAccessError("Calculation for step was not a multiple of two. This shouldn't have happened"); } } return results; @@ -166,10 +167,10 @@ public class Utils { */ @SuppressWarnings("unchecked") public static Set buildSet(Class setClass, Class setValue, Object... data) { - Set results = null; + Set results; try { - results = (Set) setClass.newInstance(); + results = (Set) setClass.getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new IllegalArgumentException("Unable to create instance of " + setClass.getSimpleName()); } @@ -192,21 +193,22 @@ public class Utils { * @return - Returns a HashMap using the data. */ public static HashMap buildStringHashMap(String... data) { - return new HashMap(buildMap(HashMap.class, String.class, String.class, (Object[]) data)); + return new HashMap<>(buildMap(HashMap.class, String.class, String.class, (Object[]) data)); } /** - * Clone list list. + * Clone list. * * @param the type parameter * @param list the list * * @return the list * @throws IllegalAccessException throw when an object in the list cannot be accessed through reflection - * @throws InstantiationException thrown when an boject in the list cannot be instantiated through reflection + * @throws InstantiationException thrown when an object in the list cannot be instantiated through reflection */ @SuppressWarnings("unchecked") - public static List cloneList(List list) throws InstantiationException, IllegalAccessException { + public static List cloneList(List list) + throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { List clone = new ArrayList<>(); for (E item : list) { clone.add((E) cloneObject(item)); @@ -221,8 +223,9 @@ public class Utils { * @throws IllegalAccessException thrown when the filed cannot be access * @throws InstantiationException Thrown when the object cannot be instantiated */ - public static Object cloneObject(Object obj) throws InstantiationException, IllegalAccessException { - Object clone = obj.getClass().newInstance(); + public static Object cloneObject(Object obj) + throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Object clone = obj.getClass().getDeclaredConstructor().newInstance(); for (Field field : obj.getClass().getDeclaredFields()) { try { field.setAccessible(true); @@ -241,9 +244,7 @@ public class Utils { field.set(clone, cloneObject(field.get(obj))); } } - } catch (NullPointerException ex) { - continue; - } + } catch (NullPointerException ignored) { } } return clone; } @@ -329,7 +330,7 @@ public class Utils { @SuppressWarnings("unchecked") public static Set extractFieldToSet(String fieldName, Iterable list) { Set newSet = new LinkedHashSet<>(); - Field field = null; + Field field; for (K item : Utils.safeList(list)) { try { field = getField(item.getClass(), fieldName); @@ -358,9 +359,9 @@ public class Utils { */ @SuppressWarnings("unchecked") public static List filterList(String fieldName, Object filter, Iterable list) { - List newList = new ArrayList(); + List newList = new ArrayList<>(); - Field field = null; + Field field; for (E item : safeList(list)) { try { @@ -384,7 +385,6 @@ public class Utils { /** * Find a value within the list. - * * Public method of type E called "findValue". * The method takes a parameter of type E called "itemToFind" and a String parameter called "fieldName" and a parameter of type List called "list". * The method loops through each item in the list and tries to get the class name and return it as an item. @@ -394,7 +394,7 @@ public class Utils { * @param list - The list to search * @param - The expected class of item to be found * @param - The expected class of the Iterable list - * @return - Returns returns the value of the specified field or null if there is no value. + * @return - Returns the value of the specified field or null if there is no value. * */ @SuppressWarnings({ "unchecked", "unlikely-arg-type" }) @@ -422,11 +422,10 @@ public class Utils { /** * Find values list. - * * Public method of type List called "findValues". - * The method takes a E type parameter called "valueToFind" and a String parameter called "fieldName" and a List parameter called "list". + * The method takes an E type parameter called "valueToFind" and a String parameter called "fieldName" and a List parameter called "list". * The method creates a new array list called "tmpList". - * The method then iterates through the list and gets all of the classes as items. + * The method then iterates through the list and gets all the classes as items. * If it can't find the fields, it will throw an illegal argument exception. * @param valueToFind - The value to find * @param fieldName - The field name @@ -461,7 +460,7 @@ public class Utils { } /** - * A replacement for String.format. Allows for to many parameters or to few + * A replacement for String.format. Allows for to many parameters or too few * Replaces {} in the string in order. * * @param message the message @@ -506,8 +505,8 @@ public class Utils { /** * Use reflection to get the field values - * @param clazz - * @param fieldName + * @param clazz the class to find the field + * @param fieldName the field name * @return field */ private static Field getField(Class clazz, String fieldName) { @@ -551,7 +550,7 @@ public class Utils { * @return map value */ public static T getMapValue(Map map, K key, T defaultValue) { - return map.containsKey(key) ? map.get(key) : defaultValue; + return map.getOrDefault(key, defaultValue); } /** @@ -574,7 +573,7 @@ public class Utils { * For collections checks to see if they are null or empty * for all others just checks if they are null * @param the expected class type of the object - * @param value The value to check + * @param values The value to check * @return true if the value is not valid, false otherwise */ @SafeVarargs @@ -588,7 +587,7 @@ public class Utils { * For collections checks to see if they are not null and not empty * for all others just checks if they are not null * @param the expected class type of the object - * @param value The value to check + * @param values The value to check * @return true if the value is valid, false otherwise */ @SafeVarargs @@ -635,7 +634,7 @@ public class Utils { } /** - * List to set set. + * List to set. * * @param the type parameter * @param valueSet the value set @@ -648,13 +647,12 @@ public class Utils { /** * Creates an array from a collection - * * Public method of type array called "makeArray". * The method takes a Collection object parameter called "collection" and a Class object parameter called "clazz". * The method first checks to see if the Collection object is null. If so, it returns null. * The method then creates an array called "results" and populates it with a new instance of "clazz" and the size of the collection. - * The method then creates a Int variable called "index" and sets the value to zero. - * The method then iterates through the collection and adds all of the items to the results array. + * The method then creates an Int variable called "index" and sets the value to zero. + * The method then iterates through the collection and adds all the items to the results array. * The method then returns the results array. * @param - The type parameter * @param collection - The collection @@ -683,7 +681,7 @@ public class Utils { * The method takes a Collection object parameter called "collection". * The method checks to see if the Collection Object is null. If so, it returns null. * Otherwise, it creates an ArrayList object called "list". - * It then iterates through the Collection Object and adds all of the collection items to that list. + * It then iterates through the Collection Object and adds all the collection items to that list. * The method then returns the list. * @param - The type parameter * @param collection - The collection @@ -693,11 +691,7 @@ public class Utils { if (collection == null) { return null; } - List list = new ArrayList<>(); - for (E item : collection) { - list.add(item); - } - return list; + return new ArrayList<>(collection); } /** @@ -716,13 +710,7 @@ public class Utils { return null; } - List newList = new ArrayList<>(); - - for (E item : array) { - newList.add(item); - } - - return newList; + return List.of(array); } /** @@ -752,11 +740,11 @@ public class Utils { * Public method of type Set called "makeSet". * The method takes an Iterable object parameter called "collection". * The method first creates a new HashSet called "set". - * The method then iterates through the collection and adds all of the items from it to the HashSet. + * The method then iterates through the collection and adds all the items from it to the HashSet. * The method then returns the HashSet. * @param - The type parameter * @param collection - The iterable object name. - * @return - Returns the HashSet with all of the items of the collection inside of it. + * @return - Returns the HashSet with all the items of the collection inside of it. */ public static Set makeSet(Iterable collection) { Set set = new HashSet<>(); @@ -767,7 +755,7 @@ public class Utils { } /** - * Map to list list. + * Map to list. * * @param the type parameter * @param the type parameter @@ -776,17 +764,12 @@ public class Utils { * @return the list */ public static List mapToList(Map map) { - List list = new ArrayList<>(); - for (V key : map.values()) { - list.add(key); - } - - return list; + return new ArrayList<>(map.values()); } /** - * Map to set set. + * Map to set. * * @param the type parameter * @param the type parameter @@ -795,13 +778,8 @@ public class Utils { * @return the set */ public static Set mapToSet(Map map) { - Set list = new HashSet<>(); - for (V key : map.values()) { - list.add(key); - } - - return list; + return new HashSet<>(map.values()); } /** @@ -833,7 +811,7 @@ public class Utils { */ public static Collection safeList(Collection list) { if (!validateValue(list)) { - return new ArrayList(); + return new ArrayList<>(); } return list; } @@ -864,7 +842,7 @@ public class Utils { */ public static Iterable safeList(Iterable list) { if (!validateValue(list)) { - return new ArrayList(); + return new ArrayList<>(); } return list; } @@ -879,7 +857,7 @@ public class Utils { */ public static List safeList(List list) { if (!validateValue(list)) { - return new ArrayList(); + return new ArrayList<>(); } return list; } @@ -892,17 +870,17 @@ public class Utils { */ public static Set safeSet(Set set) { if (set == null) { - return new HashSet(); + return new HashSet<>(); } return set; } /** * Checks to see if a string is not blank or null. - * if its not blank it will return the string + * if it's not blank it will return the string * otherwise it will return an empty string * @param string The string to check - * @return the passed in string if its not null either empty string + * @return the passed in string if it's not null either empty string */ public static String safeString(String string) { return isEmptyString(string) ? "" : string; @@ -917,18 +895,14 @@ public class Utils { * @return the to list */ public static List setToList(Set valueSet) { - List list = new ArrayList<>(); - for (E value : valueSet) { - list.add(value); - } - return list; + return new ArrayList<>(valueSet); } /** * Converts a String into an integer without exception * @param value The string value to convert * @param defaultValue The default value to return if the string cant be converted - * @return the integer representation of the passed in string or the default value if an exception occured + * @return the integer representation of the passed in string or the default value if an exception occurred */ public static Integer toInteger(String value, Integer defaultValue) { try { @@ -952,7 +926,7 @@ public class Utils { } public static List toList(Iterable iterable) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (E item : iterable) { list.add(item); } @@ -969,17 +943,13 @@ public class Utils { */ @SafeVarargs public static Set toSet(E... values) { - Set set = new LinkedHashSet<>(); - for (E value : values) { - set.add(value); - } - return set; + return new LinkedHashSet<>(Arrays.asList(values)); } public static List toByteList(byte[] bytes) { return IntStream.range(0, bytes.length) - .mapToObj(index -> new Byte(bytes[index])) + .mapToObj(index -> bytes[index]) .collect(Collectors.toList()); } @@ -1008,7 +978,7 @@ public class Utils { Optional test = StreamUtils.asStream(iterable) .filter(item -> count.getAndIncrement() == index) .findFirst(); - return test.isPresent() ? test.get() : null; + return test.orElse(null); } public static Consumer handleExceptionWrapper(ThrowingConsumer consumer) { @@ -1026,13 +996,13 @@ public class Utils { * The method takes an Object parameter called "value". * The method loops through and checks to see if value is empty. * If so, it will return that the value is not null and that it is not empty. - * Otherwise it will check to see the value is an instance of Map. + * Otherwise, it will check to see the value is an instance of Map. * If so, it will return that the value is not null and that it is not empty. - * Otherwise it will check to see if value is an instance of a String. + * Otherwise, it will check to see if value is an instance of a String. * If so, it will return that the value is not null and that it is not empty. - * Otherwise it will check to see if value is an instance of a Collection Object. - * If so, it will return that the value is not null and if the value.size is greater than zero. - * Otherwise it will just return that the value is not null and not empty. + * Otherwise, it will check to see if value is an instance of a Collection Object. + * If so, it will return that the value is not null and if the value size is greater than zero. + * Otherwise, it will just return that the value is not null and not empty. * @param - The type parameter * @param value - Value to validate * @return - Returns true if it is valid; false otherwise @@ -1047,25 +1017,21 @@ public class Utils { } else if (value instanceof Map){ return !((Map)value).isEmpty(); } else if (value instanceof Boolean) { - return ((Boolean)value) == true; + return ((Boolean) value); } - else if (!(value instanceof String)) { - return value != null; - } return !value.toString().trim().isEmpty(); } /** * Validates all given values. - * * Public method of type Boolean called "validateValues". * The method takes multiple V type objects called "objectToValidate". * The method creates a boolean type variable called "valid" and sets it to true. * The method then loops through all the objects and validates each value in the object. * The method sets valid equal to the result of true and false. * If the method cannot validate the object values, it will throw an exception. If so, it returns false. - * Otherwise, the method will return the variable called "valid'. + * Otherwise, the method will return the variable called "valid". * @param - The type parameter * @param objectsToValidate - Objects to validate * @return - Returns true if all objects are valid false otherwise. @@ -1084,7 +1050,7 @@ public class Utils { */ @SafeVarargs public static boolean validateValues(boolean or, V... objectsToValidate) { - boolean valid = true; + boolean valid = !or; try { for (Object obj : objectsToValidate) { if (or && validateValue(obj)) { @@ -1103,14 +1069,14 @@ public class Utils { public static boolean isJUnitRunning() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); Optional junit = StreamUtils.asStream(stackTrace) - .map(element -> element.getClassName()) + .map(StackTraceElement::getClassName) .filter(className -> className.startsWith("org.junit.")) .findFirst(); return junit.isPresent(); } private static Set> getWrapperTypes() { - Set> ret = new HashSet>(); + Set> ret = new HashSet<>(); ret.add(Boolean.class); ret.add(Character.class); ret.add(Byte.class); @@ -1122,4 +1088,4 @@ public class Utils { ret.add(String.class); return ret; } -} \ No newline at end of file +} diff --git a/src/test/java/net/locusworks/test/AESEncryptionTest.java b/src/test/java/net/locusworks/test/AESEncryptionTest.java index 794a91d..7854265 100644 --- a/src/test/java/net/locusworks/test/AESEncryptionTest.java +++ b/src/test/java/net/locusworks/test/AESEncryptionTest.java @@ -1,10 +1,10 @@ package net.locusworks.test; -import org.junit.Assert; -import org.junit.Test; - import net.locusworks.common.crypto.AES; import net.locusworks.common.utils.Utils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class AESEncryptionTest { @@ -12,10 +12,10 @@ public class AESEncryptionTest { public void testEncryption() { try { String encrypted = AES.createInstance().encrypt("hello world"); - Assert.assertTrue(String.format("Encrypted String is not blank? :%s", encrypted), !Utils.isEmptyString(encrypted)); + assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); } catch (Exception ex) { ex.printStackTrace(System.err); - Assert.fail(); + fail(); } } @@ -25,16 +25,16 @@ public class AESEncryptionTest { try { AES aes = AES.createInstance(); String encrypted = aes.encrypt(testString); - Assert.assertTrue(String.format("Encrypted String is not blank? :%s", encrypted), !Utils.isEmptyString(encrypted)); + assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); String decrypted = aes.decrypt(encrypted); - Assert.assertTrue(String.format("Decrypted String is not blank? :%s", decrypted), !Utils.isEmptyString(encrypted)); - - Assert.assertTrue("Test String and Original String the same? :%s", testString.equals(decrypted)); + assertFalse(Utils.isEmptyString(encrypted), String.format("Decrypted String is not blank? :%s", decrypted)); + + assertEquals(testString, decrypted, "Test String and Original String the same? :%s"); } catch (Exception ex) { ex.printStackTrace(System.err); - Assert.fail(); + fail(); } } diff --git a/src/test/java/net/locusworks/test/AllTests.java b/src/test/java/net/locusworks/test/AllTests.java index 7d9e8e9..0407b5a 100644 --- a/src/test/java/net/locusworks/test/AllTests.java +++ b/src/test/java/net/locusworks/test/AllTests.java @@ -1,11 +1,7 @@ package net.locusworks.test; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; +import org.junit.jupiter.api.extension.ExtendWith; -@RunWith(Suite.class) -@SuiteClasses({ AESEncryptionTest.class, FileReaderTest.class, HashSaltTest.class, RandomStringTest.class }) public class AllTests { } diff --git a/src/test/java/net/locusworks/test/FileReaderTest.java b/src/test/java/net/locusworks/test/FileReaderTest.java index 9296da6..b35f4da 100644 --- a/src/test/java/net/locusworks/test/FileReaderTest.java +++ b/src/test/java/net/locusworks/test/FileReaderTest.java @@ -1,6 +1,12 @@ package net.locusworks.test; -import static org.junit.Assert.*; +import net.locusworks.common.interfaces.AutoCloseableIterator; +import net.locusworks.common.utils.FileReader; +import net.locusworks.common.utils.FileReader.LineInfo; +import net.locusworks.common.utils.RandomString; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileOutputStream; @@ -10,26 +16,19 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import net.locusworks.common.interfaces.AutoCloseableIterator; -import net.locusworks.common.utils.FileReader; -import net.locusworks.common.utils.FileReader.LineInfo; -import net.locusworks.common.utils.RandomString; +import static org.junit.jupiter.api.Assertions.assertEquals; public class FileReaderTest { private static final String TEST_FILE = "test_file.txt"; - private static Map numLines = new LinkedHashMap<>(); + private static final Map numLines = new LinkedHashMap<>(); - @BeforeClass + @BeforeAll public static void setUpBeforeClass() throws Exception { File testFile = new File(TEST_FILE); FileOutputStream fos = new FileOutputStream(testFile); - Integer count = ThreadLocalRandom.current().nextInt(100); + int count = ThreadLocalRandom.current().nextInt(100); for (int i = 1; i <= count; i++) { String randomString = RandomString.getString(ThreadLocalRandom.current().nextInt(5, 100)) + "\n"; @@ -40,7 +39,7 @@ public class FileReaderTest { fos.close(); } - @AfterClass + @AfterAll public static void tearDownAfterClass() throws Exception { File file = new File(TEST_FILE); file.delete(); @@ -48,48 +47,50 @@ public class FileReaderTest { @Test public void testForLoop() { - Integer lineCount = 0; + int lineCount = 0; try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { for (LineInfo s : fr) { lineCount++; Integer lineNumber = s.getLineNumber(); - Integer lineLength = s.getLine().length(); + int lineLength = s.getLine().length(); Integer mapLineLength = numLines.get(lineNumber); - assertTrue(lineLength == (mapLineLength-1)); + assertEquals(lineLength, (mapLineLength - 1)); } } - assertTrue(lineCount == numLines.size()); + assertEquals(lineCount, numLines.size()); } @Test public void testIterator() { - Integer lineCount = 0; + int lineCount = 0; try(AutoCloseableIterator iter = new FileReader(Paths.get(TEST_FILE))) { while(iter.hasNext()) { lineCount++; LineInfo s = iter.next(); Integer lineNumber = s.getLineNumber(); - Integer lineLength = s.getLine().length(); + int lineLength = s.getLine().length(); Integer mapLineLength = numLines.get(lineNumber); - assertTrue(lineLength == (mapLineLength-1)); + assertEquals(lineLength, (mapLineLength - 1)); } } - assertTrue(lineCount == numLines.size()); + assertEquals(lineCount, numLines.size()); } @Test public void testForIterator() { - Integer lineCount = 0; - for(Iterator iter = new FileReader(Paths.get(TEST_FILE)); iter.hasNext();) { - lineCount++; - LineInfo s = iter.next(); - Integer lineNumber = s.getLineNumber(); - Integer lineLength = s.getLine().length(); - Integer mapLineLength = numLines.get(lineNumber); - assertTrue(lineLength == (mapLineLength-1)); + int lineCount = 0; + try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { + while (((Iterator) fr).hasNext()) { + lineCount++; + LineInfo s = ((Iterator) fr).next(); + Integer lineNumber = s.getLineNumber(); + int lineLength = s.getLine().length(); + Integer mapLineLength = numLines.get(lineNumber); + assertEquals(lineLength, (mapLineLength - 1)); + } + assertEquals(lineCount, numLines.size()); } - assertTrue(lineCount == numLines.size()); } } diff --git a/src/test/java/net/locusworks/test/HashSaltTest.java b/src/test/java/net/locusworks/test/HashSaltTest.java index 8851ac5..b42681f 100644 --- a/src/test/java/net/locusworks/test/HashSaltTest.java +++ b/src/test/java/net/locusworks/test/HashSaltTest.java @@ -1,22 +1,22 @@ package net.locusworks.test; -import org.junit.Assert; -import org.junit.Test; - import net.locusworks.common.crypto.HashSalt; import net.locusworks.common.utils.Utils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class HashSaltTest { - private static String samplePassword="Hello World"; + private static final String samplePassword="Hello World"; @Test public void testEncryption() { try { String hashSalt = HashSalt.createHash(samplePassword); - Assert.assertTrue(String.format("Encrypted String is not blank? :%s", hashSalt), !Utils.isEmptyString(hashSalt)); + assertFalse(Utils.isEmptyString(hashSalt), String.format("Encrypted String is not blank? :%s", hashSalt)); } catch(Exception ex) { - Assert.fail(); + fail(); } } @@ -25,9 +25,9 @@ public class HashSaltTest { try { String hashSalt = HashSalt.createHash(samplePassword); boolean decrypted = HashSalt.validatePassword(samplePassword, hashSalt); - Assert.assertTrue("Test String and Original String the same? :%s", decrypted); + assertTrue(decrypted, "Test String and Original String the same? :%s"); } catch(Exception ex) { - Assert.fail(); + fail(); } } diff --git a/src/test/java/net/locusworks/test/HashUtilsTest.java b/src/test/java/net/locusworks/test/HashUtilsTest.java index 1eaad9b..063a4f6 100644 --- a/src/test/java/net/locusworks/test/HashUtilsTest.java +++ b/src/test/java/net/locusworks/test/HashUtilsTest.java @@ -1,11 +1,10 @@ package net.locusworks.test; -import static org.junit.Assert.*; - -import org.apache.commons.codec.digest.DigestUtils; -import org.junit.Test; - import net.locusworks.common.utils.HashUtils; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class HashUtilsTest { @@ -15,24 +14,24 @@ public class HashUtilsTest { public void testMD5() throws Exception { String digestUtilsMD5 = DigestUtils.md5Hex(TEST_STRING.getBytes()); String hashUtilsMD5 = HashUtils.hash("MD5", TEST_STRING); - - assertTrue(digestUtilsMD5.equals(hashUtilsMD5)); + + assertEquals(digestUtilsMD5, hashUtilsMD5); } @Test public void testSHA1() throws Exception { String digestUtilsMD5 = DigestUtils.sha1Hex(TEST_STRING.getBytes()); String hashUtilsMD5 = HashUtils.hash("SHA-1", TEST_STRING); - - assertTrue(digestUtilsMD5.equals(hashUtilsMD5)); + + assertEquals(digestUtilsMD5, hashUtilsMD5); } @Test public void testSHA512() throws Exception { String digestUtilsMD5 = DigestUtils.sha512Hex(TEST_STRING.getBytes()); String hashUtilsMD5 = HashUtils.hash("SHA-512", TEST_STRING); - - assertTrue(digestUtilsMD5.equals(hashUtilsMD5)); + + assertEquals(digestUtilsMD5, hashUtilsMD5); } } diff --git a/src/test/java/net/locusworks/test/ImmutablesTest.java b/src/test/java/net/locusworks/test/ImmutablesTest.java index d8992a6..4269faa 100644 --- a/src/test/java/net/locusworks/test/ImmutablesTest.java +++ b/src/test/java/net/locusworks/test/ImmutablesTest.java @@ -1,12 +1,12 @@ package net.locusworks.test; -import static org.junit.Assert.*; - -import org.junit.Test; - import net.locusworks.common.immutables.Pair; import net.locusworks.common.immutables.Triplet; import net.locusworks.common.immutables.Unit; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ImmutablesTest { @@ -14,32 +14,32 @@ public class ImmutablesTest { @Test public void testUnit() { Unit unit = new Unit<>("Hello World"); - assertTrue(unit.getValue1().equals("Hello World")); + assertEquals("Hello World", unit.getValue1()); Unit unit2 = new Unit<>(2); - assertTrue(unit2.getValue1().equals(2)); + assertEquals(2, (int) unit2.getValue1()); } @Test public void testPair() { Pair pair1 = new Pair<>("Hello", "World"); - assertTrue(pair1.getValue1().equals("Hello")); - assertTrue(pair1.getValue2().equals("World")); + assertEquals("Hello", pair1.getValue1()); + assertEquals("World", pair1.getValue2()); Pair pair2 = new Pair<>("Foo", 25); - assertTrue(pair2.getValue1().equals("Foo")); - assertTrue(pair2.getValue2().equals(25)); + assertEquals("Foo", pair2.getValue1()); + assertEquals(25, (int) pair2.getValue2()); Pair pair3 = new Pair<>(1, 23); - assertTrue(pair3.getValue1().equals(1)); - assertTrue(pair3.getValue2().equals(23)); + assertEquals(1, (int) pair3.getValue1()); + assertEquals(23, (int) pair3.getValue2()); } @Test public void testTriplet() { Triplet triplet1 = new Triplet<>("Hello", 24, "World"); - assertTrue(triplet1.getValue1().equals("Hello")); - assertTrue(triplet1.getValue2().equals(24)); - assertTrue(triplet1.getValue3().equals("World")); + assertEquals("Hello", triplet1.getValue1()); + assertEquals(24, (int) triplet1.getValue2()); + assertEquals("World", triplet1.getValue3()); } } diff --git a/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java b/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java index e27b861..5c0af39 100644 --- a/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java +++ b/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java @@ -3,20 +3,18 @@ */ package net.locusworks.test; -import static org.junit.Assert.*; - import java.util.List; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - import net.locusworks.common.immutables.Triplet; import net.locusworks.common.objectmapper.ObjectMapperHelper; import net.locusworks.common.utils.Utils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import static net.locusworks.common.utils.Constants.JUNIT_TEST_CHECK; import static net.locusworks.common.utils.Constants.LOG4J_CONFIG_PROPERTY; +import static org.junit.jupiter.api.Assertions.*; /** * Test cases to test ObjectMapperHelper.class @@ -31,14 +29,14 @@ public class ObjectMapperHelperTest { /** * @throws java.lang.Exception exception */ - @BeforeClass + @BeforeAll public static void setUpBeforeClass() throws Exception { System.setProperty(LOG4J_CONFIG_PROPERTY, "log4j2-test.xml"); System.setProperty(JUNIT_TEST_CHECK, "true"); test = new Triplet("Hello", 24, "World"); } - @AfterClass + @AfterAll public static void tearDownAfterClass() throws Exception { System.clearProperty(LOG4J_CONFIG_PROPERTY); System.clearProperty(JUNIT_TEST_CHECK); @@ -56,8 +54,8 @@ public class ObjectMapperHelperTest { assertTrue(value != null && !value.trim().isEmpty()); Triplet tmp = ObjectMapperHelper.readValue(value, Triplet.class).getResults(); - assertTrue(tmp != null); - assertTrue(tmp.equals(test)); + assertNotNull(tmp); + assertEquals(tmp, test); } @SuppressWarnings("rawtypes") @@ -67,7 +65,7 @@ public class ObjectMapperHelperTest { String value = ObjectMapperHelper.writeValue(htrList).getResults(); assertTrue(value != null && !value.trim().isEmpty()); List tmpList = ObjectMapperHelper.readListValue(value, Triplet.class).getResults(); - assertTrue(tmpList != null && tmpList.size() > 0); + assertTrue(tmpList != null && !tmpList.isEmpty()); } } diff --git a/src/test/java/net/locusworks/test/PropertiesManagerTest.java b/src/test/java/net/locusworks/test/PropertiesManagerTest.java index 0c9f166..698e8ee 100644 --- a/src/test/java/net/locusworks/test/PropertiesManagerTest.java +++ b/src/test/java/net/locusworks/test/PropertiesManagerTest.java @@ -1,17 +1,16 @@ package net.locusworks.test; -import static org.junit.Assert.*; - import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Properties; -import org.junit.AfterClass; -import org.junit.Test; - import net.locusworks.common.configuration.PropertiesManager; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /** * Test cases for the properties manager class @@ -31,7 +30,7 @@ public class PropertiesManagerTest { USER_EXPIRATION_DAYS("userExpirationDays"), LOG_LEVEL("logLevel"); - private String value; + private final String value; private Configuration(String value) { this.value = value; @@ -51,7 +50,7 @@ public class PropertiesManagerTest { } } - @AfterClass + @AfterAll public static void removeSavedProps() { File tmp = new File(TMP_PROPS); if (tmp.exists()) { @@ -63,7 +62,7 @@ public class PropertiesManagerTest { public void testPropertiesLoad() { try { Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); - assertTrue(props != null); + assertNotNull(props); assertTrue(props.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); assertTrue(props.containsKey(Configuration.DB_HOST.toString())); assertTrue(props.containsKey(Configuration.DB_PORT.toString())); @@ -78,9 +77,9 @@ public class PropertiesManagerTest { try { Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); Properties tmp = new Properties(); - assertTrue(tmp.keySet().size() == 0); + assertEquals(0, tmp.keySet().size()); PropertiesManager.addConfiguration(tmp, props); - assertTrue(tmp.keySet().size() == ENTRY_SIZE); + assertEquals(ENTRY_SIZE, tmp.keySet().size()); assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); @@ -95,11 +94,12 @@ public class PropertiesManagerTest { try { Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); Properties tmp = new Properties(); - assertTrue(props.keySet().size() == ENTRY_SIZE); - assertTrue(tmp.keySet().size() == 0); + assert props != null; + assertEquals(ENTRY_SIZE, props.keySet().size()); + assertEquals(0, tmp.keySet().size()); PropertiesManager.removeConfiguration(props, tmp); - assertTrue(props.keySet().size() == 0); - assertTrue(tmp.keySet().size() == 0); + assertEquals(0, props.keySet().size()); + assertEquals(0, tmp.keySet().size()); } catch (IOException e) { fail(e.getMessage()); } @@ -112,7 +112,7 @@ public class PropertiesManagerTest { Path tmpFile = Paths.get(TMP_PROPS); PropertiesManager.saveConfiguration(props, tmpFile, "test propertis"); Properties tmp = PropertiesManager.loadConfiguration(tmpFile); - assertTrue(tmp.keySet().size() == ENTRY_SIZE); + assertEquals(ENTRY_SIZE, tmp.keySet().size()); assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); diff --git a/src/test/java/net/locusworks/test/RandomStringTest.java b/src/test/java/net/locusworks/test/RandomStringTest.java index 8e411b0..50a32e1 100644 --- a/src/test/java/net/locusworks/test/RandomStringTest.java +++ b/src/test/java/net/locusworks/test/RandomStringTest.java @@ -1,17 +1,16 @@ package net.locusworks.test; -import static org.junit.Assert.*; - -import org.junit.Test; - import net.locusworks.common.utils.RandomString; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class RandomStringTest { @Test public void testStaticBytes() { for (Integer length = 3; length < 50; length++) { - assertTrue(RandomString.getBytes(length).length == length); + assertEquals(RandomString.getBytes(length).length, (int) length); } } @@ -19,7 +18,7 @@ public class RandomStringTest { public void testStaticString() { for (Integer length = 3; length < 50; length++) { String random = RandomString.getString(length); - assertTrue(random.length() == length); + assertEquals(random.length(), (int) length); } } diff --git a/src/test/java/net/locusworks/test/UtilsTest.java b/src/test/java/net/locusworks/test/UtilsTest.java index e16587d..389e0dd 100644 --- a/src/test/java/net/locusworks/test/UtilsTest.java +++ b/src/test/java/net/locusworks/test/UtilsTest.java @@ -1,10 +1,18 @@ package net.locusworks.test; -import static org.junit.Assert.*; - -import org.junit.Test; - import net.locusworks.common.utils.Utils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.ParameterizedTest; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; /** * Test cases for the Utils class @@ -20,6 +28,30 @@ public class UtilsTest { assertTrue(Utils.safeString(null).isEmpty()); assertFalse(Utils.safeString("hello world").isEmpty()); } + + + @Test + public void testAreEqual() { + String val1 = "H"; + String val2 = "H"; + assertTrue(Utils.areEqual(val1, val2)); + + assertThrows(IllegalArgumentException.class, () -> Utils.areEqual("1")); + } + @ParameterizedTest + @MethodSource("areEqualParams") + public void testAreEqualWithParams(List objectList, boolean or, boolean equal) { + assertEquals(equal, Utils.areEqual(or, objectList.toArray())); + } + + static Stream areEqualParams() { + return Stream.of( + Arguments.of(List.of("Hello", "Hello"), false, true), + Arguments.of(List.of("Hello", "World"), false, false), + Arguments.of(List.of("Hello", "World", "Hello"), true, true), + Arguments.of(List.of("Hello", "World", "Fair"), true, false) + ); + } @Test public void testEmptyString() { @@ -32,8 +64,55 @@ public class UtilsTest { @Test public void testToInteger() { - assertTrue(Utils.toInteger("Hello word", 2) == 2); - assertTrue(Utils.toInteger("23", 5023) == 23); + assertEquals(2, (int) Utils.toInteger("Hello word", 2)); + assertEquals(23, (int) Utils.toInteger("23", 5023)); + } + + @ParameterizedTest + @MethodSource("validateValueParams") + public void testValidateValue(V value, boolean valid) { + assertEquals(valid, Utils.validateValue(value)); + } + + static Stream validateValueParams() { + return Stream.of( + Arguments.of(null, false), + Arguments.of(Collections.emptyList(), false), + Arguments.of(Map.of(), false), + Arguments.of(false, false), + Arguments.of("", false), + Arguments.of("Hello", true), + Arguments.of(List.of("Hello"), true), + Arguments.of(Map.of("Hello", "World"), true), + Arguments.of(true, true) + ); + } + + @ParameterizedTest + @MethodSource("areValidParams") + public void testValidateValues(List objects, boolean or, boolean valid) { + assertEquals(valid, Utils.areValid(objects.toArray())); + assertEquals(!valid, Utils.areNotValid(objects.toArray())); + } + + static Stream areValidParams() { + return Stream.of( + Arguments.of(List.of("Hello", List.of("Hello"), Map.of("Hello", "World"), true), false, true), + Arguments.of(List.of(Collections.emptyList(), Map.of(), false), false, false) + ); + } + + @Test + public void testBuildMap() { + Map mymap = Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", "World"); + assertNotNull(mymap); + assertEquals(1, mymap.size()); + assertEquals("World", mymap.get("Hello")); + } + + @Test + public void testIsJunitRunning() { + assertTrue(Utils.isJUnitRunning()); } } From 3cd7639da1775028c4e55817c3141863a23f4610 Mon Sep 17 00:00:00 2001 From: Isaac Parenteau Date: Mon, 18 Sep 2023 18:32:52 -0500 Subject: [PATCH 2/4] More unit tests for jdk17 --- .../common/annotations/MapValue.java | 2 +- .../net/locusworks/common/utils/Utils.java | 198 +++++++----------- .../java/net/locusworks/test/UtilsTest.java | 134 +++++++++++- 3 files changed, 212 insertions(+), 122 deletions(-) diff --git a/src/main/java/net/locusworks/common/annotations/MapValue.java b/src/main/java/net/locusworks/common/annotations/MapValue.java index b1845e7..951698d 100644 --- a/src/main/java/net/locusworks/common/annotations/MapValue.java +++ b/src/main/java/net/locusworks/common/annotations/MapValue.java @@ -13,7 +13,7 @@ import java.lang.annotation.Target; * @author Isaac Parenteau * */ -@Target({ElementType.FIELD, ElementType.TYPE}) +@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface MapValue { diff --git a/src/main/java/net/locusworks/common/utils/Utils.java b/src/main/java/net/locusworks/common/utils/Utils.java index 2257988..95af3d4 100644 --- a/src/main/java/net/locusworks/common/utils/Utils.java +++ b/src/main/java/net/locusworks/common/utils/Utils.java @@ -4,17 +4,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -24,8 +14,6 @@ import java.util.stream.IntStream; import net.locusworks.common.annotations.MapValue; import net.locusworks.common.interfaces.ThrowingConsumer; -import java.util.Set; - /*** * Utils class with generic methods * @author Isaac Parenteau @@ -110,49 +98,33 @@ public class Utils { */ @SuppressWarnings("unchecked") public static Map buildMap(Class mapClass, Class mapKey, Class mapValue, Object... data) { + Objects.requireNonNull(mapKey, "Null key value"); + Objects.requireNonNull(mapValue, "Null map value"); + Map results; try { results = (Map) mapClass.getDeclaredConstructor().newInstance(); } catch (Exception ex) { - throw new IllegalArgumentException("Unable to create instance of " + mapClass.getSimpleName()); + throw new IllegalArgumentException("Unable to create instance of " + mapClass.getSimpleName() + " e: " + ex.getMessage()); } if (data.length % 2 != 0) { throw new IllegalArgumentException("Odd number of arguments provided"); } - Object key = null; - int step = -1; + for(int i = 0; i < data.length; i+=2) { + Object key = data[i]; + Object value = data[i + 1]; - for (Object value : data) { - switch(++step % 2) { - case 0: - if (mapKey == null) { - throw new IllegalArgumentException("Null key value"); - } - if (value instanceof Class) { - try { - value = ((Class)value).getDeclaredConstructor().newInstance(); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to create new instance of " + value); - } - } - - if (!mapKey.isInstance(value)) { - throw new IllegalArgumentException("Key is not the correct instance. Expecting " + mapKey.getName() + " Received " + value.getClass().getName()); - } - - key = value; - continue; - case 1: - if (!mapValue.isInstance(value)) { - throw new IllegalArgumentException("Value is not the correct instance. Expecting " + mapValue.getName() + " Received " + value.getClass().getName()); - } - results.put((K) key, (V) value); - break; - default: - throw new IllegalAccessError("Calculation for step was not a multiple of two. This shouldn't have happened"); + if (!mapKey.isInstance(key)) { + throw new IllegalArgumentException("Key is not the correct instance. Expecting " + mapKey.getName() + " Received " + key.getClass().getName()); } + + if (!mapValue.isInstance(value)) { + throw new IllegalArgumentException("Value is not the correct instance. Expecting " + mapValue.getName() + " Received " + value.getClass().getName()); + } + results.put((K) key, (V) value); + } return results; } @@ -161,28 +133,24 @@ public class Utils { * Builds a set of objects * @param setClass Class type of the set to create * @param setValue Class type of the objects being placed in the set - * @param Class type of the return object (should be the same as setValue) + * @param Class type of the return object (should be the same as setValue) * @param data Data to place inside the set * @return Set filled with the data + * @deprecated since JDk11 can use Set.of */ @SuppressWarnings("unchecked") - public static Set buildSet(Class setClass, Class setValue, Object... data) { - Set results; + public static Set buildSet(Class setClass, Class setValue, E... data) { + return buildSet(data); + } - try { - results = (Set) setClass.getDeclaredConstructor().newInstance(); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to create instance of " + setClass.getSimpleName()); - } - - for (Object value : data) { - if (!setValue.isInstance(value)) { - throw new IllegalArgumentException("Value is not the correct instance. Expecting " + setValue.getName() + " Received " + value.getClass().getName()); - } - results.add((V) value); - } - - return results; + /** + * Builds a set of objects + * @param data the data to set + * @return the set + * @param the type + */ + @SafeVarargs public static Set buildSet(E... data) { + return Set.of(data); } /** @@ -197,23 +165,12 @@ public class Utils { } /** - * Clone list. + * Clone list and makes it unmodifiable. * - * @param the type parameter - * @param list the list - * - * @return the list - * @throws IllegalAccessException throw when an object in the list cannot be accessed through reflection - * @throws InstantiationException thrown when an object in the list cannot be instantiated through reflection + * @return unmodifiable list */ - @SuppressWarnings("unchecked") - public static List cloneList(List list) - throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - List clone = new ArrayList<>(); - for (E item : list) { - clone.add((E) cloneObject(item)); - } - return clone; + public static List cloneList(List list) { + return Collections.unmodifiableList(list); } /** @@ -223,7 +180,8 @@ public class Utils { * @throws IllegalAccessException thrown when the filed cannot be access * @throws InstantiationException Thrown when the object cannot be instantiated */ - public static Object cloneObject(Object obj) + @SuppressWarnings("unchecked") + public static O cloneObject(O obj) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Object clone = obj.getClass().getDeclaredConstructor().newInstance(); for (Field field : obj.getClass().getDeclaredFields()) { @@ -232,9 +190,10 @@ public class Utils { if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())){ continue; } - if (field.getType().isPrimitive() || field.getType().equals(String.class) - || field.getType().getSuperclass().equals(Number.class) - || field.getType().equals(Boolean.class)){ + + Class type = field.getType(); + + if (type.isPrimitive() || getWrapperTypes().contains(type)) { field.set(clone, field.get(obj)); } else { Object childObj = field.get(obj); @@ -246,7 +205,7 @@ public class Utils { } } catch (NullPointerException ignored) { } } - return clone; + return (O) clone; } /*** @@ -260,18 +219,17 @@ public class Utils { Class clazz = obj.getClass(); Map map = new LinkedHashMap<>(); - boolean hasAnnotations = clazz.isAnnotationPresent(MapValue.class); + for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); + boolean hasAnnotations = field.isAnnotationPresent(MapValue.class); + String key = field.getName(); Object value = field.get(obj); if (value == null) continue; if (hasAnnotations) { - if (!field.isAnnotationPresent(MapValue.class)) { - continue; - } MapValue annotation = field.getDeclaredAnnotation(MapValue.class); if (annotation.ignore()) { continue; @@ -303,6 +261,12 @@ public class Utils { return convert; } + /** + * Converts an object toa string map; + * @param obj the object to convert + * @return the map + * @throws Exception thrown if something happens + */ public static Map convertToStringMap(Object obj) throws Exception { return convertToStringMap(convertToMap(obj)); } @@ -459,6 +423,27 @@ public class Utils { return tmpList; } + /** + * Use reflection to get the field values + * @param clazz the class to find the field + * @param fieldName the field name + * @return field + */ + private static Field getField(Class clazz, String fieldName) { + Field field = null; + while(clazz != null) { + try { + field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + break; + } catch(Exception ex) { + clazz = clazz.getSuperclass(); + } + } + + return field; + } + /** * A replacement for String.format. Allows for to many parameters or too few * Replaces {} in the string in order. @@ -503,27 +488,6 @@ public class Utils { return obj == null ? "Unknown" : (verbose ? obj.getClass().getName() : obj.getClass().getSimpleName()); } - /** - * Use reflection to get the field values - * @param clazz the class to find the field - * @param fieldName the field name - * @return field - */ - private static Field getField(Class clazz, String fieldName) { - Field field = null; - while(clazz != null) { - try { - field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); - break; - } catch(Exception ex) { - clazz = clazz.getSuperclass(); - } - } - - return field; - } - /** * Get a value from a map * @@ -1076,16 +1040,16 @@ public class Utils { } private static Set> getWrapperTypes() { - Set> ret = new HashSet<>(); - ret.add(Boolean.class); - ret.add(Character.class); - ret.add(Byte.class); - ret.add(Short.class); - ret.add(Integer.class); - ret.add(Long.class); - ret.add(Float.class); - ret.add(Double.class); - ret.add(String.class); - return ret; + return Set.of( + Boolean.class, + Character.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + String.class + ); } } diff --git a/src/test/java/net/locusworks/test/UtilsTest.java b/src/test/java/net/locusworks/test/UtilsTest.java index 389e0dd..cd324c5 100644 --- a/src/test/java/net/locusworks/test/UtilsTest.java +++ b/src/test/java/net/locusworks/test/UtilsTest.java @@ -1,15 +1,14 @@ package net.locusworks.test; +import net.locusworks.common.annotations.MapValue; import net.locusworks.common.utils.Utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.ParameterizedTest; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.lang.reflect.InvocationTargetException; +import java.util.*; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; @@ -110,9 +109,136 @@ public class UtilsTest { assertEquals("World", mymap.get("Hello")); } + @Test + public void testCloneList() { + String[] values = new String[]{ + "Hello", + "world" + }; + List cloned = Utils.cloneList(new ArrayList<>(Arrays.stream(values).toList())); + assertEquals(2, cloned.size()); + assertThrows(UnsupportedOperationException.class, () -> cloned.add("asdf")); + } + + @Test + public void testCloneObject() + throws InvocationTargetException, InstantiationException, IllegalAccessException, + NoSuchMethodException { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + + TestClass clazz3 = new TestClass(); + clazz3.field1 = "hi"; + clazz3.field2 = "smash"; + clazz3.field3 = "world"; + + clazz.testClass = clazz3; + + TestClass clazz2 = Utils.cloneObject(clazz); + assertNotNull(clazz2); + assertEquals(clazz.field1, clazz2.field1); + assertEquals(clazz.field2, clazz2.field2); + assertEquals(clazz.field3, clazz2.field3); + assertEquals(clazz.field5, clazz2.field5); + assertEquals(clazz.number, clazz2.number); + assertEquals(clazz.aBoolean, clazz2.aBoolean); + assertNotNull(clazz2.field4); + assertNull(clazz2.nullField); + } + + @Test + public void testCovertToMap() throws Exception { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + + TestClass clazz3 = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + clazz.testClass = clazz3; + + Map converted = Utils.convertToMap(clazz); + + assertEquals(7, converted.size()); + assertEquals("hi", converted.get("other_field")); + } + + @Test + public void testConvertToStringMap() throws Exception { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + Map converted = Utils.convertToStringMap(clazz); + assertEquals(6, converted.size()); + assertEquals("hi", converted.get("other_field")); + + converted = Utils.convertToStringMap(converted); + assertEquals(6, converted.size()); + assertEquals("hi", converted.get("other_field")); + } + + @Test + public void testBuildStringMap() { + Map myMap = Utils.buildStringHashMap("hello", "world"); + assertNotNull(myMap); + assertEquals(1, myMap.size()); + } + + @Test + public void testBuildMapExceptions() { + Throwable ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(String.class, String.class, String.class, "" )); + + assertNotNull(ex); + + ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello")); + + assertEquals("Odd number of arguments provided", ex.getMessage()); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, new Object(), "Hello")); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", new Object())); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, Object.class, String.class, "Hello", new Object())); + } + + @Test + public void testBuildSet() { + Set myset = Utils.buildSet("Hello", "World"); + assertNotNull(myset); + assertEquals(2, myset.size()); + } + @Test public void testIsJunitRunning() { assertTrue(Utils.isJUnitRunning()); } + public static class TestClass { + @MapValue("other_field") + private String field1; + + @MapValue(ignore = true) + private String field2; + + + private String field3; + + private final String field4 = "final"; + @MapValue + private int field5 = 5; + + private Boolean aBoolean = Boolean.valueOf("true"); + + private Double number = 1.0d; + + private String nullField = null; + + private TestClass testClass; + } + } From 800cabda34e4d61b706bc4e7b7741a2e5ed2d7f5 Mon Sep 17 00:00:00 2001 From: Isaac Parenteau Date: Tue, 19 Sep 2023 19:53:15 -0500 Subject: [PATCH 3/4] More unit tests --- .../net/locusworks/common/crypto/AES.java | 16 +- .../net/locusworks/common/utils/Checks.java | 4 +- .../locusworks/common/utils/RandomString.java | 83 ++- .../net/locusworks/common/utils/Splitter.java | 80 ++- .../net/locusworks/common/utils/Utils.java | 175 ++---- .../utils/DateTimeStampSerializerTest.java | 233 ++++++++ .../utils}/FileReaderTest.java | 18 +- .../{test => common/utils}/HashUtilsTest.java | 2 +- .../common/utils/RandomStringTest.java | 30 + .../locusworks/common/utils/SplitterTest.java | 81 +++ .../common/utils/StreamUtilsTest.java | 19 + .../locusworks/common/utils/UtilsTest.java | 512 ++++++++++++++++++ .../net/locusworks/test/RandomStringTest.java | 25 - .../java/net/locusworks/test/UtilsTest.java | 244 --------- 14 files changed, 1066 insertions(+), 456 deletions(-) create mode 100644 src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java rename src/test/java/net/locusworks/{test => common/utils}/FileReaderTest.java (76%) rename src/test/java/net/locusworks/{test => common/utils}/HashUtilsTest.java (96%) create mode 100644 src/test/java/net/locusworks/common/utils/RandomStringTest.java create mode 100644 src/test/java/net/locusworks/common/utils/SplitterTest.java create mode 100644 src/test/java/net/locusworks/common/utils/StreamUtilsTest.java create mode 100644 src/test/java/net/locusworks/common/utils/UtilsTest.java delete mode 100644 src/test/java/net/locusworks/test/RandomStringTest.java delete mode 100644 src/test/java/net/locusworks/test/UtilsTest.java diff --git a/src/main/java/net/locusworks/common/crypto/AES.java b/src/main/java/net/locusworks/common/crypto/AES.java index b44355a..d329715 100644 --- a/src/main/java/net/locusworks/common/crypto/AES.java +++ b/src/main/java/net/locusworks/common/crypto/AES.java @@ -41,15 +41,18 @@ public class AES { private String seed; + private RandomString randomizer; + private void initSecureKey(String seed) { try { SecureRandom sr = getSecureRandom(seed); KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE); generator.init(128, sr); - init(generator.generateKey().getEncoded(), sr); + + randomizer = RandomString.newInstance(sr); + init(generator.generateKey().getEncoded()); } catch (Exception ex) { - System.err.println(ex); - throw new IllegalArgumentException("Unable to initalize encryption:", ex); + throw new IllegalArgumentException("Unable to initialize encryption:", ex); } } @@ -69,13 +72,12 @@ public class AES { /** * Initialize the aes engine * @param key secret key to use - * @param sr the secure random class */ - private void init(final byte[] key, SecureRandom sr) { + private void init(final byte[] key) { try { this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITH, PROVIDER); this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE); - this.ivParamSpec = new IvParameterSpec(RandomString.getBytes(16, sr)); + this.ivParamSpec = new IvParameterSpec(randomizer.getBytes(16)); } catch (Exception ex) { throw new IllegalArgumentException("Unable to initialize encryption:", ex); } @@ -130,7 +132,7 @@ public class AES { } public static AES createInstance() { - return createInstance(RandomString.getString(16)); + return createInstance(RandomString.getInstance().getString(16)); } public static AES createInstance(byte[] byteSeed) { diff --git a/src/main/java/net/locusworks/common/utils/Checks.java b/src/main/java/net/locusworks/common/utils/Checks.java index 85ef34b..af2bab6 100644 --- a/src/main/java/net/locusworks/common/utils/Checks.java +++ b/src/main/java/net/locusworks/common/utils/Checks.java @@ -1,5 +1,7 @@ package net.locusworks.common.utils; +import java.util.Objects; + public class Checks { public static void checkArguments(boolean expression, String error) { @@ -19,7 +21,7 @@ public class Checks { } public static void checkNotNull(Object item, String error) { - if (item == null) throw new IllegalAccessError("Provided item is null"); + Objects.requireNonNull(item, error); } } diff --git a/src/main/java/net/locusworks/common/utils/RandomString.java b/src/main/java/net/locusworks/common/utils/RandomString.java index 00def2a..86b041f 100644 --- a/src/main/java/net/locusworks/common/utils/RandomString.java +++ b/src/main/java/net/locusworks/common/utils/RandomString.java @@ -1,5 +1,6 @@ package net.locusworks.common.utils; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Objects; import java.util.Random; @@ -17,34 +18,30 @@ public class RandomString { private Random random; private char[] symbols; - private int length; - + private static RandomString instance; - - private RandomString(Integer length) { - this(length, new SecureRandom()); - } - - private RandomString(Integer length, Random random) { - this(length, random, ALPHA_NUMERIC); - } - - private RandomString(Integer length, Random random, String symbols) { - if (length < 1) throw new IllegalArgumentException("Length has to be greater than 1"); - if (symbols.length() < 2) throw new IllegalArgumentException("Symbols need to be greater than 2"); - this.random = Objects.requireNonNull(random); - this.symbols = symbols.toCharArray(); - } - - private synchronized final void setRandom(Random random) { - this.random = random; + + private RandomString() { + Random random; + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + random = new SecureRandom(); + } + init(random); } - private synchronized final void setLength(int length) { - this.length = length; + private RandomString(Random random) { + init(random); } - public String nextString() { + private void init(Random random) { + this.random = Objects.requireNonNull(random, "Random generator cannot be null"); + this.symbols = ALPHA_NUMERIC.toCharArray(); + } + + private String nextString(int length) { + if (length < 1) throw new IllegalArgumentException("String Length has to be greater than 0"); char[] buffer = new char[length]; for (int index = 0; index < buffer.length; index++) { buffer[index] = symbols[random.nextInt(symbols.length)]; @@ -52,28 +49,28 @@ public class RandomString { return new String(buffer); } - public static String getString(Integer length) { - if (instance == null) { - instance = new RandomString(length); - } - instance.setLength(length); - return instance.nextString(); + public String getString(Integer length) { + return this.nextString(length); } - - public static String getString(Integer length, Random random) { - if (instance == null) { - instance = new RandomString(length); - } - instance.setLength(length); - instance.setRandom(random); - return instance.nextString(); - } - - public static byte[] getBytes(Integer length) { + + public byte[] getBytes(Integer length) { return getString(length).getBytes(UTF_8); } - - public static byte[] getBytes(Integer length, Random random) { - return getString(length, random).getBytes(UTF_8); + + public static RandomString getInstance() { + if (instance == null) { + instance = new RandomString(); + } + return instance; + } + + public static RandomString newInstance() { + instance = new RandomString(); + return instance; + } + + public static RandomString newInstance(Random random) { + instance = new RandomString(random); + return instance; } } diff --git a/src/main/java/net/locusworks/common/utils/Splitter.java b/src/main/java/net/locusworks/common/utils/Splitter.java index d1af387..5b2ba8f 100644 --- a/src/main/java/net/locusworks/common/utils/Splitter.java +++ b/src/main/java/net/locusworks/common/utils/Splitter.java @@ -8,56 +8,82 @@ import java.util.Map; import static net.locusworks.common.utils.Checks.checkArguments; import static net.locusworks.common.utils.Checks.checkNotNull; +/** + * Class to help split a string in various ways. + * By partition (fixed length split) or by sequence (look for a specific string sequence). + * it can also split on new line or not. + */ public class Splitter { - + private enum SplitterType { + PARTITION, + SEQUENCE + } private String splitSeq; private boolean omitEmptyStrings = false; private int partition; private int limit; - + private final SplitterType splitterType; private static Splitter splitter; private Splitter(String seq) { this.splitSeq = seq; + this.splitterType = SplitterType.SEQUENCE; } private Splitter(int partition) { this.partition = partition; + this.splitterType = SplitterType.PARTITION; } + /** + * Remove empty string from the resulting lists + * @return this + */ public Splitter omitEmptyStrings() { this.omitEmptyStrings = true; return this; } + /** + * Return a subset of the resulting list + * @param limit how many items to retrieve + * @return this + */ public Splitter withLimit(int limit) { this.limit = limit; return this; } - public MapSplitter withKeyValueSeparator(String separator) { - checkArguments(!Utils.isEmptyString(separator), "Key value separator cannot be empty or null"); - return new MapSplitter(this, separator); - } - + /** + * Return an array instead of a list + * @param sentence the string sentence to split + * @return this + */ public String[] splitToArray(String sentence) { List list = split(sentence); return list.toArray(new String[0]); } + /** + * Split the string + * @param sentence the string to split + * @return the resulting list. + */ public List split(String sentence) { checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); List list = new ArrayList<>(); - if (!Utils.isEmptyString(splitSeq)) - populateForTrimmer(sentence, list); - else + if (splitterType == SplitterType.PARTITION) { populateForFixedWidth(sentence, list); + } else { + populateForTrimmer(sentence, list); + } return limit > 0 ? list.subList(0, limit) : list; } private void populateForFixedWidth(String sentence, List list) { + checkArguments(partition > 0, "Partition should be greater than 0"); int strLength = sentence.length(); for (int i = 0; i < strLength; i += partition) { list.add(sentence.substring(i, Math.min(strLength, i + partition))); @@ -65,37 +91,63 @@ public class Splitter { } private void populateForTrimmer(String sentence, List list) { + checkNotNull(splitSeq, "Split value provided was null"); for (String s : sentence.split(splitSeq)) { - if (s == null || (omitEmptyStrings && s.trim().isEmpty())) + if (omitEmptyStrings && s.trim().isEmpty()) continue; list.add(s.trim()); } } + /** + * Split the string on fixed length partitions + * @param partition the length to split the string on + * @return this + */ public static Splitter fixedLengthSplit(int partition) { - checkArguments(partition > 0, "Partition has to be greater than 0"); splitter = new Splitter(partition); return splitter; } + /** + * Split the length on a specified string sequence + * @param split the sequence to split + * @return this + */ public static Splitter on(String split) { - checkNotNull(split, "Split value provided was null"); splitter = new Splitter(split); return splitter; } + /** + * Split on new line sequence + * @return this + */ public static Splitter onNewLine() { return on("\\r?\\n"); } + /** + * Split on spaces + * @return this + */ public static Splitter onSpace() { return on(" "); } + /** + * Separator on what the key value is. return map + * @param separator the separator value + * @return this + */ + public MapSplitter withKeyValueSeparator(String separator) { + return new MapSplitter(this, separator); + } + public static class MapSplitter { private final Splitter splitter; - private String separator; + private final String separator; private boolean skipInvalid = false; private MapSplitter(Splitter splitter, String separator) { diff --git a/src/main/java/net/locusworks/common/utils/Utils.java b/src/main/java/net/locusworks/common/utils/Utils.java index 95af3d4..14ad53e 100644 --- a/src/main/java/net/locusworks/common/utils/Utils.java +++ b/src/main/java/net/locusworks/common/utils/Utils.java @@ -185,25 +185,18 @@ public class Utils { throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Object clone = obj.getClass().getDeclaredConstructor().newInstance(); for (Field field : obj.getClass().getDeclaredFields()) { - try { - field.setAccessible(true); - if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())){ - continue; - } + field.setAccessible(true); + if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())){ + continue; + } - Class type = field.getType(); + Class type = field.getType(); - if (type.isPrimitive() || getWrapperTypes().contains(type)) { - field.set(clone, field.get(obj)); - } else { - Object childObj = field.get(obj); - if (childObj == obj) { - field.set(clone, clone); - } else { - field.set(clone, cloneObject(field.get(obj))); - } - } - } catch (NullPointerException ignored) { } + if (isPrimitiveOrWrapper(type)) { + field.set(clone, field.get(obj)); + } else { + field.set(clone, cloneObject(field.get(obj))); + } } return (O) clone; } @@ -279,8 +272,8 @@ public class Utils { * @param The expected class of the Iterable list * @return The converted list */ - public static List extractFieldToList(String fieldName, Iterable list) { - return setToList(extractFieldToSet(fieldName, list)); + public static Set extractFieldToSet(String fieldName, Iterable list) { + return listToSet(extractFieldToList(fieldName, list)); } /** @@ -292,10 +285,10 @@ public class Utils { * @return set with the desired items */ @SuppressWarnings("unchecked") - public static Set extractFieldToSet(String fieldName, Iterable list) { - Set newSet = new LinkedHashSet<>(); + public static List extractFieldToList(String fieldName, Iterable list) { + List newSet = new ArrayList<>(); Field field; - for (K item : Utils.safeList(list)) { + for (K item : Utils.safeIterable(list)) { try { field = getField(item.getClass(), fieldName); @@ -327,7 +320,7 @@ public class Utils { Field field; - for (E item : safeList(list)) { + for (E item : safeIterable(list)) { try { field = getField(item.getClass(), fieldName); @@ -361,27 +354,9 @@ public class Utils { * @return - Returns the value of the specified field or null if there is no value. * */ - @SuppressWarnings({ "unchecked", "unlikely-arg-type" }) + @SuppressWarnings({"unlikely-arg-type" }) public static E findValue(E itemToFind, String fieldName, List list) { - for (K item : Utils.safeList(list)) { - try { - if (isPrimitiveOrWrapper(item.getClass())) { - if (itemToFind.equals(item)) { - return (E) item; - } - } else { - Field field = getField(item.getClass(), fieldName); - E value = (E) field.get(item); - if (itemToFind.equals(value)) { - return value; - } - } - } catch(IllegalArgumentException | IllegalAccessException e) { - throw new IllegalArgumentException(String.format("Unable to find field %s. -> %s", fieldName, e.getMessage())); - } - } - - return null; + return findValues(itemToFind, fieldName, list).stream().findFirst().orElse(null); } /** @@ -398,29 +373,12 @@ public class Utils { * @param - The expected class of the Iterable list * @return - Returns the list. */ - @SuppressWarnings({ "unchecked", "unlikely-arg-type" }) + @SuppressWarnings({"unlikely-arg-type" }) public static List findValues(E valueToFind, String fieldName, List list) { - List tmpList = new ArrayList<>(); - - for (K item : Utils.safeList(list)) { - try { - if (isPrimitiveOrWrapper(item.getClass())) { - if (valueToFind.equals(item)) { - tmpList.add((E)item); - } - } else { - Field field = getField(item.getClass(), fieldName); - E value = (E) field.get(item); - if (valueToFind.equals(value)) { - tmpList.add(value); - } - } - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new IllegalArgumentException(String.format("Unable to find field %s. -> %s", fieldName, e.getMessage())); - } - } - - return tmpList; + List extracted = extractFieldToList(fieldName, list); + return extracted.stream() + .filter(Objects::nonNull) + .filter(v -> v.equals(valueToFind)).collect(Collectors.toList()); } /** @@ -571,7 +529,7 @@ public class Utils { } /** - * Converts a list of items into a map + * Converts a list of items into a map. * * @param the type parameter * @param the type parameter @@ -589,7 +547,7 @@ public class Utils { Field field = getField(value.getClass(), keyFieldName); K key = (K) field.get(value); map.put(key, value); - } catch(IllegalArgumentException | IllegalAccessException e) { + } catch(Exception e) { throw new IllegalArgumentException(String.format("Unable to find field %s. -> %s", keyFieldName, e.getMessage())); } } @@ -712,7 +670,7 @@ public class Utils { */ public static Set makeSet(Iterable collection) { Set set = new HashSet<>(); - for (E item : safeList(collection)) { + for (E item : safeIterable(collection)) { set.add(item); } return set; @@ -756,15 +714,26 @@ public class Utils { * @return map with reverse key value pairs */ public static Map reverseMap(Map map) { - Map reverseMap = new HashMap<>(); - - for(Map.Entry entry : map.entrySet()) { - reverseMap.put(entry.getValue(), entry.getKey()); - } - - return reverseMap; + return map.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey)); } + /** + * create safe array e [ ]. + * + * @param the type parameter + * @param list the list + * + * @return the e [ ] + */ + @SuppressWarnings("unchecked") + public static E[] safeArray(E[] list) { + if (!validateValue(list)) { + return (E[]) new Object[0]; + } + return list; + } + + /** * Creates a safe list * @@ -780,21 +749,6 @@ public class Utils { return list; } - /** - * Safe list e [ ]. - * - * @param the type parameter - * @param list the list - * - * @return the e [ ] - */ - @SuppressWarnings("unchecked") - public static E[] safeList(E[] list) { - if (!validateValue(list)) { - return (E[]) new Object[0]; - } - return list; - } /** * Creates a safe list @@ -804,22 +758,7 @@ public class Utils { * * @return an empty list if list is invalid or the list if its valid */ - public static Iterable safeList(Iterable list) { - if (!validateValue(list)) { - return new ArrayList<>(); - } - return list; - } - - /** - * Creates a safe list - * - * @param the type parameter - * @param list The list to check - * - * @return an empty list if list is invalid or the list if its valid - */ - public static List safeList(List list) { + public static Iterable safeIterable(Iterable list) { if (!validateValue(list)) { return new ArrayList<>(); } @@ -907,7 +846,6 @@ public class Utils { */ @SafeVarargs public static Set toSet(E... values) { - return new LinkedHashSet<>(Arrays.asList(values)); } @@ -1002,7 +940,12 @@ public class Utils { */ @SafeVarargs public static boolean validateValues(V... objectsToValidate) { - return validateValues(false, objectsToValidate); + return validateValues(false, toList(objectsToValidate)); + } + + @SafeVarargs + public static boolean validateValuesOr(V... objectsToValidate) { + return validateValues(true, toList(objectsToValidate)); } /** @@ -1012,19 +955,13 @@ public class Utils { * @param the expected class type of the objects passed in. All objects need to be of same type * @return true if the values are valid, false otherwise */ - @SafeVarargs - public static boolean validateValues(boolean or, V... objectsToValidate) { + public static boolean validateValues(boolean or, List objectsToValidate) { boolean valid = !or; - try { - for (Object obj : objectsToValidate) { - if (or && validateValue(obj)) { - return true; - } - - valid &= validateValue(obj); + for (Object obj : objectsToValidate) { + if (or && validateValue(obj)) { + return true; } - } catch (Exception ex) { - return false; + valid &= validateValue(obj); } return valid; diff --git a/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java b/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java new file mode 100644 index 0000000..feb7c8d --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java @@ -0,0 +1,233 @@ +package net.locusworks.common.utils; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.jupiter.api.Assertions.*; + +class DateTimeStampSerializerTest { + + static AtomicLong atomicLong = new AtomicLong(0); + + @Test + void testSerializer(){ + DateTimeStampSerializer serializer = new DateTimeStampSerializer(); + assertDoesNotThrow(() -> serializer.serialize(new Date(), new MyJsonGenerator(), new MySerializerProvider())); + assertTrue(atomicLong.get() > 0); + } + + private static class MyJsonGenerator extends JsonGenerator { + @Override public JsonGenerator setCodec(ObjectCodec objectCodec) { + return null; + } + + @Override public ObjectCodec getCodec() { + return null; + } + + @Override public Version version() { + return null; + } + + @Override public JsonStreamContext getOutputContext() { + return null; + } + + @Override public JsonGenerator enable(Feature feature) { + return null; + } + + @Override public JsonGenerator disable(Feature feature) { + return null; + } + + @Override public boolean isEnabled(Feature feature) { + return false; + } + + @Override public int getFeatureMask() { + return 0; + } + + @Override public JsonGenerator setFeatureMask(int i) { + return null; + } + + @Override public JsonGenerator useDefaultPrettyPrinter() { + return null; + } + + @Override public void writeStartArray() { + fail(); + } + + @Override public void writeEndArray() { + fail(); + } + + @Override public void writeStartObject() { + fail(); + } + + @Override public void writeEndObject() { + fail(); + } + + @Override public void writeFieldName(String s) { + fail(); + } + + @Override public void writeFieldName(SerializableString serializableString) + { + fail(); + } + + @Override public void writeString(String s) { + fail(); + } + + @Override public void writeString(char[] chars, int i, int i1) { + fail(); + } + + @Override public void writeString(SerializableString serializableString) { + fail(); + } + + @Override public void writeRawUTF8String(byte[] bytes, int i, int i1) { + fail(); + } + + @Override public void writeUTF8String(byte[] bytes, int i, int i1) { + fail(); + } + + @Override public void writeRaw(String s) { + fail(); + } + + @Override public void writeRaw(String s, int i, int i1) { + fail(); + } + + @Override public void writeRaw(char[] chars, int i, int i1) { + fail(); + } + + @Override public void writeRaw(char c) { + fail(); + } + + @Override public void writeRawValue(String s) { + fail(); + } + + @Override public void writeRawValue(String s, int i, int i1) { + fail(); + } + + @Override public void writeRawValue(char[] chars, int i, int i1) { + fail(); + } + + @Override public void writeBinary(Base64Variant base64Variant, byte[] bytes, int i, int i1) + { + fail(); + } + + @Override public int writeBinary(Base64Variant base64Variant, InputStream inputStream, int i) + { + fail(); + return 0; + } + + @Override public void writeNumber(int i) { + fail(); + } + + @Override public void writeNumber(long l) { + atomicLong.set(l); + } + + @Override public void writeNumber(BigInteger bigInteger) { + fail(); + } + + @Override public void writeNumber(double v) { + fail(); + } + + @Override public void writeNumber(float v) { + fail(); + } + + @Override public void writeNumber(BigDecimal bigDecimal) { + fail(); + } + + @Override public void writeNumber(String s) { + fail(); + } + + @Override public void writeBoolean(boolean b) { + fail(); + } + + @Override public void writeNull() { + fail(); + } + + @Override public void writeObject(Object o) { + fail(); + } + + @Override public void writeTree(TreeNode treeNode) { + fail(); + } + + @Override public void flush() { + fail(); + } + + @Override public boolean isClosed() { + return false; + } + + @Override public void close() { + fail(); + } + } + + + private static class MySerializerProvider extends SerializerProvider { + @Override + public WritableObjectId findObjectId(Object o, ObjectIdGenerator objectIdGenerator) { + return null; + } + + @Override public JsonSerializer serializerInstance(Annotated annotated, Object o) { + return null; + } + + @Override public Object includeFilterInstance(BeanPropertyDefinition beanPropertyDefinition, + Class aClass) { + return null; + } + + @Override public boolean includeFilterSuppressNulls(Object o) { + return false; + } + } +} diff --git a/src/test/java/net/locusworks/test/FileReaderTest.java b/src/test/java/net/locusworks/common/utils/FileReaderTest.java similarity index 76% rename from src/test/java/net/locusworks/test/FileReaderTest.java rename to src/test/java/net/locusworks/common/utils/FileReaderTest.java index b35f4da..330bb03 100644 --- a/src/test/java/net/locusworks/test/FileReaderTest.java +++ b/src/test/java/net/locusworks/common/utils/FileReaderTest.java @@ -1,4 +1,4 @@ -package net.locusworks.test; +package net.locusworks.common.utils; import net.locusworks.common.interfaces.AutoCloseableIterator; import net.locusworks.common.utils.FileReader; @@ -7,16 +7,22 @@ import net.locusworks.common.utils.RandomString; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import java.io.File; import java.io.FileOutputStream; import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; public class FileReaderTest { @@ -31,7 +37,7 @@ public class FileReaderTest { int count = ThreadLocalRandom.current().nextInt(100); for (int i = 1; i <= count; i++) { - String randomString = RandomString.getString(ThreadLocalRandom.current().nextInt(5, 100)) + "\n"; + String randomString = RandomString.getInstance().getString(ThreadLocalRandom.current().nextInt(5, 100)) + "\n"; numLines.put(i, randomString.length()); fos.write(randomString.getBytes()); } @@ -93,4 +99,12 @@ public class FileReaderTest { } } + @Test + public void testNoAlgorithmException() { + try(MockedStatic mocked = mockStatic(SecureRandom.class)) { + mocked.when(() -> SecureRandom.getInstance(anyString())).thenThrow(NoSuchAlgorithmException.class); + assertDoesNotThrow(() -> RandomString.newInstance()); + } + } + } diff --git a/src/test/java/net/locusworks/test/HashUtilsTest.java b/src/test/java/net/locusworks/common/utils/HashUtilsTest.java similarity index 96% rename from src/test/java/net/locusworks/test/HashUtilsTest.java rename to src/test/java/net/locusworks/common/utils/HashUtilsTest.java index 063a4f6..120f943 100644 --- a/src/test/java/net/locusworks/test/HashUtilsTest.java +++ b/src/test/java/net/locusworks/common/utils/HashUtilsTest.java @@ -1,4 +1,4 @@ -package net.locusworks.test; +package net.locusworks.common.utils; import net.locusworks.common.utils.HashUtils; import org.apache.commons.codec.digest.DigestUtils; diff --git a/src/test/java/net/locusworks/common/utils/RandomStringTest.java b/src/test/java/net/locusworks/common/utils/RandomStringTest.java new file mode 100644 index 0000000..7cb8bf1 --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/RandomStringTest.java @@ -0,0 +1,30 @@ +package net.locusworks.common.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class RandomStringTest { + + @Test + public void testStaticBytes() { + for (int length = 3; length < 50; length++) { + assertEquals(RandomString.getInstance().getBytes(length).length, length); + } + } + + @Test + public void testStaticString() { + for (int length = 3; length < 50; length++) { + String random = RandomString.getInstance().getString(length); + assertEquals(random.length(), length); + } + } + + @Test + public void testExceptions() { + assertThrows(IllegalArgumentException.class, () -> RandomString.newInstance().getString(0)); + } + +} diff --git a/src/test/java/net/locusworks/common/utils/SplitterTest.java b/src/test/java/net/locusworks/common/utils/SplitterTest.java new file mode 100644 index 0000000..26af52e --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/SplitterTest.java @@ -0,0 +1,81 @@ +package net.locusworks.common.utils; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class SplitterTest { + + @Test + void testBasicSplitter() { + List split = Splitter.onSpace().split("Hello World"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); + + split = Splitter.fixedLengthSplit(5).split("HelloWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); + + split = Splitter.onNewLine().split("Hello\nWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); + + split = Splitter.onNewLine().split("Hello\r\nWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); + + String[] array = Splitter.onSpace().splitToArray("Hello World"); + assertNotNull(array); + assertEquals(2, array.length); + assertEquals("Hello", array[0]); + assertEquals("World", array[1]); + } + + @Test + void testOmitEmptyStringWithLimits() { + List split = Splitter.onSpace().split("Hello World"); + assertEquals(3, split.size()); + split = Splitter.onSpace().omitEmptyStrings().withLimit(1).split("Hello World"); + assertNotNull(split); + assertEquals(1, split.size()); + } + + @Test + void testExceptions() { + assertThrows(IllegalArgumentException.class, () -> Splitter.onSpace().split("")); + assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLengthSplit(0).split("Hello World")); + assertThrows(NullPointerException.class, () -> Splitter.on(null).split("Hello World")); + + } + + @Test + void testWithKeyValueSeparator() { + Map map = Splitter.on(";").withKeyValueSeparator("=").split("Hello=World;bubba=hotep"); + assertEquals(2, map.size()); + assertEquals("World", map.get("Hello")); + assertEquals("hotep", map.get("bubba")); + } + + @Test + void testWithMapSplitterErrors() { + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("").split("Hello=;bubba=hotep")); + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split("Hello=;bubba=hotep")); + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split(null)); + Map map = Splitter.on(";").withKeyValueSeparator("=").skipInvalidKeyValues().split("Hello=;bubba=hotep"); + assertEquals(1, map.size()); + assertEquals("hotep", map.get("bubba")); + } + + +} diff --git a/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java b/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java new file mode 100644 index 0000000..dd41898 --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java @@ -0,0 +1,19 @@ +package net.locusworks.common.utils; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class StreamUtilsTest { + + @Test + void testAsStream() { + List list = List.of("Hello", "World"); + assertEquals(2, StreamUtils.asStream(list).count()); + assertEquals(2, StreamUtils.asStream(list.iterator()).count()); + assertEquals(2, StreamUtils.asStream(list.toArray(), false).count()); + } + +} diff --git a/src/test/java/net/locusworks/common/utils/UtilsTest.java b/src/test/java/net/locusworks/common/utils/UtilsTest.java new file mode 100644 index 0000000..ba9996b --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/UtilsTest.java @@ -0,0 +1,512 @@ +package net.locusworks.common.utils; + +import net.locusworks.common.annotations.MapValue; +import net.locusworks.common.utils.Utils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.ParameterizedTest; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test cases for the Utils class + * @author Isaac Parenteau + * @since 1.0.0-RELEASE + * + */ +public class UtilsTest { + + @Test + public void testSafeString() { + assertNotNull(Utils.safeString(null)); + assertTrue(Utils.safeString(null).isEmpty()); + assertFalse(Utils.safeString("hello world").isEmpty()); + } + + + @Test + public void testAreEqual() { + String val1 = "H"; + String val2 = "H"; + assertTrue(Utils.areEqual(val1, val2)); + + assertThrows(IllegalArgumentException.class, () -> Utils.areEqual("1")); + } + @ParameterizedTest + @MethodSource("areEqualParams") + public void testAreEqualWithParams(List objectList, boolean or, boolean equal) { + assertEquals(equal, Utils.areEqual(or, objectList.toArray())); + } + + static Stream areEqualParams() { + return Stream.of( + Arguments.of(List.of("Hello", "Hello"), false, true), + Arguments.of(List.of("Hello", "World"), false, false), + Arguments.of(List.of("Hello", "World", "Hello"), true, true), + Arguments.of(List.of("Hello", "World", "Fair"), true, false) + ); + } + + @Test + public void testEmptyString() { + assertTrue(Utils.isEmptyString(null)); + assertTrue(Utils.isEmptyString("")); + assertTrue(Utils.isEmptyString(" ")); + assertFalse(Utils.isEmptyString("foo")); + assertFalse(Utils.isEmptyString(" bar ")); + } + + @Test + public void testToInteger() { + assertEquals(2, (int) Utils.toInteger("Hello word", 2)); + assertEquals(23, (int) Utils.toInteger("23", 5023)); + } + + @ParameterizedTest + @MethodSource("validateValueParams") + public void testValidateValue(V value, boolean valid) { + assertEquals(valid, Utils.validateValue(value)); + } + + static Stream validateValueParams() { + return Stream.of( + Arguments.of(null, false), + Arguments.of(Collections.emptyList(), false), + Arguments.of(Map.of(), false), + Arguments.of(false, false), + Arguments.of("", false), + Arguments.of("Hello", true), + Arguments.of(List.of("Hello"), true), + Arguments.of(Map.of("Hello", "World"), true), + Arguments.of(true, true) + ); + } + + @ParameterizedTest + @MethodSource("areValidParams") + public void testValidateValues(List objects, boolean or, boolean valid) { + assertEquals(valid, Utils.areValid(objects.toArray())); + assertEquals(!valid, Utils.areNotValid(objects.toArray())); + } + + static Stream areValidParams() { + return Stream.of( + Arguments.of(List.of("Hello", List.of("Hello"), Map.of("Hello", "World"), true), false, true), + Arguments.of(List.of(Collections.emptyList(), Map.of(), false), false, false) + ); + } + + @Test + public void testBuildMap() { + Map mymap = Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", "World"); + assertNotNull(mymap); + assertEquals(1, mymap.size()); + assertEquals("World", mymap.get("Hello")); + } + + + + @Test + public void testCloneList() { + String[] values = new String[]{ + "Hello", + "world" + }; + List cloned = Utils.cloneList(new ArrayList<>(Arrays.stream(values).toList())); + assertEquals(2, cloned.size()); + assertThrows(UnsupportedOperationException.class, () -> cloned.add("asdf")); + } + + @Test + public void testCloneObject() + throws InvocationTargetException, InstantiationException, IllegalAccessException, + NoSuchMethodException { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + + TestClass clazz3 = new TestClass(); + clazz3.field1 = "hi"; + clazz3.field2 = "smash"; + clazz3.field3 = "world"; + + clazz.testClass = clazz3; + + TestClass clazz2 = Utils.cloneObject(clazz); + assertNotNull(clazz2); + assertEquals(clazz.field1, clazz2.field1); + assertEquals(clazz.field2, clazz2.field2); + assertEquals(clazz.field3, clazz2.field3); + assertEquals(clazz.field5, clazz2.field5); + assertEquals(clazz.number, clazz2.number); + assertEquals(clazz.aBoolean, clazz2.aBoolean); + assertNotNull(clazz2.field4); + assertNull(clazz2.nullField); + } + + @Test + public void testCovertToMap() throws Exception { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + + TestClass clazz3 = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + clazz.testClass = clazz3; + + Map converted = Utils.convertToMap(clazz); + + assertEquals(7, converted.size()); + assertEquals("hi", converted.get("other_field")); + } + + @Test + public void testConvertToStringMap() throws Exception { + TestClass clazz = new TestClass(); + clazz.field1 = "hi"; + clazz.field2 = "low"; + clazz.field3 = "world"; + Map converted = Utils.convertToStringMap(clazz); + assertEquals(6, converted.size()); + assertEquals("hi", converted.get("other_field")); + + converted = Utils.convertToStringMap(converted); + assertEquals(6, converted.size()); + assertEquals("hi", converted.get("other_field")); + } + + @Test + public void testBuildStringMap() { + Map myMap = Utils.buildStringHashMap("hello", "world"); + assertNotNull(myMap); + assertEquals(1, myMap.size()); + } + + @Test + public void testExtractFieldToList() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TestClass clazz = new TestClass(); + clazz.field5 = i; + list.add(clazz); + } + + List extractedList = Utils.extractFieldToList("field5", list); + Set extractedSet = Utils.extractFieldToSet("field5", list); + + assertEquals(10, extractedList.size()); + assertEquals(10, extractedSet.size()); + + for (int i = 0; i < 10; i++) { + assertEquals(i, extractedList.get(i)); + assertTrue(extractedSet.contains(i)); + } + + assertThrows(IllegalArgumentException.class, () -> Utils.extractFieldToList("fieldName", list)); + assertThrows(IllegalArgumentException.class, () -> Utils.extractFieldToSet("fieldName", list)); + } + + + @Test + public void testFilterList() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TestClass clazz = new TestClass(); + clazz.field5 = i; + list.add(clazz); + } + + list.get(0).testClass = list.get(1); + + List filteredList = Utils.filterList("field5", 5, list); + assertEquals(1, filteredList.size()); + assertEquals(5, filteredList.get(0).field5); + assertThrows(IllegalArgumentException.class, () -> Utils.filterList("fieldName", 5, list)); + + int value = Utils.findValue(5, "field5", list); + assertEquals(5, value); + + TestClass tc = Utils.findValue(list.get(1), "testClass", list); + assertNotNull(tc); + assertEquals(1, tc.field5); + } + + @Test + public void testFindValues() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TestClass clazz = new TestClass(); + clazz.field5 = i % 2; + list.add(clazz); + } + + List values = Utils.findValues(0, "field5", list); + assertEquals(5, values.size()); + + } + + @Test + public void testBuildMapExceptions() { + Throwable ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(String.class, String.class, String.class, "" )); + + assertNotNull(ex); + + ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello")); + + assertEquals("Odd number of arguments provided", ex.getMessage()); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, new Object(), "Hello")); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", new Object())); + + assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, Object.class, String.class, "Hello", new Object())); + } + + @Test + public void testBuildSet() { + Set myset = Utils.buildSet(HashSet.class, String.class, "Hello", "World"); + assertNotNull(myset); + assertEquals(2, myset.size()); + } + + @Test + public void testFormatString() { + String formatted = Utils.formatString("{} {}", "Hi", 1); + assertEquals("Hi 1", formatted); + } + + @Test + public void testGetClassName() { + TestClass tc = new TestClass(); + assertEquals(tc.getClass().getSimpleName(), Utils.getClassName(tc)); + assertEquals(tc.getClass().getName(), Utils.getClassName(tc, true)); + assertEquals("Unknown", Utils.getClassName(null)); + } + + @Test + public void testGetMapValue() { + Integer value = Utils.getMapValue(Map.of("hi", 1), "hi"); + assertEquals(1, value); + + value = Utils.getMapValue(Map.of("hi", 1), "low", 0); + + assertEquals(0, value); + } + + @Test + public void testIsNotValid() { + assertTrue(Utils.isNotValid(null, null, null)); + assertFalse(Utils.isNotValid("null", "null", "null")); + } + + @Test + public void testIsValid() { + assertFalse(Utils.isValid(null, null, null)); + assertTrue(Utils.isValid("null", "null", "null")); + } + + @Test + public void testListToMap() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TestClass clazz = new TestClass(); + clazz.field5 = i; + list.add(clazz); + } + + Map values = Utils.listToMap("field5", list); + assertEquals(10, values.size()); + assertEquals(1, values.get(1).field5); + + assertThrows(Throwable.class, () -> Utils.listToMap("fieldasdf5", list)); + } + + @Test + public void testMakeArray() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TestClass clazz = new TestClass(); + clazz.field5 = i; + list.add(clazz); + } + + assertNull(Utils.makeArray(null, String.class)); + + TestClass[] array = Utils.makeArray(list, TestClass.class); + assertEquals(10, array.length); + for(int i = 0; i < 10; i++) { + assertEquals(list.get(i), array[i]); + } + + assertNull(Utils.makeList((Collection) null)); + assertNull(Utils.makeList((Iterable) null)); + assertNull(Utils.makeList((String[]) null)); + + List testList = Utils.makeList(list); + for(int i = 0; i < 10; i++) { + assertEquals(list.get(i), testList.get(i)); + } + + TestClassList tcl = new TestClassList(); + tcl.testClassList = testList; + + List testClassList = Utils.makeList(tcl); + for(int i = 0; i < 10; i++) { + assertEquals(list.get(i), testClassList.get(i)); + } + + List testArrayList = Utils.makeList(array); + for(int i = 0; i < 10; i++) { + assertEquals(list.get(i), testArrayList.get(i)); + } + + Set testSet = Utils.makeSet(list); + assertEquals(10, testSet.size()); + } + + @Test + public void testMapToList() { + List values = Utils.mapToList(Map.of("Hello", 1)); + assertEquals(1, values.size()); + assertEquals(1, values.get(0)); + + Set setValues = Utils.mapToSet(Map.of("Hello", 1)); + assertEquals(1, setValues.size()); + assertTrue(setValues.contains(1)); + } + + @Test + public void testReverseMap() { + Map map = Utils.reverseMap(Map.of("Hello", 1)); + assertEquals(1, map.size()); + assertEquals("Hello", map.get(1)); + } + + @Test + public void testSafeList() { + assertEquals(0, Utils.safeArray(null).length); + assertTrue(Utils.safeList(null).isEmpty()); + assertFalse(Utils.safeIterable(null).iterator().hasNext()); + assertTrue(Utils.safeSet(null).isEmpty()); + + List list = List.of("Hello", "World"); + + String[] stringArray = list.toArray(new String[0]); + + assertEquals(list, Utils.safeList(list)); + assertEquals(stringArray, Utils.safeArray(stringArray)); + assertEquals(list, Utils.safeIterable(list)); + + Set testSet = Set.of("Hello", "World"); + + assertEquals(testSet, Utils.safeSet(testSet)); + } + + @Test + public void testSetToList() { + List list = Utils.setToList(Set.of("Hello", "World")); + assertEquals(2, list.size()); + + list = Utils.toList(Set.of("Hello", "World")); + assertEquals(2, list.size()); + + Set set = Utils.toSet("Hello", "World"); + + assertEquals(2, set.size()); + + assertTrue(set.contains("Hello")); + assertTrue(set.contains("World")); + } + + @Test + public void testToByteList() { + byte[] bytes = new byte[10]; + + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte)i; + } + + List byteList = Utils.toByteList(bytes); + assertEquals(bytes.length, byteList.size()); + + for(int i = 0; i < bytes.length; i++) { + assertEquals(bytes[i], byteList.get(i)); + } + } + + @Test + public void testIterableSize() { + List stringList = List.of("Hello", "World"); + assertEquals(stringList.size(), (int)Utils.size(stringList)); + assertEquals(stringList.get(1), Utils.get(stringList, 1)); + } + + @Test + public void testExceptionWrapper() { + assertThrows(RuntimeException.class, () -> List.of("Hi", "Low").forEach(Utils.handleExceptionWrapper(o -> { + throw new Exception("bleh"); + }))); + + AtomicInteger atomicInteger = new AtomicInteger(0); + List.of("Hi", "Low").forEach(Utils.handleExceptionWrapper(o -> { + atomicInteger.incrementAndGet(); + })); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void testValidateValues() { + assertTrue(Utils.validateValues("Hello", "World")); + assertTrue(Utils.validateValuesOr("Hello", null)); + assertTrue(Utils.validateValuesOr(null, "Hello")); + assertFalse(Utils.validateValuesOr(null, null)); + } + + @Test + public void testIsJunitRunning() { + assertTrue(Utils.isJUnitRunning()); + } + + static class TestClassList implements Iterable { + private List testClassList; + @Override public Iterator iterator() { + return testClassList.iterator(); + } + + } + + public static class TestClass { + @MapValue("other_field") + private String field1; + + @MapValue(ignore = true) + private String field2; + + private String field3; + + private final String field4 = "final"; + @MapValue + private int field5 = 5; + + private Boolean aBoolean = Boolean.valueOf("true"); + + private Double number = 1.0d; + + private String nullField = null; + + private TestClass testClass; + + } + +} diff --git a/src/test/java/net/locusworks/test/RandomStringTest.java b/src/test/java/net/locusworks/test/RandomStringTest.java deleted file mode 100644 index 50a32e1..0000000 --- a/src/test/java/net/locusworks/test/RandomStringTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.locusworks.test; - -import net.locusworks.common.utils.RandomString; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class RandomStringTest { - - @Test - public void testStaticBytes() { - for (Integer length = 3; length < 50; length++) { - assertEquals(RandomString.getBytes(length).length, (int) length); - } - } - - @Test - public void testStaticString() { - for (Integer length = 3; length < 50; length++) { - String random = RandomString.getString(length); - assertEquals(random.length(), (int) length); - } - } - -} diff --git a/src/test/java/net/locusworks/test/UtilsTest.java b/src/test/java/net/locusworks/test/UtilsTest.java deleted file mode 100644 index cd324c5..0000000 --- a/src/test/java/net/locusworks/test/UtilsTest.java +++ /dev/null @@ -1,244 +0,0 @@ -package net.locusworks.test; - -import net.locusworks.common.annotations.MapValue; -import net.locusworks.common.utils.Utils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.ParameterizedTest; - -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Test cases for the Utils class - * @author Isaac Parenteau - * @since 1.0.0-RELEASE - * - */ -public class UtilsTest { - - @Test - public void testSafeString() { - assertNotNull(Utils.safeString(null)); - assertTrue(Utils.safeString(null).isEmpty()); - assertFalse(Utils.safeString("hello world").isEmpty()); - } - - - @Test - public void testAreEqual() { - String val1 = "H"; - String val2 = "H"; - assertTrue(Utils.areEqual(val1, val2)); - - assertThrows(IllegalArgumentException.class, () -> Utils.areEqual("1")); - } - @ParameterizedTest - @MethodSource("areEqualParams") - public void testAreEqualWithParams(List objectList, boolean or, boolean equal) { - assertEquals(equal, Utils.areEqual(or, objectList.toArray())); - } - - static Stream areEqualParams() { - return Stream.of( - Arguments.of(List.of("Hello", "Hello"), false, true), - Arguments.of(List.of("Hello", "World"), false, false), - Arguments.of(List.of("Hello", "World", "Hello"), true, true), - Arguments.of(List.of("Hello", "World", "Fair"), true, false) - ); - } - - @Test - public void testEmptyString() { - assertTrue(Utils.isEmptyString(null)); - assertTrue(Utils.isEmptyString("")); - assertTrue(Utils.isEmptyString(" ")); - assertFalse(Utils.isEmptyString("foo")); - assertFalse(Utils.isEmptyString(" bar ")); - } - - @Test - public void testToInteger() { - assertEquals(2, (int) Utils.toInteger("Hello word", 2)); - assertEquals(23, (int) Utils.toInteger("23", 5023)); - } - - @ParameterizedTest - @MethodSource("validateValueParams") - public void testValidateValue(V value, boolean valid) { - assertEquals(valid, Utils.validateValue(value)); - } - - static Stream validateValueParams() { - return Stream.of( - Arguments.of(null, false), - Arguments.of(Collections.emptyList(), false), - Arguments.of(Map.of(), false), - Arguments.of(false, false), - Arguments.of("", false), - Arguments.of("Hello", true), - Arguments.of(List.of("Hello"), true), - Arguments.of(Map.of("Hello", "World"), true), - Arguments.of(true, true) - ); - } - - @ParameterizedTest - @MethodSource("areValidParams") - public void testValidateValues(List objects, boolean or, boolean valid) { - assertEquals(valid, Utils.areValid(objects.toArray())); - assertEquals(!valid, Utils.areNotValid(objects.toArray())); - } - - static Stream areValidParams() { - return Stream.of( - Arguments.of(List.of("Hello", List.of("Hello"), Map.of("Hello", "World"), true), false, true), - Arguments.of(List.of(Collections.emptyList(), Map.of(), false), false, false) - ); - } - - @Test - public void testBuildMap() { - Map mymap = Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", "World"); - assertNotNull(mymap); - assertEquals(1, mymap.size()); - assertEquals("World", mymap.get("Hello")); - } - - @Test - public void testCloneList() { - String[] values = new String[]{ - "Hello", - "world" - }; - List cloned = Utils.cloneList(new ArrayList<>(Arrays.stream(values).toList())); - assertEquals(2, cloned.size()); - assertThrows(UnsupportedOperationException.class, () -> cloned.add("asdf")); - } - - @Test - public void testCloneObject() - throws InvocationTargetException, InstantiationException, IllegalAccessException, - NoSuchMethodException { - TestClass clazz = new TestClass(); - clazz.field1 = "hi"; - clazz.field2 = "low"; - clazz.field3 = "world"; - - TestClass clazz3 = new TestClass(); - clazz3.field1 = "hi"; - clazz3.field2 = "smash"; - clazz3.field3 = "world"; - - clazz.testClass = clazz3; - - TestClass clazz2 = Utils.cloneObject(clazz); - assertNotNull(clazz2); - assertEquals(clazz.field1, clazz2.field1); - assertEquals(clazz.field2, clazz2.field2); - assertEquals(clazz.field3, clazz2.field3); - assertEquals(clazz.field5, clazz2.field5); - assertEquals(clazz.number, clazz2.number); - assertEquals(clazz.aBoolean, clazz2.aBoolean); - assertNotNull(clazz2.field4); - assertNull(clazz2.nullField); - } - - @Test - public void testCovertToMap() throws Exception { - TestClass clazz = new TestClass(); - clazz.field1 = "hi"; - clazz.field2 = "low"; - clazz.field3 = "world"; - - TestClass clazz3 = new TestClass(); - clazz.field1 = "hi"; - clazz.field2 = "low"; - clazz.field3 = "world"; - clazz.testClass = clazz3; - - Map converted = Utils.convertToMap(clazz); - - assertEquals(7, converted.size()); - assertEquals("hi", converted.get("other_field")); - } - - @Test - public void testConvertToStringMap() throws Exception { - TestClass clazz = new TestClass(); - clazz.field1 = "hi"; - clazz.field2 = "low"; - clazz.field3 = "world"; - Map converted = Utils.convertToStringMap(clazz); - assertEquals(6, converted.size()); - assertEquals("hi", converted.get("other_field")); - - converted = Utils.convertToStringMap(converted); - assertEquals(6, converted.size()); - assertEquals("hi", converted.get("other_field")); - } - - @Test - public void testBuildStringMap() { - Map myMap = Utils.buildStringHashMap("hello", "world"); - assertNotNull(myMap); - assertEquals(1, myMap.size()); - } - - @Test - public void testBuildMapExceptions() { - Throwable ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(String.class, String.class, String.class, "" )); - - assertNotNull(ex); - - ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello")); - - assertEquals("Odd number of arguments provided", ex.getMessage()); - - assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, new Object(), "Hello")); - - assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", new Object())); - - assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, Object.class, String.class, "Hello", new Object())); - } - - @Test - public void testBuildSet() { - Set myset = Utils.buildSet("Hello", "World"); - assertNotNull(myset); - assertEquals(2, myset.size()); - } - - @Test - public void testIsJunitRunning() { - assertTrue(Utils.isJUnitRunning()); - } - - public static class TestClass { - @MapValue("other_field") - private String field1; - - @MapValue(ignore = true) - private String field2; - - - private String field3; - - private final String field4 = "final"; - @MapValue - private int field5 = 5; - - private Boolean aBoolean = Boolean.valueOf("true"); - - private Double number = 1.0d; - - private String nullField = null; - - private TestClass testClass; - } - -} From 61efd0eaec07fe50b7ebc4526818ef5b9c5b8490 Mon Sep 17 00:00:00 2001 From: Isaac Parenteau Date: Sun, 12 Jul 2026 22:15:45 -0500 Subject: [PATCH 4/4] Upgraded to jdk21, package versions, and added test coverage --- .gitignore | 3 + pom.xml | 57 +- .../java/net/locusworks/common/Charsets.java | 55 +- .../common/annotations/MapValue.java | 5 +- .../configuration/ConfigurationCallback.java | 2 +- .../configuration/ConfigurationManager.java | 252 +-- .../configuration/PropertiesManager.java | 247 +-- .../net/locusworks/common/crypto/AES.java | 223 +- .../net/locusworks/common/crypto/AESKey.java | 39 +- .../locusworks/common/crypto/AESKeySpec.java | 46 +- .../common/crypto/EncryptionKeyFactory.java | 38 +- .../locusworks/common/crypto/HashSalt.java | 323 ++- .../net/locusworks/common/crypto/KeyFile.java | 346 +-- .../net/locusworks/common/crypto/RSA.java | 258 +-- .../common/crypto/SSHEncodedKeySpec.java | 78 +- .../exceptions/ApplicationException.java | 179 +- .../locusworks/common/immutables/Pair.java | 103 +- .../locusworks/common/immutables/Triplet.java | 100 +- .../locusworks/common/immutables/Unit.java | 94 +- .../interfaces/AutoCloseableIterator.java | 6 +- .../common/interfaces/ThrowingConsumer.java | 2 +- .../net/locusworks/common/io/IOUtils.java | 597 +++--- .../migration/BaseMigrationManager.java | 8 +- .../common/migration/MigrationCallback.java | 2 +- .../common/migration/MigrationItem.java | 116 +- .../common/net/HttpClientHelper.java | 148 +- .../certmanagers/TrustAllCertsManager.java | 24 +- .../hostverifiers/AllHostValidVerifier.java | 13 + .../hostverifiers/AllHostValidVerifyer.java | 11 - .../locusworks/common/net/ssl/SSLManager.java | 33 +- .../objectmapper/ObjectMapperError.java | 3 +- .../objectmapper/ObjectMapperHelper.java | 263 +-- .../objectmapper/ObjectMapperListResults.java | 79 +- .../objectmapper/ObjectMapperResults.java | 170 +- .../properties/ImmutableProperties.java | 45 +- .../common/properties/OrderedProperties.java | 1741 ++++++++------- .../net/locusworks/common/utils/Checks.java | 40 +- .../locusworks/common/utils/Constants.java | 19 +- .../common/utils/DataOutputStreamHelper.java | 69 +- .../utils/DateTimeStampDeserializer.java | 154 +- .../common/utils/DateTimeStampSerializer.java | 34 +- .../locusworks/common/utils/FileReader.java | 329 +-- .../locusworks/common/utils/HashUtils.java | 332 +-- .../locusworks/common/utils/ObjectUtils.java | 38 + .../locusworks/common/utils/RandomString.java | 108 +- .../net/locusworks/common/utils/Splitter.java | 324 +-- .../locusworks/common/utils/StreamUtils.java | 107 +- .../net/locusworks/common/utils/Success.java | 54 +- .../net/locusworks/common/utils/Utils.java | 1873 +++++++++-------- .../ConfigurationCoverageTest.java | 165 ++ .../crypto/AESAndHashSaltCoverageTest.java | 80 + .../common/crypto/CryptoCoverageTest.java | 227 ++ .../exceptions/ApplicationExceptionTest.java | 43 + .../common/immutables/ImmutablesTest.java | 69 + .../net/locusworks/common/io/IOUtilsTest.java | 158 ++ .../common/migration/MigrationItemTest.java | 83 + .../common/net/HttpClientHelperTest.java | 45 + .../TrustAllCertsManagerTest.java | 25 + .../AllHostValidVerifierTest.java | 17 + .../common/net/ssl/SSLManagerTest.java | 25 + .../ObjectMapperCoverageTest.java | 119 ++ .../properties/ImmutablePropertiesTest.java | 41 + .../OrderedPropertiesCoverageTest.java | 196 ++ .../properties/OrderedPropertiesTest.java | 75 + .../locusworks/common/utils/ChecksTest.java | 28 + .../utils/DateTimeStampSerializerTest.java | 459 ++-- .../common/utils/FileReaderTest.java | 311 ++- .../common/utils/HashUtilsTest.java | 45 +- .../common/utils/ObjectUtilsTest.java | 81 + .../common/utils/RandomStringTest.java | 34 +- .../locusworks/common/utils/SplitterTest.java | 120 +- .../common/utils/StreamUtilsTest.java | 16 +- .../common/utils/UtilityWrappersTest.java | 65 + .../utils/UtilsPackageCoverageTest.java | 161 ++ .../locusworks/common/utils/UtilsTest.java | 16 +- .../locusworks/test/AESEncryptionTest.java | 55 +- .../java/net/locusworks/test/AllTests.java | 7 - .../net/locusworks/test/HashSaltTest.java | 46 +- .../net/locusworks/test/ImmutablesTest.java | 84 +- .../test/ObjectMapperHelperTest.java | 92 +- .../test/PropertiesManagerTest.java | 185 +- 81 files changed, 7235 insertions(+), 5128 deletions(-) create mode 100644 src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifier.java delete mode 100644 src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifyer.java create mode 100644 src/main/java/net/locusworks/common/utils/ObjectUtils.java create mode 100644 src/test/java/net/locusworks/common/configuration/ConfigurationCoverageTest.java create mode 100644 src/test/java/net/locusworks/common/crypto/AESAndHashSaltCoverageTest.java create mode 100644 src/test/java/net/locusworks/common/crypto/CryptoCoverageTest.java create mode 100644 src/test/java/net/locusworks/common/exceptions/ApplicationExceptionTest.java create mode 100644 src/test/java/net/locusworks/common/immutables/ImmutablesTest.java create mode 100644 src/test/java/net/locusworks/common/io/IOUtilsTest.java create mode 100644 src/test/java/net/locusworks/common/migration/MigrationItemTest.java create mode 100644 src/test/java/net/locusworks/common/net/HttpClientHelperTest.java create mode 100644 src/test/java/net/locusworks/common/net/certmanagers/TrustAllCertsManagerTest.java create mode 100644 src/test/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifierTest.java create mode 100644 src/test/java/net/locusworks/common/net/ssl/SSLManagerTest.java create mode 100644 src/test/java/net/locusworks/common/objectmapper/ObjectMapperCoverageTest.java create mode 100644 src/test/java/net/locusworks/common/properties/ImmutablePropertiesTest.java create mode 100644 src/test/java/net/locusworks/common/properties/OrderedPropertiesCoverageTest.java create mode 100644 src/test/java/net/locusworks/common/properties/OrderedPropertiesTest.java create mode 100644 src/test/java/net/locusworks/common/utils/ChecksTest.java create mode 100644 src/test/java/net/locusworks/common/utils/ObjectUtilsTest.java create mode 100644 src/test/java/net/locusworks/common/utils/UtilityWrappersTest.java create mode 100644 src/test/java/net/locusworks/common/utils/UtilsPackageCoverageTest.java delete mode 100644 src/test/java/net/locusworks/test/AllTests.java diff --git a/.gitignore b/.gitignore index e56cc8a..ee5042b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ **/*.iml **/git.properties .metadata/ +id_rsa* +named* +short* diff --git a/pom.xml b/pom.xml index 6faff9a..7c8e274 100644 --- a/pom.xml +++ b/pom.xml @@ -18,11 +18,11 @@ - 2.20.0 - 2.0.9 - 2.15.2 - 17 - 17 + 2.26.1 + 2.0.18 + 2.22.1 + 21 + 21 https://nexus.locusworks.net @@ -30,13 +30,16 @@ org.apache.maven.plugins - maven-surefire-plugin - 3.1.2 + maven-surefire-plugin + 3.5.6 + + @{argLine} --add-exports java.base/sun.security.jca=ALL-UNNAMED + org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.15.0 ${maven.compiler.source} ${maven.compiler.target} @@ -51,7 +54,14 @@ org.owasp dependency-check-maven - 8.4.0 + 12.2.2 + + true + true + true + true + e2eb1036-9b5b-4df9-95e7-0888be947011 + @@ -63,7 +73,7 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.15 @@ -108,29 +118,37 @@ + + + commons-codec + commons-codec + 1.22.0 + compile + + org.junit.jupiter junit-jupiter-api - 5.10.0 + 6.1.1 test org.junit.jupiter junit-jupiter-params - 5.10.0 + 6.1.1 test org.mockito mockito-core - 5.5.0 + 5.23.0 test org.flywaydb flyway-core - 9.22.1 + 12.10.0 org.apache.logging.log4j @@ -161,12 +179,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.2.1 - - - org.apache.httpcomponents - httpmime - 4.5.14 + 5.6.2 @@ -178,7 +191,7 @@ com.fasterxml.jackson.core jackson-annotations - ${jackson.version} + 2.22 com.fasterxml.jackson.core @@ -189,7 +202,7 @@ com.google.code.gson gson - 2.10.1 + 2.14.0 diff --git a/src/main/java/net/locusworks/common/Charsets.java b/src/main/java/net/locusworks/common/Charsets.java index 85f05ee..9503359 100644 --- a/src/main/java/net/locusworks/common/Charsets.java +++ b/src/main/java/net/locusworks/common/Charsets.java @@ -24,7 +24,7 @@ import java.nio.charset.Charset; * *

See the Guava User Guide article on {@code Charsets}. - * + *

* Please do not add new Charset references to this class, unless those character encodings are * part of the set required to be supported by all Java platform implementations! Any Charsets * initialized here may cause unexpected delays when this class is loaded. See the Charset @@ -32,37 +32,38 @@ import java.nio.charset.Charset; * */ public final class Charsets { - private Charsets() {} + private Charsets() { + } - /** - * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). - */ - public static final Charset US_ASCII = java.nio.charset.StandardCharsets.US_ASCII; + /** + * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). + */ + public static final Charset US_ASCII = java.nio.charset.StandardCharsets.US_ASCII; - /** - * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). - */ - public static final Charset ISO_8859_1 = java.nio.charset.StandardCharsets.ISO_8859_1; + /** + * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). + */ + public static final Charset ISO_8859_1 = java.nio.charset.StandardCharsets.ISO_8859_1; - /** - * UTF-8: eight-bit UCS Transformation Format. - */ - public static final Charset UTF_8 = java.nio.charset.StandardCharsets.UTF_8; + /** + * UTF-8: eight-bit UCS Transformation Format. + */ + public static final Charset UTF_8 = java.nio.charset.StandardCharsets.UTF_8; - /** - * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. - */ - public static final Charset UTF_16BE = java.nio.charset.StandardCharsets.UTF_16BE; + /** + * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. + */ + public static final Charset UTF_16BE = java.nio.charset.StandardCharsets.UTF_16BE; - /** - * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. - */ - public static final Charset UTF_16LE = java.nio.charset.StandardCharsets.UTF_16LE; + /** + * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. + */ + public static final Charset UTF_16LE = java.nio.charset.StandardCharsets.UTF_16LE; - /** - * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order - * mark. - */ - public static final Charset UTF_16 = java.nio.charset.StandardCharsets.UTF_16; + /** + * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order + * mark. + */ + public static final Charset UTF_16 = java.nio.charset.StandardCharsets.UTF_16; } \ No newline at end of file diff --git a/src/main/java/net/locusworks/common/annotations/MapValue.java b/src/main/java/net/locusworks/common/annotations/MapValue.java index 951698d..48b03ef 100644 --- a/src/main/java/net/locusworks/common/annotations/MapValue.java +++ b/src/main/java/net/locusworks/common/annotations/MapValue.java @@ -10,6 +10,7 @@ import java.lang.annotation.Target; * This is at the field level and will map the * filed name as the key and the field value as the value * in the map + * * @author Isaac Parenteau * */ @@ -17,8 +18,8 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface MapValue { - String value() default ""; + String value() default ""; - boolean ignore() default false; + boolean ignore() default false; } diff --git a/src/main/java/net/locusworks/common/configuration/ConfigurationCallback.java b/src/main/java/net/locusworks/common/configuration/ConfigurationCallback.java index cc4a75c..a21fce5 100644 --- a/src/main/java/net/locusworks/common/configuration/ConfigurationCallback.java +++ b/src/main/java/net/locusworks/common/configuration/ConfigurationCallback.java @@ -2,5 +2,5 @@ package net.locusworks.common.configuration; @FunctionalInterface public interface ConfigurationCallback { - void results(String msg); + void results(String msg); } diff --git a/src/main/java/net/locusworks/common/configuration/ConfigurationManager.java b/src/main/java/net/locusworks/common/configuration/ConfigurationManager.java index 24c5d7e..213a42e 100644 --- a/src/main/java/net/locusworks/common/configuration/ConfigurationManager.java +++ b/src/main/java/net/locusworks/common/configuration/ConfigurationManager.java @@ -19,132 +19,136 @@ import net.locusworks.common.utils.Utils; public class ConfigurationManager { - private Properties configuration; - private Properties defaults = null; - private Path conf = null; - - protected AES aes; - - private ConfigurationCallback callback; - - protected void init(String baseDir, String propertiesFile, ConfigurationCallback callback) throws IOException { - init(baseDir, propertiesFile, this.getClass().getName().getBytes(Charsets.UTF_8), callback); - } - - protected void init(String baseDir, String propertiesFile, byte[] aesKey, ConfigurationCallback callback) throws IOException { - aes = aesKey.length > 0 ? AES.createInstance(aesKey) : AES.createInstance(); - this.callback = callback; - try { - defaults = PropertiesManager.loadConfiguration(this.getClass(), propertiesFile); - } catch (IOException ex) { - throw ex; - } - // create patchrepoConf File object - conf = Paths.get(baseDir).resolve(propertiesFile); - - loadConfiguration(); - } - - private void loadConfiguration() { - // load the active config file - // ignore read error, we can continue with an empty configuration map - // and all default items will be added below and the file created - - callbackMessage("Loading config file: " + conf); - try { - configuration = PropertiesManager.loadConfiguration(conf); - } catch (Exception e) { - callbackMessage("Config file: " + conf + " will be created from template"); - configuration = new Properties(); + private Properties configuration; + private Properties defaults = null; + private Path conf = null; + + protected AES aes; + + private ConfigurationCallback callback; + + protected void init(String baseDir, String propertiesFile, ConfigurationCallback callback) throws IOException { + init(baseDir, propertiesFile, this.getClass().getName().getBytes(Charsets.UTF_8), callback); } - Map results = PropertiesManager.addConfiguration(configuration, defaults); - boolean changed = !results.isEmpty(); - if (!results.isEmpty()) { - StringBuilder sb = new StringBuilder("Added new configuration items:\n"); - for (Entry entry : results.entrySet()) { - sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue())); - } - callbackMessage(sb.toString()); - } - - results = PropertiesManager.removeConfiguration(configuration, defaults); - changed |= !results.isEmpty(); - if (!results.isEmpty()) { - StringBuilder sb = new StringBuilder("Added new configuration items:\n"); - for (Entry entry : results.entrySet()) { - sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue())); - } - callbackMessage(sb.toString()); - } - - if (changed) { - PropertiesManager.saveConfiguration(configuration, conf, "Patch Repository properties file"); - } - } - - /** - * Save the configuration values to file - * @param confs Configuration property values to save - * @throws Exception general exception - */ - public void saveToConf(Properties confs) throws Exception { - PropertiesManager.saveConfiguration(confs, conf, conf.getFileName().toString()); - callbackMessage("Saved config file: " + conf + ", " + confs.size() + " entries"); - loadConfiguration(); - } - - public String getPropertyValue(String key) { - return getPropertyValue(key, null); - } - public String getPropertyValue(String key, String defaultValue) { - return configuration.containsKey(key) ? configuration.getProperty(key) : defaultValue; - } - - public Properties getConfiguration() { - return configuration; - } - - public void saveConfiguration(PersistableRequest request, Set fieldsToSave, Set excryptedFields) throws Exception { - if (fieldsToSave == null || fieldsToSave.isEmpty()) { - throw ApplicationException.generic("No fields to save were defined"); - } - if (excryptedFields == null) { - excryptedFields = new HashSet<>(); - } - try { - Properties props = new Properties(); - - //copy what is current in the configuration settings into the new properties file - configuration.entrySet().forEach(item -> props.setProperty(String.valueOf(item.getKey()), String.valueOf(item.getValue()))); - boolean changed = false; - for (Field f : request.getClass().getDeclaredFields()) { - f.setAccessible(true); - String fieldName = f.getName(); - String fieldValue = String.valueOf(f.get(request)); + protected void init(String baseDir, String propertiesFile, byte[] aesKey, ConfigurationCallback callback) throws IOException { + aes = aesKey.length > 0 ? AES.createInstance(aesKey) : AES.createInstance(); + this.callback = callback; + try { + defaults = PropertiesManager.loadConfiguration(this.getClass(), propertiesFile); + } catch (IOException ex) { + throw ex; + } + // create patchrepoConf File object + conf = Paths.get(baseDir).resolve(propertiesFile); - //Ensures we are only saving values that are already configured - if (!fieldsToSave.contains(fieldName)) continue; - - //Check to see if the old value changed - String oldValue = props.getProperty(fieldName); - if (Utils.isNotValid(oldValue, fieldValue) || oldValue.equals(fieldValue)) { continue; } - - changed = true; - - fieldValue = excryptedFields.contains(fieldName) ? aes.encrypt(fieldValue) : fieldValue; - - props.setProperty(fieldName, fieldValue); - } - if (changed) { - saveToConf(props); - } - } catch (Exception ex) { - throw ApplicationException.actionNotPermitted(ex.getMessage()); + loadConfiguration(); + } + + private void loadConfiguration() { + // load the active config file + // ignore read error, we can continue with an empty configuration map + // and all default items will be added below and the file created + + callbackMessage("Loading config file: " + conf); + try { + configuration = PropertiesManager.loadConfiguration(conf); + } catch (Exception e) { + callbackMessage("Config file: " + conf + " will be created from template"); + configuration = new Properties(); + } + + Map results = PropertiesManager.addConfiguration(configuration, defaults); + boolean changed = !results.isEmpty(); + if (!results.isEmpty()) { + StringBuilder sb = new StringBuilder("Added new configuration items:\n"); + for (Entry entry : results.entrySet()) { + sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue())); + } + callbackMessage(sb.toString()); + } + + results = PropertiesManager.removeConfiguration(configuration, defaults); + changed |= !results.isEmpty(); + if (!results.isEmpty()) { + StringBuilder sb = new StringBuilder("Added new configuration items:\n"); + for (Entry entry : results.entrySet()) { + sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue())); + } + callbackMessage(sb.toString()); + } + + if (changed) { + PropertiesManager.saveConfiguration(configuration, conf, "Patch Repository properties file"); + } + } + + /** + * Save the configuration values to file + * + * @param confs Configuration property values to save + * @throws Exception general exception + */ + public void saveToConf(Properties confs) throws Exception { + PropertiesManager.saveConfiguration(confs, conf, conf.getFileName().toString()); + callbackMessage("Saved config file: " + conf + ", " + confs.size() + " entries"); + loadConfiguration(); + } + + public String getPropertyValue(String key) { + return getPropertyValue(key, null); + } + + public String getPropertyValue(String key, String defaultValue) { + return configuration.containsKey(key) ? configuration.getProperty(key) : defaultValue; + } + + public Properties getConfiguration() { + return configuration; + } + + public void saveConfiguration(PersistableRequest request, Set fieldsToSave, Set excryptedFields) throws Exception { + if (fieldsToSave == null || fieldsToSave.isEmpty()) { + throw ApplicationException.generic("No fields to save were defined"); + } + if (excryptedFields == null) { + excryptedFields = new HashSet<>(); + } + try { + Properties props = new Properties(); + + //copy what is current in the configuration settings into the new properties file + configuration.entrySet().forEach(item -> props.setProperty(String.valueOf(item.getKey()), String.valueOf(item.getValue()))); + boolean changed = false; + for (Field f : request.getClass().getDeclaredFields()) { + f.setAccessible(true); + String fieldName = f.getName(); + String fieldValue = String.valueOf(f.get(request)); + + //Ensures we are only saving values that are already configured + if (!fieldsToSave.contains(fieldName)) continue; + + //Check to see if the old value changed + String oldValue = props.getProperty(fieldName); + if (Utils.isNotValid(oldValue, fieldValue) || oldValue.equals(fieldValue)) { + continue; + } + + changed = true; + + fieldValue = excryptedFields.contains(fieldName) ? aes.encrypt(fieldValue) : fieldValue; + + props.setProperty(fieldName, fieldValue); + } + if (changed) { + saveToConf(props); + } + } catch (Exception ex) { + throw ApplicationException.actionNotPermitted(ex.getMessage()); + } + } + + private void callbackMessage(String msg) { + if (callback != null) callback.results(msg); } - } - - private void callbackMessage(String msg) { - if (callback != null) callback.results(msg); - } } diff --git a/src/main/java/net/locusworks/common/configuration/PropertiesManager.java b/src/main/java/net/locusworks/common/configuration/PropertiesManager.java index a9b8a0b..de4143b 100644 --- a/src/main/java/net/locusworks/common/configuration/PropertiesManager.java +++ b/src/main/java/net/locusworks/common/configuration/PropertiesManager.java @@ -19,134 +19,143 @@ import net.locusworks.common.immutables.Unit; /** * Properties manager class to help load and read properties + * * @author Isaac Parenteau * @version 2.0.0 * @date 09/17/2023 */ public class PropertiesManager { - /** - * Load a configuration from resource - * @param clazz class loader - * @param src source of the file - * @return properties - * @throws IOException Exception thrown the file can't be read - */ - public static Properties loadConfiguration(Class clazz, String src) throws IOException { - InputStream is = clazz.getResourceAsStream(src); - if (is == null) { - is = clazz.getClassLoader().getResourceAsStream(src); - } - if (is == null) { - return null; - } - BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8)); - return loadConfiguration(br); - } - - /** - * Load configuration from a file. This method has been deprecated and may be removed in - * future released. Please use java.nio.Path - * @param file File to load - * @return properties - * @throws IOException Exception thrown the file can't be read - */ - @Deprecated - public static Properties loadConfiguration(File file) throws IOException { - return loadConfiguration(file.toPath()); - } - - /** - * Load configuration from a file. - * @param path the path to the file - * @return properties - * @throws IOException Exception thrown the file can't be read - */ - public static Properties loadConfiguration(Path path) throws IOException { - if (Files.notExists(path)) { - return new Properties(); + /** + * Load a configuration from a resource + * + * @param clazz class loader + * @param src source of the file + * @return properties + * @throws IOException Exception thrown the file can't be read + */ + public static Properties loadConfiguration(Class clazz, String src) throws IOException { + InputStream is = clazz.getResourceAsStream(src); + if (is == null) { + is = clazz.getClassLoader().getResourceAsStream(src); + } + if (is == null) { + return null; + } + BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8)); + return loadConfiguration(br); } - try(BufferedReader br = Files.newBufferedReader(path)) { - return loadConfiguration(br); + /** + * Load configuration from a file. This method has been deprecated and may be removed in + * future released. Please use java.nio.Path + * + * @param file File to load + * @return properties + * @throws IOException Exception thrown the file can't be read + */ + @Deprecated + public static Properties loadConfiguration(File file) throws IOException { + return loadConfiguration(file.toPath()); } - } - /** - * Load configuration from a buffered reader - * @param reader Buffered reader to read the properties values from - * @return properties - * @throws IOException Exception thrown the file can't be read - */ - public static Properties loadConfiguration(BufferedReader reader) throws IOException { - Properties config = new Properties(); - config.load(reader); - return config; - } + /** + * Load configuration from a file. + * + * @param path the path to the file + * @return properties + * @throws IOException Exception thrown the file can't be read + */ + public static Properties loadConfiguration(Path path) throws IOException { + if (Files.notExists(path)) { + return new Properties(); + } - /** - * Add configurations from one properties file to another - * @param to Properties file to copy values to - * @param from Properties file to copy values from - * @return a map containing the results of the values added - */ - public static Map addConfiguration(Properties to, Properties from) { - - return from.entrySet() - .stream() - .filter(entry -> !to.containsKey(entry.getKey())) - .map(entry -> { - String key = entry.getKey().toString(); - String value = entry.getValue().toString(); - to.put(key, value); - return new Pair(key, value); - }) - .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); - } - - /** - * Removes configuration values that are not present in the comparedTo - * @param from Properties file to remove values from - * @param comparedTo Properties file to compare to - * @return a map containing the results of the values removed - */ - public static Map removeConfiguration(Properties from, Properties comparedTo) { - - return from.keySet() - .stream() - .filter(key -> !comparedTo.containsKey(key)) //only get the items that are not in the comparedTo properties - .map(key -> new Pair(String.valueOf(key), String.valueOf(from.get(key)))) - .toList() //Create a list of paired items (key value) of the items that were filtered - .stream() - .peek(pair -> { //remove those pairs from the from properties - from.remove(pair.getValue1()); - }) - .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); - } - - /** - *

Save the properties file to disk.

- *

This method has been depecreated and could be removed in future release.
Please use java.nio.Path

- * @param props Properties file to save - * @param fileToSave File to save to - * @param comment Any comments to add - */ - @Deprecated - public static void saveConfiguration(Properties props, File fileToSave, String comment) { - saveConfiguration(props, fileToSave.toPath(), comment); - - } - - /** - *

Save the properties file to disk.

- * @param props Properties file to save - * @param fileToSave File to save to - * @param comment Any comments to add - */ - public static void saveConfiguration(Properties props, Path fileToSave, String comment) { - try(OutputStream fos = Files.newOutputStream(fileToSave)) { - props.store(fos, comment == null ? "" : comment); - } catch (IOException ex) { - throw new RuntimeException(ex.getMessage(), ex); + try (BufferedReader br = Files.newBufferedReader(path)) { + return loadConfiguration(br); + } + } + + /** + * Load configuration from a buffered reader + * + * @param reader Buffered reader to read the properties values from + * @return properties + * @throws IOException Exception thrown the file can't be read + */ + public static Properties loadConfiguration(BufferedReader reader) throws IOException { + Properties config = new Properties(); + config.load(reader); + return config; + } + + /** + * Add configurations from one properties file to another + * + * @param to Properties file to copy values to + * @param from Properties file to copy values from + * @return a map containing the results of the values added + */ + public static Map addConfiguration(Properties to, Properties from) { + + return from.entrySet() + .stream() + .filter(entry -> !to.containsKey(entry.getKey())) + .map(entry -> { + String key = entry.getKey().toString(); + String value = entry.getValue().toString(); + to.put(key, value); + return new Pair(key, value); + }) + .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); + } + + /** + * Removes configuration values that are not present in the comparedTo + * + * @param from Properties file to remove values from + * @param comparedTo Properties file to compare to + * @return a map containing the results of the values removed + */ + public static Map removeConfiguration(Properties from, Properties comparedTo) { + + return from.keySet() + .stream() + .filter(key -> !comparedTo.containsKey(key)) //only get the items that are not in the comparedTo properties + .map(key -> new Pair(String.valueOf(key), String.valueOf(from.get(key)))) + .toList() //Create a list of paired items (key value) of the items that were filtered + .stream() + .peek(pair -> { //remove those pairs from the from properties + from.remove(pair.getValue1()); + }) + .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2)); + } + + /** + *

Save the properties file to disk.

+ *

This method has been depecreated and could be removed in future release.
Please use java.nio.Path

+ * + * @param props Properties file to save + * @param fileToSave File to save to + * @param comment Any comments to add + */ + @Deprecated + public static void saveConfiguration(Properties props, File fileToSave, String comment) { + saveConfiguration(props, fileToSave.toPath(), comment); + + } + + /** + *

Save the properties file to disk.

+ * + * @param props Properties file to save + * @param fileToSave File to save to + * @param comment Any comments to add + */ + public static void saveConfiguration(Properties props, Path fileToSave, String comment) { + try (OutputStream fos = Files.newOutputStream(fileToSave)) { + props.store(fos, comment == null ? "" : comment); + } catch (IOException ex) { + throw new RuntimeException(ex.getMessage(), ex); + } } - } } diff --git a/src/main/java/net/locusworks/common/crypto/AES.java b/src/main/java/net/locusworks/common/crypto/AES.java index d329715..91ef029 100644 --- a/src/main/java/net/locusworks/common/crypto/AES.java +++ b/src/main/java/net/locusworks/common/crypto/AES.java @@ -12,15 +12,17 @@ import java.util.Base64; import net.locusworks.common.utils.RandomString; import net.locusworks.common.utils.Utils; + import static net.locusworks.common.Charsets.UTF_8; /** * AES encryption/decryption class - * This class will encrypt/decrypt data. The encryption key is never known. + * This class will encrypt/decrypt data. The encryption key is never known. * Instead, it is generated by the provided seed. As long as the seed stays the same * the key will remain the same and the encryption/decryption will work. This * provides and added security. + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 @@ -28,130 +30,133 @@ import static net.locusworks.common.Charsets.UTF_8; */ public class AES { - private static final String ENCRYPTION_TYPE = "AES"; - private static final String ENCRYPTION_ALGORITH = "AES/CBC/PKCS5Padding"; - private static final String PROVIDER = "SunJCE"; - private static final String ALGORITHM = "SHA1PRNG"; + private static final String ENCRYPTION_TYPE = "AES"; + private static final String ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"; + private static final String PROVIDER = "SunJCE"; + private static final String ALGORITHM = "SHA1PRNG"; - private Cipher cipher; + private Cipher cipher; - private SecretKeySpec secretKeySpec; + private SecretKeySpec secretKeySpec; - private IvParameterSpec ivParamSpec; + private IvParameterSpec ivParamSpec; - private String seed; + private String seed; - private RandomString randomizer; + private RandomString randomizer; - private void initSecureKey(String seed) { - try { - SecureRandom sr = getSecureRandom(seed); - KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE); - generator.init(128, sr); + private void initSecureKey(String seed) { + try { + SecureRandom sr = getSecureRandom(seed); + KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE); + generator.init(128, sr); - randomizer = RandomString.newInstance(sr); - init(generator.generateKey().getEncoded()); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to initialize encryption:", ex); + randomizer = RandomString.newInstance(sr); + init(generator.generateKey().getEncoded()); + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to initialize encryption:", ex); + } } - } - /** - * Initializes the secure random object to be the same across all platforms - * with regard to provider and algorithm used - * @param seed Seed to initialize SecureRandom with - * @return SecureRandom object - * @throws NoSuchAlgorithmException thrown when algorithm can't be used - */ - private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException { - SecureRandom sr = SecureRandom.getInstance(ALGORITHM); - sr.setSeed(seed.getBytes(UTF_8)); - return sr; - } + /** + * Initializes the secure random object to be the same across all platforms + * with regard to provider and algorithm used + * + * @param seed Seed to initialize SecureRandom with + * @return SecureRandom object + * @throws NoSuchAlgorithmException thrown when algorithm can't be used + */ + private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException { + SecureRandom sr = SecureRandom.getInstance(ALGORITHM); + sr.setSeed(seed.getBytes(UTF_8)); + return sr; + } - /** - * Initialize the aes engine - * @param key secret key to use - */ - private void init(final byte[] key) { - try { - this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITH, PROVIDER); - this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE); - this.ivParamSpec = new IvParameterSpec(randomizer.getBytes(16)); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to initialize encryption:", ex); + /** + * Initialize the aes engine + * + * @param key secret key to use + */ + private void init(final byte[] key) { + try { + this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER); + this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE); + this.ivParamSpec = new IvParameterSpec(randomizer.getBytes(16)); + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to initialize encryption:", ex); + } } - } - /*** - * Encrypt a text string - * @param plainText String to encrypt - * @return encrypted string - */ - public String encrypt(String plainText) { - if (Utils.isEmptyString(plainText)) { - plainText = ""; + /*** + * Encrypt a text string + * @param plainText String to encrypt + * @return encrypted string + */ + public String encrypt(String plainText) { + if (Utils.isEmptyString(plainText)) { + plainText = ""; + } + try { + cipher.init(Cipher.ENCRYPT_MODE, this.secretKeySpec, this.ivParamSpec); + byte[] cypherText = cipher.doFinal(plainText.getBytes(UTF_8)); + return new String(Base64.getEncoder().encode(cypherText), UTF_8); + } catch (Exception ex) { + throw new IllegalArgumentException(ex.getMessage(), ex); + } } - try { - cipher.init(Cipher.ENCRYPT_MODE, this.secretKeySpec, this.ivParamSpec); - byte[] cypherText = cipher.doFinal(plainText.getBytes(UTF_8)); - return new String(Base64.getEncoder().encode(cypherText), UTF_8); - } catch (Exception ex) { - throw new IllegalArgumentException(ex.getMessage(), ex); - } - } - /*** - * Decrypt an encrypted string - * @param cipherString encrypted string to decrypt - * @return unecrypted string - */ - public String decrypt(String cipherString) { - if (Utils.isEmptyString(cipherString)) { - return ""; - } - byte[] cipherText = Base64.getDecoder().decode(cipherString.getBytes(UTF_8)); - try { - cipher.init(Cipher.DECRYPT_MODE, this.secretKeySpec, this.ivParamSpec); - return new String(cipher.doFinal(cipherText), UTF_8); - } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { - throw new IllegalArgumentException(e.getMessage(), e); + /*** + * Decrypt an encrypted string + * @param cipherString encrypted string to decrypt + * @return unecrypted string + */ + public String decrypt(String cipherString) { + if (Utils.isEmptyString(cipherString)) { + return ""; + } + byte[] cipherText = Base64.getDecoder().decode(cipherString.getBytes(UTF_8)); + try { + cipher.init(Cipher.DECRYPT_MODE, this.secretKeySpec, this.ivParamSpec); + return new String(cipher.doFinal(cipherText), UTF_8); + } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | + InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } } - } - - public AES withSeed(String seed) { - if (this.seed == null || !this.seed.equals(seed)) { - initSecureKey(seed); - } - this.seed = seed; - return this; - } - - public final String getSeed() { - return this.seed; - } - public static AES createInstance() { - return createInstance(RandomString.getInstance().getString(16)); - } - - public static AES createInstance(byte[] byteSeed) { - String seed = new String(byteSeed, UTF_8); - return createInstance(seed); - } - - public static AES createInstance(String seed) { - return new AES().withSeed(seed); - } - - public static void main(String[] args) throws NoSuchAlgorithmException { - if (args == null || !(args.length > 0)) { - throw new IllegalArgumentException("No args provided. Need password as argument"); + public AES withSeed(String seed) { + if (this.seed == null || !this.seed.equals(seed)) { + initSecureKey(seed); + } + this.seed = seed; + return this; } - if (args.length % 2 == 0) { - System.out.println(AES.createInstance(String.valueOf(args[1])).decrypt(String.valueOf(args[0]))); - } else { - System.out.println(AES.createInstance().encrypt(String.valueOf(args[0]))); + + public final String getSeed() { + return this.seed; + } + + public static AES createInstance() { + return createInstance(RandomString.getInstance().getString(16)); + } + + public static AES createInstance(byte[] byteSeed) { + String seed = new String(byteSeed, UTF_8); + return createInstance(seed); + } + + public static AES createInstance(String seed) { + return new AES().withSeed(seed); + } + + public static void main(String[] args) throws NoSuchAlgorithmException { + if (args == null || !(args.length > 0)) { + throw new IllegalArgumentException("No args provided. Need password as argument"); + } + if (args.length % 2 == 0) { + System.out.println(AES.createInstance(String.valueOf(args[1])).decrypt(String.valueOf(args[0]))); + } else { + System.out.println(AES.createInstance().encrypt(String.valueOf(args[0]))); + } } - } } diff --git a/src/main/java/net/locusworks/common/crypto/AESKey.java b/src/main/java/net/locusworks/common/crypto/AESKey.java index 3bec359..c0a8ef0 100644 --- a/src/main/java/net/locusworks/common/crypto/AESKey.java +++ b/src/main/java/net/locusworks/common/crypto/AESKey.java @@ -6,26 +6,27 @@ import java.security.PrivateKey; import net.locusworks.common.Charsets; public class AESKey implements PrivateKey { - - @Serial private static final long serialVersionUID = -8452357427706386362L; - private final String seed; - - public AESKey(String seed) { - this.seed = seed; - } - @Override - public String getAlgorithm() { - return "aes"; - } + @Serial + private static final long serialVersionUID = -8452357427706386362L; + private final String seed; - @Override - public String getFormat() { - return "aes-seed"; - } + public AESKey(String seed) { + this.seed = seed; + } - @Override - public byte[] getEncoded() { - return this.seed.getBytes(Charsets.UTF_8); - } + @Override + public String getAlgorithm() { + return "aes"; + } + + @Override + public String getFormat() { + return "aes-seed"; + } + + @Override + public byte[] getEncoded() { + return this.seed.getBytes(Charsets.UTF_8); + } } diff --git a/src/main/java/net/locusworks/common/crypto/AESKeySpec.java b/src/main/java/net/locusworks/common/crypto/AESKeySpec.java index a29c54f..3df5ea9 100644 --- a/src/main/java/net/locusworks/common/crypto/AESKeySpec.java +++ b/src/main/java/net/locusworks/common/crypto/AESKeySpec.java @@ -15,31 +15,31 @@ import static net.locusworks.common.utils.Utils.get; import static net.locusworks.common.utils.Utils.size; public class AESKeySpec extends EncodedKeySpec { - - private static final String AES_MARKER = "aes-seed"; - public AESKeySpec(byte[] encodedKey) { - super(encodedKey); - } - - public AESKey generateKey() throws InvalidKeySpecException { - try { - byte[] data = this.getEncoded(); - InputStream stream = new ByteArrayInputStream(data); - Iterable parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" ")); - - checkArguments(size(parts) == 2 && AES_MARKER.equals(get(parts, 0)), "Bad format, should be: aes-seed AAB3..."); - stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1)))); - String marker = IOUtils.toString(stream, Charsets.UTF_8); - return new AESKey(marker); - } catch (Exception ex) { - throw new InvalidKeySpecException(ex); + private static final String AES_MARKER = "aes-seed"; + + public AESKeySpec(byte[] encodedKey) { + super(encodedKey); } - } - @Override - public String getFormat() { - return "aes"; - } + public AESKey generateKey() throws InvalidKeySpecException { + try { + byte[] data = this.getEncoded(); + InputStream stream = new ByteArrayInputStream(data); + Iterable parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" ")); + + checkArguments(size(parts) == 2 && AES_MARKER.equals(get(parts, 0)), "Bad format, should be: aes-seed AAB3..."); + stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1)))); + String marker = IOUtils.toString(stream, Charsets.UTF_8); + return new AESKey(marker); + } catch (Exception ex) { + throw new InvalidKeySpecException(ex); + } + } + + @Override + public String getFormat() { + return "aes"; + } } diff --git a/src/main/java/net/locusworks/common/crypto/EncryptionKeyFactory.java b/src/main/java/net/locusworks/common/crypto/EncryptionKeyFactory.java index e21d4f7..c2737f3 100644 --- a/src/main/java/net/locusworks/common/crypto/EncryptionKeyFactory.java +++ b/src/main/java/net/locusworks/common/crypto/EncryptionKeyFactory.java @@ -14,25 +14,25 @@ import sun.security.jca.GetInstance.Instance; public class EncryptionKeyFactory extends KeyFactory { - protected EncryptionKeyFactory(KeyFactorySpi keyFacSpi, Provider provider, String algorithm) { - super(keyFacSpi, provider, algorithm); - } - - public PrivateKey generatePrivateKey(KeySpec keySpec) throws InvalidKeySpecException { - if (keySpec instanceof AESKeySpec) { - return ((AESKeySpec)keySpec).generateKey(); + protected EncryptionKeyFactory(KeyFactorySpi keyFacSpi, Provider provider, String algorithm) { + super(keyFacSpi, provider, algorithm); + } + + public PrivateKey generatePrivateKey(KeySpec keySpec) throws InvalidKeySpecException { + if (keySpec instanceof AESKeySpec) { + return ((AESKeySpec) keySpec).generateKey(); + } + return super.generatePrivate(keySpec); + } + + public PublicKey generatePublicKey(KeySpec keySpec) throws InvalidKeySpecException { + keySpec = keySpec instanceof SSHEncodedKeySpec ? ((SSHEncodedKeySpec) keySpec).convertToRSAPubKeySpec() : keySpec; + return super.generatePublic(keySpec); + } + + public static EncryptionKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException { + Instance instance = GetInstance.getInstance("KeyFactory", KeyFactorySpi.class, algorithm); + return new EncryptionKeyFactory((KeyFactorySpi) instance.impl, instance.provider, algorithm); } - return super.generatePrivate(keySpec); - } - - public PublicKey generatePublicKey(KeySpec keySpec) throws InvalidKeySpecException { - keySpec = keySpec instanceof SSHEncodedKeySpec ? ((SSHEncodedKeySpec)keySpec).convertToRSAPubKeySpec() : keySpec; - return super.generatePublic(keySpec); - } - - public static EncryptionKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException { - Instance instance = GetInstance.getInstance("KeyFactory", KeyFactorySpi.class, algorithm); - return new EncryptionKeyFactory((KeyFactorySpi)instance.impl, instance.provider, algorithm); - } } diff --git a/src/main/java/net/locusworks/common/crypto/HashSalt.java b/src/main/java/net/locusworks/common/crypto/HashSalt.java index 1c84004..ca7ec9c 100644 --- a/src/main/java/net/locusworks/common/crypto/HashSalt.java +++ b/src/main/java/net/locusworks/common/crypto/HashSalt.java @@ -9,181 +9,178 @@ import javax.crypto.spec.PBEKeySpec; /** * The type Hash salt. + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 */ public class HashSalt { - /** - * The constant PBKDF2_ALGORITHM. - */ - private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; + /** + * The constant PBKDF2_ALGORITHM. + */ + private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; - /** - * The constant SALT_BYTE_SIZE. - */ - private static final int SALT_BYTE_SIZE = 24; + /** + * The constant SALT_BYTE_SIZE. + */ + private static final int SALT_BYTE_SIZE = 24; - /** - * The constant HASH_BYTE_SIZE. - */ - private static final int HASH_BYTE_SIZE = 24; + /** + * The constant HASH_BYTE_SIZE. + */ + private static final int HASH_BYTE_SIZE = 24; - /** - * The constant PBKDF2_ITERATIONS. - */ - private static final int PBKDF2_ITERATIONS = 1000; + /** + * The constant PBKDF2_ITERATIONS. + */ + private static final int PBKDF2_ITERATIONS = 1000; - /** - * The constant ITERATION_INDEX. - */ - private static final int ITERATION_INDEX = 0; - /** - * The constant SALT_INDEX. - */ - private static final int SALT_INDEX = 1; - /** - * The constant PBKDF2_INDEX. - */ - private static final int PBKDF2_INDEX = 2; + /** + * The constant ITERATION_INDEX. + */ + private static final int ITERATION_INDEX = 0; + /** + * The constant SALT_INDEX. + */ + private static final int SALT_INDEX = 1; + /** + * The constant PBKDF2_INDEX. + */ + private static final int PBKDF2_INDEX = 2; - /** - * Returns a salted PBKDF2 hash of the password. - * - * @param password the password to hash - * - * @return a salted PBKDF2 hash of the password - * @throws NoSuchAlgorithmException the no such algorithm exception - * @throws InvalidKeySpecException the invalid key spec exception - */ - public static String createHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { - return createHash(password.toCharArray()); - } - - /** - * Returns a salted PBKDF2 hash of the password. - * - * @param password the password to hash - * - * @return a salted PBKDF2 hash of the password - * @throws NoSuchAlgorithmException the no such algorithm exception - * @throws InvalidKeySpecException the invalid key spec exception - */ - public static String createHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException { - // Generate a random salt - SecureRandom random = new SecureRandom(); - byte[] salt = new byte[SALT_BYTE_SIZE]; - random.nextBytes(salt); - - // Hash the password - byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE); - // format iterations:salt:hash - return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash); - } - - /** - * Validates a password using a hash. - * - * @param password the password to check - * @param correctHash the hash of the valid password - * - * @return true if the password is correct, false if not - * @throws NoSuchAlgorithmException the no such algorithm exception - * @throws InvalidKeySpecException the invalid key spec exception - */ - public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException { - return validatePassword(password.toCharArray(), correctHash); - } - - /** - * Validates a password using a hash. - * - * @param password the password to check - * @param correctHash the hash of the valid password - * - * @return true if the password is correct, false if not - * @throws NoSuchAlgorithmException the no such algorithm exception - * @throws InvalidKeySpecException the invalid key spec exception - */ - public static boolean validatePassword(char[] password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException { - // Decode the hash into its parameters - String[] params = correctHash.split(":"); - int iterations = Integer.parseInt(params[ITERATION_INDEX]); - byte[] salt = fromHex(params[SALT_INDEX]); - byte[] hash = fromHex(params[PBKDF2_INDEX]); - // Compute the hash of the provided password, using the same salt, - // iteration count, and hash length - byte[] testHash = pbkdf2(password, salt, iterations, hash.length); - // Compare the hashes in constant time. The password is correct if - // both hashes match. - return slowEquals(hash, testHash); - } - - /** - * Compares two byte arrays in length-constant time. This comparison method - * is used so that password hashes cannot be extracted from an on-line - * system using a timing attack and then attacked off-line. - * - * @param a the first byte array - * @param b the second byte array - * @return true if both byte arrays are the same, false if not - */ - private static boolean slowEquals(byte[] a, byte[] b) { - int diff = a.length ^ b.length; - for(int i = 0; i < a.length && i < b.length; i++) - diff |= a[i] ^ b[i]; - return diff == 0; - } - - /** - * Computes the PBKDF2 hash of a password. - * - * @param password the password to hash. - * @param salt the salt - * @param iterations the iteration count (slowness factor) - * @param bytes the length of the hash to compute in bytes - * @return the PBDKF2 hash of the password - */ - private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { - PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8); - SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); - return skf.generateSecret(spec).getEncoded(); - } - - /** - * Converts a string of hexadecimal characters into a byte array. - * - * @param hex the hex string - * @return the hex string decoded into a byte array - */ - private static byte[] fromHex(String hex) { - byte[] binary = new byte[hex.length() / 2]; - for(int i = 0; i < binary.length; i++) { - binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16); + /** + * Returns a salted PBKDF2 hash of the password. + * + * @param password the password to hash + * @return a salted PBKDF2 hash of the password + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeySpecException the invalid key spec exception + */ + public static String createHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { + return createHash(password.toCharArray()); } - return binary; - } - /** - * Converts a byte array into a hexadecimal string. - * - * @param array the byte array to convert - * @return a length*2 character string encoding the byte array - */ - private static String toHex(byte[] array) { - BigInteger bi = new BigInteger(1, array); - String hex = bi.toString(16); - int paddingLength = (array.length * 2) - hex.length(); - if(paddingLength > 0) - return String.format("%0" + paddingLength + "d", 0) + hex; - else - return hex; - } + /** + * Returns a salted PBKDF2 hash of the password. + * + * @param password the password to hash + * @return a salted PBKDF2 hash of the password + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeySpecException the invalid key spec exception + */ + public static String createHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException { + // Generate a random salt + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[SALT_BYTE_SIZE]; + random.nextBytes(salt); - public static void main (String[] args) throws Exception { - if (args == null || !(args.length > 0)) { - throw new IllegalArgumentException("No args provided. Need password as argument"); + // Hash the password + byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE); + // format iterations:salt:hash + return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash); + } + + /** + * Validates a password using a hash. + * + * @param password the password to check + * @param correctHash the hash of the valid password + * @return true if the password is correct, false if not + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeySpecException the invalid key spec exception + */ + public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException { + return validatePassword(password.toCharArray(), correctHash); + } + + /** + * Validates a password using a hash. + * + * @param password the password to check + * @param correctHash the hash of the valid password + * @return true if the password is correct, false if not + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeySpecException the invalid key spec exception + */ + public static boolean validatePassword(char[] password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException { + // Decode the hash into its parameters + String[] params = correctHash.split(":"); + int iterations = Integer.parseInt(params[ITERATION_INDEX]); + byte[] salt = fromHex(params[SALT_INDEX]); + byte[] hash = fromHex(params[PBKDF2_INDEX]); + // Compute the hash of the provided password, using the same salt, + // iteration count, and hash length + byte[] testHash = pbkdf2(password, salt, iterations, hash.length); + // Compare the hashes in constant time. The password is correct if + // both hashes match. + return slowEquals(hash, testHash); + } + + /** + * Compares two byte arrays in length-constant time. This comparison method + * is used so that password hashes cannot be extracted from an on-line + * system using a timing attack and then attacked off-line. + * + * @param a the first byte array + * @param b the second byte array + * @return true if both byte arrays are the same, false if not + */ + private static boolean slowEquals(byte[] a, byte[] b) { + int diff = a.length ^ b.length; + for (int i = 0; i < a.length && i < b.length; i++) + diff |= a[i] ^ b[i]; + return diff == 0; + } + + /** + * Computes the PBKDF2 hash of a password. + * + * @param password the password to hash. + * @param salt the salt + * @param iterations the iteration count (slowness factor) + * @param bytes the length of the hash to compute in bytes + * @return the PBDKF2 hash of the password + */ + private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { + PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8); + SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); + return skf.generateSecret(spec).getEncoded(); + } + + /** + * Converts a string of hexadecimal characters into a byte array. + * + * @param hex the hex string + * @return the hex string decoded into a byte array + */ + private static byte[] fromHex(String hex) { + byte[] binary = new byte[hex.length() / 2]; + for (int i = 0; i < binary.length; i++) { + binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); + } + return binary; + } + + /** + * Converts a byte array into a hexadecimal string. + * + * @param array the byte array to convert + * @return a length*2 character string encoding the byte array + */ + private static String toHex(byte[] array) { + BigInteger bi = new BigInteger(1, array); + String hex = bi.toString(16); + int paddingLength = (array.length * 2) - hex.length(); + if (paddingLength > 0) + return String.format("%0" + paddingLength + "d", 0) + hex; + else + return hex; + } + + public static void main(String[] args) throws Exception { + if (args == null || !(args.length > 0)) { + throw new IllegalArgumentException("No args provided. Need password as argument"); + } + System.out.println(HashSalt.createHash(String.valueOf(args[0]))); } - System.out.println(HashSalt.createHash(String.valueOf(args[0]))); - } } \ No newline at end of file diff --git a/src/main/java/net/locusworks/common/crypto/KeyFile.java b/src/main/java/net/locusworks/common/crypto/KeyFile.java index 0ebffec..bad712b 100644 --- a/src/main/java/net/locusworks/common/crypto/KeyFile.java +++ b/src/main/java/net/locusworks/common/crypto/KeyFile.java @@ -27,187 +27,195 @@ import static net.locusworks.common.utils.Splitter.fixedLengthSplit; import static java.lang.String.join; public class KeyFile implements AutoCloseable { - - public enum EncryptionType { - RSA, - AES, - SSH - } - - private Key key; - private String description; - private Writer writer; - private EncryptionType encryptionType; - - public KeyFile(Key key) { - this(key, null); - } - - public KeyFile(Key key, String description) { - this(key, description, EncryptionType.valueOf(key.getAlgorithm().toUpperCase())); - } - - public KeyFile(Key key, String description, EncryptionType encryptionType) { - this.key = key; - this.description = description; - this.encryptionType = encryptionType; - } - - private KeyFile() {} - - private void loadFromFile(String fileName) { - if (Utils.isEmptyString(fileName)) return; - - this.key = null; - try { - File keyFile = new File(fileName); - if (!keyFile.exists()) { - throw new IllegalArgumentException(String.format("Unable to find file with name %s. Please check path", fileName)); - } - - String contentStr = IOUtils.toString(new FileInputStream(keyFile), Charsets.UTF_8); - - boolean rsaFormat = !contentStr.startsWith("ssh-rsa") && !contentStr.startsWith("aes-seed"); - if (rsaFormat) { - contentStr = contentStr.replace("-----.*", ""); - } - - contentStr = contentStr.replace("\\r?\\n", ""); - - byte[] content = rsaFormat ? Base64.getDecoder().decode(contentStr): contentStr.getBytes(Charsets.UTF_8); - - EncryptionKeyFactory kf = EncryptionKeyFactory.getInstance("RSA"); - - List keySpecs = Utils.toList( - new KeySpecHelper(new AESKeySpec(content), true, EncryptionType.AES), - new KeySpecHelper(new SSHEncodedKeySpec(content), false, EncryptionType.SSH), - new KeySpecHelper(new PKCS8EncodedKeySpec(content), true, EncryptionType.RSA), - new KeySpecHelper(new X509EncodedKeySpec(content), false, EncryptionType.RSA) - ); - - for (KeySpecHelper ksh : keySpecs) { + + public enum EncryptionType { + RSA, + AES, + SSH + } + + private Key key; + private String description; + private Writer writer; + private EncryptionType encryptionType; + + public KeyFile(Key key) { + this(key, null); + } + + public KeyFile(Key key, String description) { + this(key, description, EncryptionType.valueOf(key.getAlgorithm().toUpperCase())); + } + + public KeyFile(Key key, String description, EncryptionType encryptionType) { + this.key = key; + this.description = description; + this.encryptionType = encryptionType; + } + + private KeyFile() { + } + + private void loadFromFile(String fileName) { + if (Utils.isEmptyString(fileName)) return; + + this.key = null; try { - this.key = ksh.isPrivate() ? kf.generatePrivateKey(ksh.getKeySpec()) : kf.generatePublicKey(ksh.getKeySpec()); - this.encryptionType = ksh.getEncryptionType(); - break; - } catch (NullPointerException | InvalidKeySpecException ikse) { continue; } - } - - throw new InvalidKeySpecException(String.format("Unable to determine if file %s is a private or public key. Not type of PKCS8, X509, SSH or AES spec", fileName)); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public void write(String fileName) { - try { - String data; - switch(this.encryptionType) { - case AES: - data = String.format("%s %s", this.key.getFormat(), Base64.getEncoder().encodeToString(this.key.getEncoded())); - IOUtils.writeStringToFile(fileName, data); - break; - case SSH: - try (DataOutputStreamHelper dosh = new DataOutputStreamHelper()) { - getSSHPubKeyBytes(this.key) - .forEach(handleExceptionWrapper(item ->{ - dosh.writeInt(item.length); - dosh.write(item); - })); - data = String.format("ssh-rsa %s %s", dosh.base64Encoded(), this.description); - IOUtils.writeStringToFile(fileName, data); + File keyFile = new File(fileName); + if (!keyFile.exists()) { + throw new IllegalArgumentException(String.format("Unable to find file with name %s. Please check path", fileName)); + } + + String contentStr; + try (FileInputStream input = new FileInputStream(keyFile)) { + contentStr = IOUtils.toString(input, Charsets.UTF_8); + } + + boolean rsaFormat = !contentStr.startsWith("ssh-rsa") && !contentStr.startsWith("aes-seed"); + if (rsaFormat) { + contentStr = contentStr.replaceAll("-----[^-]+-----", ""); + contentStr = contentStr.replaceAll("\\s", ""); + } else { + contentStr = contentStr.trim(); + } + + byte[] content = rsaFormat ? Base64.getDecoder().decode(contentStr) : contentStr.getBytes(Charsets.UTF_8); + + EncryptionKeyFactory kf = EncryptionKeyFactory.getInstance("RSA"); + + List keySpecs = Utils.toList( + new KeySpecHelper(new AESKeySpec(content), true, EncryptionType.AES), + new KeySpecHelper(new SSHEncodedKeySpec(content), false, EncryptionType.SSH), + new KeySpecHelper(new PKCS8EncodedKeySpec(content), true, EncryptionType.RSA), + new KeySpecHelper(new X509EncodedKeySpec(content), false, EncryptionType.RSA) + ); + + for (KeySpecHelper ksh : keySpecs) { + try { + this.key = ksh.isPrivate() ? kf.generatePrivateKey(ksh.getKeySpec()) : kf.generatePublicKey(ksh.getKeySpec()); + this.encryptionType = ksh.getEncryptionType(); + break; + } catch (NullPointerException | InvalidKeySpecException ikse) { + continue; + } + } + if (this.key == null) { + throw new InvalidKeySpecException(String.format("Unable to determine if file %s is a private or public key. Not type of PKCS8, X509, SSH or AES spec", fileName)); + } + } catch (Exception ex) { + throw new RuntimeException(ex); } - break; - default: - writePem(fileName); - } - } catch (Exception ex) { - throw new IllegalArgumentException(ex); } - } - - public void setDescription(String description) { - this.description = description; - } - - public String getDescription() { - if (Utils.isEmptyString(this.description)) { - return this.key instanceof PrivateKey ? "PRIVATE KEY" : "PUBLIC KEY"; + + public void write(String fileName) { + try { + String data; + switch (this.encryptionType) { + case AES: + data = String.format("%s %s", this.key.getFormat(), Base64.getEncoder().encodeToString(this.key.getEncoded())); + IOUtils.writeStringToFile(fileName, data); + break; + case SSH: + try (DataOutputStreamHelper dosh = new DataOutputStreamHelper()) { + getSSHPubKeyBytes(this.key) + .forEach(handleExceptionWrapper(item -> { + dosh.writeInt(item.length); + dosh.write(item); + })); + data = String.format("ssh-rsa %s %s", dosh.base64Encoded(), this.description); + IOUtils.writeStringToFile(fileName, data); + } + break; + default: + writePem(fileName); + } + } catch (Exception ex) { + throw new IllegalArgumentException(ex); + } } - return this.description; - } - - public Key getKey() { - return this.key; - } - - @Override - public void close() { - try { - if (this.writer != null) { - this.writer.flush(); - this.writer.close(); - this.writer = null; - } - } catch (Exception ex) { - throw new RuntimeException(ex); + + public void setDescription(String description) { + this.description = description; } - } - - public static KeyFile read(String fileName) { - try (KeyFile kf = new KeyFile()) { - kf.loadFromFile(fileName); - return kf; + + public String getDescription() { + if (Utils.isEmptyString(this.description)) { + return this.key instanceof PrivateKey ? "PRIVATE KEY" : "PUBLIC KEY"; + } + return this.description; } - } - - private void writePem(String fileName) { - try { - String desc = getDescription(); - this.writer = new OutputStreamWriter(new FileOutputStream(fileName), Charsets.UTF_8); - this.writer.write(String.format("-----BEGIN RSA %s-----", desc)); - - String encoded = Base64.getEncoder().encodeToString(this.key.getEncoded()); - String out = join("\n", fixedLengthSplit(60).split(encoded)); - - this.writer.write(out); - this.writer.write(String.format("-----END RSA %s-----", desc)); - } catch (IOException ex) { - throw new RuntimeException(ex); + + public Key getKey() { + return this.key; } - } - - private List getSSHPubKeyBytes(Key key) { - RSAPublicKey rpk = (RSAPublicKey)key; - return Arrays.asList("ssh-rsa".getBytes(Charsets.UTF_8), - rpk.getPublicExponent().toByteArray(), - rpk.getModulus().toByteArray() + + @Override + public void close() { + try { + if (this.writer != null) { + this.writer.flush(); + this.writer.close(); + this.writer = null; + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static KeyFile read(String fileName) { + try (KeyFile kf = new KeyFile()) { + kf.loadFromFile(fileName); + return kf; + } + } + + private void writePem(String fileName) { + try { + String desc = getDescription(); + this.writer = new OutputStreamWriter(new FileOutputStream(fileName), Charsets.UTF_8); + this.writer.write(String.format("-----BEGIN RSA %s-----", desc)); + + String encoded = Base64.getEncoder().encodeToString(this.key.getEncoded()); + String out = join("\n", fixedLengthSplit(60).split(encoded)); + + this.writer.write(out); + this.writer.write(String.format("-----END RSA %s-----", desc)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private List getSSHPubKeyBytes(Key key) { + RSAPublicKey rpk = (RSAPublicKey) key; + return Arrays.asList("ssh-rsa".getBytes(Charsets.UTF_8), + rpk.getPublicExponent().toByteArray(), + rpk.getModulus().toByteArray() ); - } - - private static class KeySpecHelper { - private final KeySpec keySpec; - private final boolean isPrivate; - private final EncryptionType encryptionType; - - public KeySpecHelper(KeySpec keySpec, boolean isPrivate, EncryptionType encryptionType) { - super(); - this.keySpec = keySpec; - this.isPrivate = isPrivate; - this.encryptionType = encryptionType; } - public synchronized final KeySpec getKeySpec() { - return keySpec; - } + private static class KeySpecHelper { + private final KeySpec keySpec; + private final boolean isPrivate; + private final EncryptionType encryptionType; - public synchronized final boolean isPrivate() { - return isPrivate; - } + public KeySpecHelper(KeySpec keySpec, boolean isPrivate, EncryptionType encryptionType) { + super(); + this.keySpec = keySpec; + this.isPrivate = isPrivate; + this.encryptionType = encryptionType; + } - public synchronized final EncryptionType getEncryptionType() { - return encryptionType; + public synchronized final KeySpec getKeySpec() { + return keySpec; + } + + public synchronized final boolean isPrivate() { + return isPrivate; + } + + public synchronized final EncryptionType getEncryptionType() { + return encryptionType; + } } - } } diff --git a/src/main/java/net/locusworks/common/crypto/RSA.java b/src/main/java/net/locusworks/common/crypto/RSA.java index 3ba85aa..8ff6ee6 100644 --- a/src/main/java/net/locusworks/common/crypto/RSA.java +++ b/src/main/java/net/locusworks/common/crypto/RSA.java @@ -21,136 +21,136 @@ import net.locusworks.common.crypto.KeyFile.EncryptionType; import net.locusworks.common.io.IOUtils; public class RSA { - - private static final String ENCRYPTION_TYPE = "RSA"; - private static final String ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS10PADDING"; - private static final String PROVIDER = "SunJCE"; - private static final String RANDOM_ALGORITHM = "SHA1PRNG"; - - private static final int PADDING_LENGTH = 11; - private static final int DEFAULT_KEY_LENGTH = 2048; - - public static KeyPair generateKeyPair() { - return generateKeyPair(DEFAULT_KEY_LENGTH); - } - - public static KeyPair generateKeyPair(int keyLength) { - try { - KeyPairGenerator kpg = KeyPairGenerator.getInstance(ENCRYPTION_TYPE); - SecureRandom sr = SecureRandom.getInstance(RANDOM_ALGORITHM); - kpg.initialize(keyLength, sr); - return kpg.genKeyPair(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static KeyPair loadPrivateKey(String privateKeyFileName) { - return loadKeyPair(null, privateKeyFileName); - } - - public static KeyPair loadPublicKey(String publicKeyFileName) { - return loadKeyPair(publicKeyFileName, null); - } - - public static KeyPair loadKeyPair(String publicKey, String privateKey) { - KeyFile pubKey = KeyFile.read(publicKey); - KeyFile prvKey = KeyFile.read(privateKey); - return new KeyPair((PublicKey)pubKey.getKey(), (PrivateKey)prvKey.getKey()); - } - - public static boolean generateAndWriteSSHKeys() { - KeyPair kp = generateKeyPair(); - return writePrivateKey(kp) && writePublicKey(kp, true); - } - - public static boolean generateAndWriteKeyPair() { - return generateAndWriteKeyPair(DEFAULT_KEY_LENGTH); - } - - public static boolean generateAndWriteKeyPair(String keyPairName) { - return generateAndWriteKeyPair(keyPairName, DEFAULT_KEY_LENGTH); - } - - public static boolean generateAndWriteKeyPair(int keyLength) { - return generateAndWriteKeyPair("id_rsa", keyLength); - } - - public static boolean generateAndWriteKeyPair(String keyPairName, int keyLength) { - KeyPair kp = generateKeyPair(keyLength); - return writePrivateKey(kp, keyPairName, "PRIVATE KEY") && writePublicKey(kp, keyPairName + ".pub", "PUBLIC KEY"); - } - - public static boolean writePrivateKey(KeyPair kp) { - return writePrivateKey(kp, "id_rsa", "PRIVATE KEY"); - } - - public static boolean writePrivateKey(KeyPair kp, String fileName, String description) { - return writePemFile(kp.getPrivate(), fileName, description); - } - - public static boolean writePublicKey(KeyPair kp) { - return writePublicKey(kp, false); - } - - public static boolean writePublicKey(KeyPair kp, boolean sshFormat) { - return writePublicKey(kp, "id_rsa.pub", "PUBLIC KEY", sshFormat); - } - - public static boolean writePublicKey(KeyPair kp, String fileName, String description) { - return writePemFile(kp.getPublic(), fileName, description, false); - } - - public static boolean writePublicKey(KeyPair kp, String fileName, String description, boolean sshFormat) { - return writePemFile(kp.getPublic(), fileName, description, sshFormat); - } - - public static int calculateRequiredKeyLength(String message) { - return (message.getBytes(Charsets.UTF_8).length + PADDING_LENGTH) * 8; - } - - public static String encrypt(Key key, String message) { - try { - calculateKeyLength(key, message); - Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER); - cipher.init(Cipher.ENCRYPT_MODE, key); - CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(message.getBytes(Charsets.UTF_8)), cipher); - byte[] encrypted = IOUtils.toByteArray(cis); - return Base64.getEncoder().encodeToString(encrypted); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static String decrypt(Key key, String message) { - try { - Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER); - cipher.init(Cipher.DECRYPT_MODE, key); - byte[] decoded = Base64.getDecoder().decode(message); - byte[] plainTextArray = cipher.doFinal(decoded); - return new String(plainTextArray, Charsets.UTF_8); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - private static boolean writePemFile(Key key, String fileName, String description) { - return writePemFile(key, fileName, description, false); - } - - private static boolean writePemFile(Key key, String fileName, String description, boolean sshFormat) { - try(KeyFile kf = sshFormat ? new KeyFile(key, description, EncryptionType.SSH) : new KeyFile(key, description)) { - kf.write(fileName); + private static final String ENCRYPTION_TYPE = "RSA"; + private static final String ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS1PADDING"; + private static final String PROVIDER = "SunJCE"; + private static final String RANDOM_ALGORITHM = "SHA1PRNG"; + + private static final int PADDING_LENGTH = 11; + private static final int DEFAULT_KEY_LENGTH = 2048; + + public static KeyPair generateKeyPair() { + return generateKeyPair(DEFAULT_KEY_LENGTH); } - return Files.exists(Paths.get(fileName)); - } - - private static void calculateKeyLength(Key key, String message) throws IllegalBlockSizeException { - int keyLength = ((RSAKey)key).getModulus().bitLength(); - int requiredKeyLength = calculateRequiredKeyLength(message); - if (keyLength < requiredKeyLength) { - throw new IllegalBlockSizeException(String.format("RSA key size of %d is not large enough to encrypt message of length %d. " - + "Increase key size to a minimum of %d and re-encrypt with new key", keyLength, message.length(), requiredKeyLength)); + + public static KeyPair generateKeyPair(int keyLength) { + try { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(ENCRYPTION_TYPE); + SecureRandom sr = SecureRandom.getInstance(RANDOM_ALGORITHM); + kpg.initialize(keyLength, sr); + return kpg.genKeyPair(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static KeyPair loadPrivateKey(String privateKeyFileName) { + return loadKeyPair(null, privateKeyFileName); + } + + public static KeyPair loadPublicKey(String publicKeyFileName) { + return loadKeyPair(publicKeyFileName, null); + } + + public static KeyPair loadKeyPair(String publicKey, String privateKey) { + KeyFile pubKey = KeyFile.read(publicKey); + KeyFile prvKey = KeyFile.read(privateKey); + return new KeyPair((PublicKey) pubKey.getKey(), (PrivateKey) prvKey.getKey()); + } + + public static boolean generateAndWriteSSHKeys() { + KeyPair kp = generateKeyPair(); + return writePrivateKey(kp) && writePublicKey(kp, true); + } + + public static boolean generateAndWriteKeyPair() { + return generateAndWriteKeyPair(DEFAULT_KEY_LENGTH); + } + + public static boolean generateAndWriteKeyPair(String keyPairName) { + return generateAndWriteKeyPair(keyPairName, DEFAULT_KEY_LENGTH); + } + + public static boolean generateAndWriteKeyPair(int keyLength) { + return generateAndWriteKeyPair("id_rsa", keyLength); + } + + public static boolean generateAndWriteKeyPair(String keyPairName, int keyLength) { + KeyPair kp = generateKeyPair(keyLength); + return writePrivateKey(kp, keyPairName, "PRIVATE KEY") && writePublicKey(kp, keyPairName + ".pub", "PUBLIC KEY"); + } + + public static boolean writePrivateKey(KeyPair kp) { + return writePrivateKey(kp, "id_rsa", "PRIVATE KEY"); + } + + public static boolean writePrivateKey(KeyPair kp, String fileName, String description) { + return writePemFile(kp.getPrivate(), fileName, description); + } + + public static boolean writePublicKey(KeyPair kp) { + return writePublicKey(kp, false); + } + + public static boolean writePublicKey(KeyPair kp, boolean sshFormat) { + return writePublicKey(kp, "id_rsa.pub", "PUBLIC KEY", sshFormat); + } + + public static boolean writePublicKey(KeyPair kp, String fileName, String description) { + return writePemFile(kp.getPublic(), fileName, description, false); + } + + public static boolean writePublicKey(KeyPair kp, String fileName, String description, boolean sshFormat) { + return writePemFile(kp.getPublic(), fileName, description, sshFormat); + } + + public static int calculateRequiredKeyLength(String message) { + return (message.getBytes(Charsets.UTF_8).length + PADDING_LENGTH) * 8; + } + + public static String encrypt(Key key, String message) { + try { + calculateKeyLength(key, message); + Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key); + CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(message.getBytes(Charsets.UTF_8)), cipher); + byte[] encrypted = IOUtils.toByteArray(cis); + return Base64.getEncoder().encodeToString(encrypted); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static String decrypt(Key key, String message) { + try { + Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] decoded = Base64.getDecoder().decode(message); + byte[] plainTextArray = cipher.doFinal(decoded); + return new String(plainTextArray, Charsets.UTF_8); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private static boolean writePemFile(Key key, String fileName, String description) { + return writePemFile(key, fileName, description, false); + } + + private static boolean writePemFile(Key key, String fileName, String description, boolean sshFormat) { + try (KeyFile kf = sshFormat ? new KeyFile(key, description, EncryptionType.SSH) : new KeyFile(key, description)) { + kf.write(fileName); + } + return Files.exists(Paths.get(fileName)); + } + + private static void calculateKeyLength(Key key, String message) throws IllegalBlockSizeException { + int keyLength = ((RSAKey) key).getModulus().bitLength(); + int requiredKeyLength = calculateRequiredKeyLength(message); + if (keyLength < requiredKeyLength) { + throw new IllegalBlockSizeException(String.format("RSA key size of %d is not large enough to encrypt message of length %d. " + + "Increase key size to a minimum of %d and re-encrypt with new key", keyLength, message.length(), requiredKeyLength)); + } } - } } diff --git a/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java b/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java index 94a33a2..8da2a4d 100644 --- a/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java +++ b/src/main/java/net/locusworks/common/crypto/SSHEncodedKeySpec.java @@ -18,47 +18,47 @@ import static net.locusworks.common.utils.Utils.get; import static net.locusworks.common.utils.Utils.size; public class SSHEncodedKeySpec extends EncodedKeySpec { - - private static final String SSH_MARKER = "ssh-rsa"; - public SSHEncodedKeySpec(byte[] encodedKey) { - super(encodedKey); - } - - public RSAPublicKeySpec convertToRSAPubKeySpec() throws InvalidKeySpecException { - try { - byte[] data = this.getEncoded(); - InputStream stream = new ByteArrayInputStream(data); - Iterable parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" ")); - - checkArguments(size(parts) >= 2 && SSH_MARKER.equals(get(parts, 0)), "Bad format, should be: ssh-rsa AAB3..."); - stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1)))); - String marker = new String(readLengthFirst(stream)); - checkArguments(SSH_MARKER.equals(marker), "Looking for marker %s but received %s", SSH_MARKER, marker); - BigInteger publicExponent = new BigInteger(readLengthFirst(stream)); - BigInteger modulus = new BigInteger(readLengthFirst(stream)); - return new RSAPublicKeySpec(modulus, publicExponent); - } catch (Exception ex) { - throw new InvalidKeySpecException(ex); - } - } + private static final String SSH_MARKER = "ssh-rsa"; - @Override - public String getFormat() { - return null; - } - - private static byte[] readLengthFirst(InputStream in) throws IOException { - int[] bytes = new int[] {in.read(), in.read(), in.read(), in.read()}; - int length = 0; - int shift = 24; - for (int i = 0; i < bytes.length; i++) { - length += bytes[i] << shift; - shift -= 8; + public SSHEncodedKeySpec(byte[] encodedKey) { + super(encodedKey); + } + + public RSAPublicKeySpec convertToRSAPubKeySpec() throws InvalidKeySpecException { + try { + byte[] data = this.getEncoded(); + InputStream stream = new ByteArrayInputStream(data); + Iterable parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" ")); + + checkArguments(size(parts) >= 2 && SSH_MARKER.equals(get(parts, 0)), "Bad format, should be: ssh-rsa AAB3..."); + stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1)))); + String marker = new String(readLengthFirst(stream)); + checkArguments(SSH_MARKER.equals(marker), "Looking for marker %s but received %s", SSH_MARKER, marker); + BigInteger publicExponent = new BigInteger(readLengthFirst(stream)); + BigInteger modulus = new BigInteger(readLengthFirst(stream)); + return new RSAPublicKeySpec(modulus, publicExponent); + } catch (Exception ex) { + throw new InvalidKeySpecException(ex); + } + } + + @Override + public String getFormat() { + return null; + } + + private static byte[] readLengthFirst(InputStream in) throws IOException { + int[] bytes = new int[]{in.read(), in.read(), in.read(), in.read()}; + int length = 0; + int shift = 24; + for (int aByte : bytes) { + length += aByte << shift; + shift -= 8; + } + byte[] val = new byte[length]; + in.read(val); + return val; } - byte[] val = new byte[length]; - in.read(val); - return val; - } } diff --git a/src/main/java/net/locusworks/common/exceptions/ApplicationException.java b/src/main/java/net/locusworks/common/exceptions/ApplicationException.java index 40cb230..9b0f813 100644 --- a/src/main/java/net/locusworks/common/exceptions/ApplicationException.java +++ b/src/main/java/net/locusworks/common/exceptions/ApplicationException.java @@ -8,103 +8,104 @@ import java.io.Serial; * */ public class ApplicationException extends Exception { - private final Integer code; - boolean success = false; - @Serial private static final long serialVersionUID = 1L; - - public static ApplicationException egregiousServer() { - return new ApplicationException(9001, "Something went wrong. Please see logs for details"); - } + private final Integer code; + boolean success = false; + @Serial + private static final long serialVersionUID = 1L; - public static ApplicationException invalidCreds() { - return new ApplicationException(9001, "Invalid credentials provided"); - } + public static ApplicationException egregiousServer() { + return new ApplicationException(9001, "Something went wrong. Please see logs for details"); + } - public static ApplicationException notLoggedIn() { - return new ApplicationException(9002, "Not logged in"); - } + public static ApplicationException invalidCreds() { + return new ApplicationException(9001, "Invalid credentials provided"); + } - public static ApplicationException invalidEmailAddress() { - return new ApplicationException(9005, "Invalid email address"); - } + public static ApplicationException notLoggedIn() { + return new ApplicationException(9002, "Not logged in"); + } - public static ApplicationException actionNotPermitted() { - return new ApplicationException(9007, "Action not permitted"); - } - - public static ApplicationException actionNotPermitted(String message) { - return new ApplicationException(9007, "Action not permitted: " + message); - } + public static ApplicationException invalidEmailAddress() { + return new ApplicationException(9005, "Invalid email address"); + } - public static ApplicationException passwordsNotEqual() { - return new ApplicationException(9008, "Passwords do not match"); - } - - public static ApplicationException unAuthorized() { - return new ApplicationException(9009, "unauthorized"); - } - - public static ApplicationException duplicateEntry(String message) { - return new ApplicationException(9100, message); - } - - public static ApplicationException duplicateEntry(String messageFmt, Object... args) { - return new ApplicationException(9100, String.format(messageFmt, args)); - } - - public static ApplicationException noEntryExists(String message) { - return new ApplicationException(9101, message); - } - - public static ApplicationException noEntryExists(String messageFmt, Object... items) { - return new ApplicationException(9101, String.format(messageFmt, items)); - } - - public static ApplicationException constraintViolation(String message) { - return new ApplicationException(9102, message); - } - - public static ApplicationException constraintViolation(String messageFmt, Object... items) { - return new ApplicationException(9102, String.format(messageFmt, items)); - } - - public static ApplicationException illegalArgument(String message) { - return new ApplicationException(9103, message); - } - - public static ApplicationException generic(String message) { - return new ApplicationException(9999, message); - } - - public static ApplicationException fromException(Throwable e) { - return new ApplicationException(9999, e); - } - - public ApplicationException(int code, Throwable e) { - this(code, e.getMessage(), e); - } + public static ApplicationException actionNotPermitted() { + return new ApplicationException(9007, "Action not permitted"); + } - public ApplicationException(int code, String message) { - super(message); - this.code = code; - } - - public ApplicationException(int code, String message, Throwable e) { - super(message, e); - this.code = code; - } + public static ApplicationException actionNotPermitted(String message) { + return new ApplicationException(9007, "Action not permitted: " + message); + } - public boolean getSuccess() { - return success; - } + public static ApplicationException passwordsNotEqual() { + return new ApplicationException(9008, "Passwords do not match"); + } - public Integer getCode() { - return code; - } + public static ApplicationException unAuthorized() { + return new ApplicationException(9009, "unauthorized"); + } - @Override - public String getMessage() { - return super.getMessage(); - } + public static ApplicationException duplicateEntry(String message) { + return new ApplicationException(9100, message); + } + + public static ApplicationException duplicateEntry(String messageFmt, Object... args) { + return new ApplicationException(9100, String.format(messageFmt, args)); + } + + public static ApplicationException noEntryExists(String message) { + return new ApplicationException(9101, message); + } + + public static ApplicationException noEntryExists(String messageFmt, Object... items) { + return new ApplicationException(9101, String.format(messageFmt, items)); + } + + public static ApplicationException constraintViolation(String message) { + return new ApplicationException(9102, message); + } + + public static ApplicationException constraintViolation(String messageFmt, Object... items) { + return new ApplicationException(9102, String.format(messageFmt, items)); + } + + public static ApplicationException illegalArgument(String message) { + return new ApplicationException(9103, message); + } + + public static ApplicationException generic(String message) { + return new ApplicationException(9999, message); + } + + public static ApplicationException fromException(Throwable e) { + return new ApplicationException(9999, e); + } + + public ApplicationException(int code, Throwable e) { + this(code, e.getMessage(), e); + } + + public ApplicationException(int code, String message) { + super(message); + this.code = code; + } + + public ApplicationException(int code, String message, Throwable e) { + super(message, e); + this.code = code; + } + + public boolean getSuccess() { + return success; + } + + public Integer getCode() { + return code; + } + + @Override + public String getMessage() { + return super.getMessage(); + } } diff --git a/src/main/java/net/locusworks/common/immutables/Pair.java b/src/main/java/net/locusworks/common/immutables/Pair.java index 36149c3..33b62cc 100644 --- a/src/main/java/net/locusworks/common/immutables/Pair.java +++ b/src/main/java/net/locusworks/common/immutables/Pair.java @@ -1,55 +1,64 @@ package net.locusworks.common.immutables; +import java.util.Objects; + /** * Class that holds two immutable objects as a pair - * @author Isaac Parenteau - * @version 1.0.0 + * * @param class type of object 1 * @param class type of object 2 + * @author Isaac Parenteau + * @version 1.0.0 */ public class Pair extends Unit { - - private V2 value2; - - /** - * Constructor with no values - */ - public Pair() { - super(); - } - - /** - * Constructor - * @param value1 Object 1 - * @param value2 Object 2 - */ - public Pair(V1 value1, V2 value2) { - super(value1); - this.value2 = value2; - } - - /** - * Set value2 - * @param value2 value to set it - */ - public void setValue2(V2 value2) { - this.value2 = value2; - } - - /** - * Get value2 - * @return value2 - */ - public V2 getValue2() { - return this.value2; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Pair)) return false; - - Pair otherPair = (Pair)other; - - return super.equals(otherPair) && this.getValue2().equals(otherPair.getValue2()); - } -} \ No newline at end of file + + private V2 value2; + + /** + * Constructor with no values + */ + public Pair() { + super(); + } + + /** + * Constructor + * + * @param value1 Object 1 + * @param value2 Object 2 + */ + public Pair(V1 value1, V2 value2) { + super(value1); + this.value2 = value2; + } + + /** + * Set value2 + * + * @param value2 value to set it + */ + public void setValue2(V2 value2) { + if (Objects.nonNull(this.value2)) { + throw new IllegalArgumentException("Value2 already set. Cannot change"); + } + this.value2 = value2; + } + + /** + * Get value2 + * + * @return value2 + */ + public V2 getValue2() { + return this.value2; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof Pair pair)) return false; + + return this.getValue2().equals(pair.getValue2()) && + this.getValue1().equals(pair.getValue1()); + } +} diff --git a/src/main/java/net/locusworks/common/immutables/Triplet.java b/src/main/java/net/locusworks/common/immutables/Triplet.java index 065b7e2..3abd5df 100644 --- a/src/main/java/net/locusworks/common/immutables/Triplet.java +++ b/src/main/java/net/locusworks/common/immutables/Triplet.java @@ -1,55 +1,67 @@ package net.locusworks.common.immutables; +import java.util.Objects; + /** * Class that holds three immutable objects as triplets - * @author Isaac Parenteau - * @version 1.0.0 + * * @param class type of object 1 * @param class type of object 2 * @param class type of object 3 + * @author Isaac Parenteau + * @version 1.0.0 */ public class Triplet extends Pair { - - private V3 value3; - - /** - * default constructor with no values - */ - public Triplet() { - super(); - } - - /** - * Constructor - * @param value1 Object 1 - * @param value2 Object 2 - * @param value3 Object 3 - */ - public Triplet(V1 value1, V2 value2, V3 value3) { - super(value1, value2); - this.value3 = value3; - } - - /** - * Set value 3 - * @param value3 value 3 - */ - public void setValue3(V3 value3) { - this.value3 = value3; - } - - /** - * Get value 3 - * @return value 3 - */ - public V3 getValue3() { - return value3; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Triplet otherTriplet)) return false; - return super.equals(otherTriplet) && this.getValue3().equals(otherTriplet.getValue3()); - } + private V3 value3; + + /** + * default constructor with no values + */ + public Triplet() { + super(); + } + + /** + * Constructor + * + * @param value1 Object 1 + * @param value2 Object 2 + * @param value3 Object 3 + */ + public Triplet(V1 value1, V2 value2, V3 value3) { + super(value1, value2); + this.value3 = value3; + } + + /** + * Set value 3 + * + * @param value3 value 3 + */ + public void setValue3(V3 value3) { + if (Objects.nonNull(this.value3)) { + throw new IllegalArgumentException("Value3 already set. Cannot change"); + } + this.value3 = value3; + } + + /** + * Get value 3 + * + * @return value 3 + */ + public V3 getValue3() { + return value3; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof Triplet triplet)) return false; + + return this.getValue3().equals(triplet.getValue3()) && + this.getValue2().equals(triplet.getValue2()) && + this.getValue1().equals(triplet.getValue1()); + } } diff --git a/src/main/java/net/locusworks/common/immutables/Unit.java b/src/main/java/net/locusworks/common/immutables/Unit.java index b0a0971..90d667b 100644 --- a/src/main/java/net/locusworks/common/immutables/Unit.java +++ b/src/main/java/net/locusworks/common/immutables/Unit.java @@ -1,50 +1,58 @@ package net.locusworks.common.immutables; +import java.util.Objects; + /** * Class that holds three immutable objects as triplets - * @author Isaac Parenteau - * @version 1.0.0 + * * @param class type of object 1 + * @author Isaac Parenteau + * @version 1.0.0 */ public class Unit { - - private V1 value1; - - /** - * Default constructor with no values - */ - public Unit() {} - - /** - * Constuctor - * @param value1 value 1 - */ - public Unit(V1 value1) { - this.value1 = value1; - } - - /** - * Set value 1 - * @param value1 value 1 - */ - public void setValue1(V1 value1) { - this.value1 = value1; - } - - /** - * Get value 1 - * @return value1 - */ - public V1 getValue1() { - return value1; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Unit)) return false; - - Unit otherUnit = (Unit)other; - - return this.getValue1().equals(otherUnit.getValue1()); - } -} \ No newline at end of file + + private V1 value1; + + /** + * Default constructor with no values + */ + public Unit() { + } + + /** + * Constructor + * + * @param value1 value 1 + */ + public Unit(V1 value1) { + this.value1 = value1; + } + + /** + * Set value 1 + * + * @param value1 value 1 + */ + public void setValue1(V1 value1) { + if (Objects.nonNull(this.value1)) { + throw new IllegalArgumentException("Value1 already set. Cannot change"); + } + this.value1 = value1; + } + + /** + * Get value 1 + * + * @return value1 + */ + public V1 getValue1() { + return value1; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof Unit)) return false; + return this.getValue1().equals(((Unit) other).getValue1()); + } +} diff --git a/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java b/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java index bc02ab1..af6d1d5 100644 --- a/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java +++ b/src/main/java/net/locusworks/common/interfaces/AutoCloseableIterator.java @@ -3,8 +3,8 @@ package net.locusworks.common.interfaces; import java.util.Iterator; public interface AutoCloseableIterator extends Iterator, AutoCloseable { - - @Override - void close(); + + @Override + void close(); } diff --git a/src/main/java/net/locusworks/common/interfaces/ThrowingConsumer.java b/src/main/java/net/locusworks/common/interfaces/ThrowingConsumer.java index cde492e..85964fa 100644 --- a/src/main/java/net/locusworks/common/interfaces/ThrowingConsumer.java +++ b/src/main/java/net/locusworks/common/interfaces/ThrowingConsumer.java @@ -2,5 +2,5 @@ package net.locusworks.common.interfaces; @FunctionalInterface public interface ThrowingConsumer { - void accept(T t) throws E; + void accept(T t) throws E; } diff --git a/src/main/java/net/locusworks/common/io/IOUtils.java b/src/main/java/net/locusworks/common/io/IOUtils.java index 71c3bf4..bbebcf4 100644 --- a/src/main/java/net/locusworks/common/io/IOUtils.java +++ b/src/main/java/net/locusworks/common/io/IOUtils.java @@ -38,321 +38,322 @@ import net.locusworks.common.Charsets; public class IOUtils { - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; - public static final int EOF = -1; + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + public static final int EOF = -1; - /** - * Gets the contents of an InputStream as a list of Strings, - * one entry per line, using the specified character encoding. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from, not null - * @param encoding the encoding to use, null means platform default - * @return the list of Strings, never null - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static List readLines(final InputStream stream, final Charset charset) throws IOException { - final InputStreamReader reader = new InputStreamReader(stream, charset); - return readLines(reader); - } - - /** - * Gets the contents of a Reader as a list of Strings, - * one entry per line. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from, not null - * @return the list of Strings, never null - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static List readLines(final Reader input) throws IOException { - final BufferedReader reader = toBufferedReader(input); - final List list = new ArrayList<>(); - for(String line = reader.readLine(); line != null; line = reader.readLine()) { - list.add(line); + /** + * Gets the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param stream the InputStream to read from, not null + * @param charset the encoding to use, null means platform default + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static List readLines(final InputStream stream, final Charset charset) throws IOException { + final InputStreamReader reader = new InputStreamReader(stream, charset); + return readLines(reader); } - return list; - } - /** - * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a BufferedReader from the given - * reader. - * - * @param reader the reader to wrap or return (not null) - * @return the given reader or a new {@link BufferedReader} for the given reader - * @throws NullPointerException if the input parameter is null - * @see #buffer(Reader) - */ - public static BufferedReader toBufferedReader(final Reader reader) { - return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); - } - - /** - * Gets the contents of an InputStream as a byte[]. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static byte[] toByteArray(final InputStream input) throws IOException { - try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) { - copy(input, output); - return output.toByteArray(); + /** + * Gets the contents of a Reader as a list of Strings, + * one entry per line. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from, not null + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static List readLines(final Reader input) throws IOException { + final BufferedReader reader = toBufferedReader(input); + final List list = new ArrayList<>(); + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + list.add(line); + } + return list; } - } - /** - * Copies bytes from an InputStream to an - * OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * Large streams (over 2GB) will return a bytes copied value of - * -1 after the copy has completed since the correct - * number of bytes cannot be returned as an int. For large streams - * use the copyLarge(InputStream, OutputStream) method. - * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static int copy(final InputStream input, final OutputStream output) throws IOException { - final long count = copyLarge(input, output); - if (count > Integer.MAX_VALUE) { - return -1; + /** + * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a BufferedReader from the given + * reader. + * + * @param reader the reader to wrap or return (not null) + * @return the given reader or a new {@link BufferedReader} for the given reader + * @throws NullPointerException if the input parameter is null + * @see #buffer(Reader) + */ + public static BufferedReader toBufferedReader(final Reader reader) { + return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); } - return (int) count; - } - /** - * Copies bytes from an InputStream to an OutputStream using an internal buffer of the - * given size. - *

- * This method buffers the input internally, so there is no need to use a BufferedInputStream. - *

- * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @param bufferSize the bufferSize used to copy from the input to the output - * @return the number of bytes copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static long copy(final InputStream input, final OutputStream output, final int bufferSize) - throws IOException { - return copyLarge(input, output, new byte[bufferSize]); - } - /** - * Copies bytes from an InputStream to chars on a - * Writer using the specified character encoding. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * This method uses {@link InputStreamReader}. - * - * @param input the InputStream to read from - * @param output the Writer to write to - * @param inputEncoding the encoding to use for the input stream, null means platform default - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static void copy(final InputStream input, final Writer output, final Charset inputEncoding) - throws IOException { - final InputStreamReader in = new InputStreamReader(input, inputEncoding); - copy(in, output); - } - - /** - * Copies chars from a Reader to a Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Large streams (over 2GB) will return a chars copied value of - * -1 after the copy has completed since the correct - * number of chars cannot be returned as an int. For large streams - * use the copyLarge(Reader, Writer) method. - * - * @param input the Reader to read from - * @param output the Writer to write to - * @return the number of characters copied, or -1 if > Integer.MAX_VALUE - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static int copy(final Reader input, final Writer output) throws IOException { - final long count = copyLarge(input, output); - if (count > Integer.MAX_VALUE) { - return -1; + /** + * Gets the contents of an InputStream as a byte[]. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(final InputStream input) throws IOException { + try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) { + copy(input, output); + return output.toByteArray(); + } } - return (int) count; - } - /** - * Copies chars from a large (over 2GB) Reader to a Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. - * - * @param input the Reader to read from - * @param output the Writer to write to - * @return the number of characters copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static long copyLarge(final Reader input, final Writer output) throws IOException { - return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); - } - - /** - * Copies bytes from a large (over 2GB) InputStream to an - * OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. - * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @return the number of bytes copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static long copyLarge(final InputStream input, final OutputStream output) - throws IOException { - return copy(input, output, DEFAULT_BUFFER_SIZE); - } - - /** - * Copies bytes from a large (over 2GB) InputStream to an - * OutputStream. - *

- * This method uses the provided buffer, so there is no need to use a - * BufferedInputStream. - *

- * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @param buffer the buffer to use for the copy - * @return the number of bytes copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer) - throws IOException { - long count = 0; - int n; - while (EOF != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; + /** + * Copies bytes from an InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Large streams (over 2GB) will return a bytes copied value of + * -1 after the copy has completed since the correct + * number of bytes cannot be returned as an int. For large streams + * use the copyLarge(InputStream, OutputStream) method. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static int copy(final InputStream input, final OutputStream output) throws IOException { + final long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; } - return count; - } - /** - * Copies chars from a large (over 2GB) Reader to a Writer. - *

- * This method uses the provided buffer, so there is no need to use a - * BufferedReader. - *

- * - * @param input the Reader to read from - * @param output the Writer to write to - * @param buffer the buffer to be used for the copy - * @return the number of characters copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - */ - public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException { - long count = 0; - int n; - while (EOF != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; + /** + * Copies bytes from an InputStream to an OutputStream using an internal buffer of the + * given size. + *

+ * This method buffers the input internally, so there is no need to use a BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param bufferSize the bufferSize used to copy from the input to the output + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static long copy(final InputStream input, final OutputStream output, final int bufferSize) + throws IOException { + return copyLarge(input, output, new byte[bufferSize]); } - return count; - } - /** - * Gets the contents of an InputStream as a String - * using the specified character encoding. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * - * @param input the InputStream to read from - * @param encoding the encoding to use, null means platform default - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static String toString(final InputStream input, final Charset encoding) throws IOException { - try (final StringWriter sw = new StringWriter()) { - copy(input, sw, encoding); - return sw.toString(); + /** + * Copies bytes from an InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @param inputEncoding the encoding to use for the input stream, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static void copy(final InputStream input, final Writer output, final Charset inputEncoding) + throws IOException { + final InputStreamReader in = new InputStreamReader(input, inputEncoding); + copy(in, output); } - } - public static void writeStringToFile(String fileName, String data) throws IOException { - writeStringToFile(Paths.get(fileName), data, Charsets.UTF_8); - } - - - public static void writeStringToFile(String fileName, String data, Charset charset) throws IOException { - writeStringToFile(Paths.get(fileName), data, charset); - } - - public static void writeStringToFile(Path file, String data) throws IOException { - writeStringToFile(file, data, Charsets.UTF_8); - } - - @Deprecated - public static void writeStringToFile(File file, String data, Charset charset) throws IOException { - writeStringToFile(file.toPath(), data, charset); - } - - public static void writeStringToFile(Path file, String data, Charset charset) throws IOException { - try(Writer writer = Files.newBufferedWriter(file, charset)) { - writer.write(data); - writer.flush(); + /** + * Copies chars from a Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Large streams (over 2GB) will return a chars copied value of + * -1 after the copy has completed since the correct + * number of chars cannot be returned as an int. For large streams + * use the copyLarge(Reader, Writer) method. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static int copy(final Reader input, final Writer output) throws IOException { + final long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; } - } - public static void deleteFile(String fileName) { - deleteFile(Paths.get(fileName)); - } - - @Deprecated - public static void deleteFile(File file) { - deleteFile(file.toPath()); - } - - public static void deleteFile(Path file) { - try { - Files.deleteIfExists(file); - } catch (IOException ex) { - throw new IllegalArgumentException(ex); + /** + * Copies chars from a large (over 2GB) Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static long copyLarge(final Reader input, final Writer output) throws IOException { + return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); } - } - public static void deleteFiles(String... fileNames) { - Arrays.asList(fileNames).forEach(file -> deleteFile(file)); - } + /** + * Copies bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static long copyLarge(final InputStream input, final OutputStream output) + throws IOException { + return copy(input, output, DEFAULT_BUFFER_SIZE); + } + + /** + * Copies bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param buffer the buffer to use for the copy + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer) + throws IOException { + long count = 0; + int n; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copies chars from a large (over 2GB) Reader to a Writer. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * + * @param input the Reader to read from + * @param output the Writer to write to + * @param buffer the buffer to be used for the copy + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + */ + public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException { + long count = 0; + int n; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Gets the contents of an InputStream as a String + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(final InputStream input, final Charset encoding) throws IOException { + try (final StringWriter sw = new StringWriter()) { + copy(input, sw, encoding); + return sw.toString(); + } + } + + public static void writeStringToFile(String fileName, String data) throws IOException { + writeStringToFile(Paths.get(fileName), data, Charsets.UTF_8); + } + + + public static void writeStringToFile(String fileName, String data, Charset charset) throws IOException { + writeStringToFile(Paths.get(fileName), data, charset); + } + + public static void writeStringToFile(Path file, String data) throws IOException { + writeStringToFile(file, data, Charsets.UTF_8); + } + + @Deprecated + public static void writeStringToFile(File file, String data, Charset charset) throws IOException { + writeStringToFile(file.toPath(), data, charset); + } + + public static void writeStringToFile(Path file, String data, Charset charset) throws IOException { + try (Writer writer = Files.newBufferedWriter(file, charset)) { + writer.write(data); + writer.flush(); + } + } + + public static void deleteFile(String fileName) { + deleteFile(Paths.get(fileName)); + } + + @Deprecated + public static void deleteFile(File file) { + deleteFile(file.toPath()); + } + + public static void deleteFile(Path file) { + try { + Files.deleteIfExists(file); + } catch (IOException ex) { + throw new IllegalArgumentException(ex); + } + } + + public static void deleteFiles(String... fileNames) { + Arrays.asList(fileNames).forEach(file -> deleteFile(file)); + } } diff --git a/src/main/java/net/locusworks/common/migration/BaseMigrationManager.java b/src/main/java/net/locusworks/common/migration/BaseMigrationManager.java index 8f96110..c9c5b2f 100644 --- a/src/main/java/net/locusworks/common/migration/BaseMigrationManager.java +++ b/src/main/java/net/locusworks/common/migration/BaseMigrationManager.java @@ -4,9 +4,9 @@ import java.util.ArrayList; import java.util.List; public abstract class BaseMigrationManager { - - protected List migrations = new ArrayList<>(); - - public abstract void migrate() throws Exception; + + protected List migrations = new ArrayList<>(); + + public abstract void migrate() throws Exception; } diff --git a/src/main/java/net/locusworks/common/migration/MigrationCallback.java b/src/main/java/net/locusworks/common/migration/MigrationCallback.java index fc47e2f..0d7f157 100644 --- a/src/main/java/net/locusworks/common/migration/MigrationCallback.java +++ b/src/main/java/net/locusworks/common/migration/MigrationCallback.java @@ -2,5 +2,5 @@ package net.locusworks.common.migration; @FunctionalInterface public interface MigrationCallback { - void results(String msg); + void results(String msg); } diff --git a/src/main/java/net/locusworks/common/migration/MigrationItem.java b/src/main/java/net/locusworks/common/migration/MigrationItem.java index ad3280a..ac881b5 100644 --- a/src/main/java/net/locusworks/common/migration/MigrationItem.java +++ b/src/main/java/net/locusworks/common/migration/MigrationItem.java @@ -7,68 +7,68 @@ import org.flywaydb.core.api.MigrationInfo; import org.flywaydb.core.internal.info.MigrationInfoDumper; public class MigrationItem { - - protected Flyway flyway = null; - protected MigrationInfo[] pendingMigrations; - protected MigrationInfo[] allMigrations; - private MigrationCallback callback; - - public MigrationItem(Flyway flyway, MigrationCallback callback) { - this.allMigrations = flyway.info().all(); - this.pendingMigrations = flyway.info().pending(); - this.flyway=flyway; - this.callback = callback; - } - public MigrationItem(Flyway flyway) { - this(flyway, null); - } + protected Flyway flyway = null; + protected MigrationInfo[] pendingMigrations; + protected MigrationInfo[] allMigrations; + private final MigrationCallback callback; - public String[] getSchemas() { - return flyway.getConfiguration().getSchemas(); - } - - public int qtyPending() { - return pendingMigrations.length; - } - - public void repair() { - // no harm in calling migrate even if none pending, log will contain - // assurance that the migrations were verified - try { - String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", ""); - - String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog()); - - callback(status); - - flyway.repair(); - } catch (Exception e) { - String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage()); - throw new RuntimeException(message, e); + public MigrationItem(Flyway flyway, MigrationCallback callback) { + this.allMigrations = flyway.info().all(); + this.pendingMigrations = flyway.info().pending(); + this.flyway = flyway; + this.callback = callback; } - } - public void migrate() { - // no harm in calling migrate even if none pending, log will contain - // assurance that the migrations were verified - try { - String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", ""); - String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog()); - callback(status); - flyway.migrate(); - } catch (Exception e) { - String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage()); - throw new RuntimeException(message, e); + public MigrationItem(Flyway flyway) { + this(flyway, null); + } + + public String[] getSchemas() { + return flyway.getConfiguration().getSchemas(); + } + + public int qtyPending() { + return pendingMigrations.length; + } + + public void repair() { + // no harm in calling migrate even if none pending, log will contain + // assurance that the migrations were verified + try { + String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", ""); + + String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog()); + + callback(status); + + flyway.repair(); + } catch (Exception e) { + String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage()); + throw new RuntimeException(message, e); + } + } + + public void migrate() { + // no harm in calling migrate even if none pending, log will contain + // assurance that the migrations were verified + try { + String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", ""); + String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog()); + callback(status); + flyway.migrate(); + } catch (Exception e) { + String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage()); + throw new RuntimeException(message, e); + } + } + + public String getAllMigrationsLog() { + return MigrationInfoDumper.dumpToAsciiTable(allMigrations); + } + + private void callback(String msg) { + if (this.callback != null) this.callback.results(msg); } - } - public String getAllMigrationsLog() { - return MigrationInfoDumper.dumpToAsciiTable(allMigrations); - } - - private void callback(String msg) { - if (this.callback != null) this.callback.results(msg); - } - } diff --git a/src/main/java/net/locusworks/common/net/HttpClientHelper.java b/src/main/java/net/locusworks/common/net/HttpClientHelper.java index 56039bf..04922ef 100644 --- a/src/main/java/net/locusworks/common/net/HttpClientHelper.java +++ b/src/main/java/net/locusworks/common/net/HttpClientHelper.java @@ -1,95 +1,79 @@ package net.locusworks.common.net; -import java.security.SecureRandom; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.util.EntityUtils; - -import net.locusworks.common.net.certmanagers.TrustAllCertsManager; -import net.locusworks.common.net.hostverifiers.AllHostValidVerifyer; +import net.locusworks.common.net.ssl.SSLManager; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; public class HttpClientHelper { - public enum HttpSchema { - HTTP, - HTTPS; + public enum HttpSchema { + HTTP, + HTTPS; - public static HttpSchema findEnum(String value) { - for (HttpSchema schema : values()) { - if (value.equalsIgnoreCase(schema.toString())) { - return schema; + public static HttpSchema findEnum(String value) { + for (HttpSchema schema : values()) { + if (value.equalsIgnoreCase(schema.toString())) { + return schema; + } + } + return null; } - } - return null; } - } - - private static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}; - private HttpClient client; - private String baseUrl; - - /** - * Constructor to handle http connection - * @param protocol protocol to use (http or https) - * @param host the host url - * @param port the host port - * @throws Exception exception - */ - public HttpClientHelper(String protocol, String host, String port) throws Exception { - HttpSchema schema = HttpSchema.findEnum(protocol); - if (schema == null) { - throw new Exception("Unable to find http schema of " + protocol); + private static final String[] TLS = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}; + + private final HttpClient client; + private final String baseUrl; + + /** + * Constructor to handle http connection + * + * @param protocol protocol to use (http or https) + * @param host the host url + * @param port the host port + * @throws Exception exception + */ + public HttpClientHelper(String protocol, String host, String port) throws Exception { + HttpSchema schema = HttpSchema.findEnum(protocol); + if (schema == null) { + throw new Exception("Unable to find http schema of " + protocol); + } + this.baseUrl = String.format("%s://%s:%s", schema.toString().toLowerCase(), host, port); + this.client = createClient(schema); } - this.baseUrl = String.format("%s://%s:%s", schema.toString().toLowerCase(), host, port); - this.client = createClient(schema); - } - - private HttpClient createClient(HttpSchema schema) throws Exception { - HttpClientBuilder builder = HttpClientBuilder.create(); - - if (schema == HttpSchema.HTTP) { - return builder.build(); + + private HttpClient createClient(HttpSchema schema) throws Exception { + HttpClientBuilder builder = HttpClientBuilder.create(); + + if (schema == HttpSchema.HTTP) { + return builder.build(); + } + + return SSLManager.getTrustAllTLSClient(); } - - TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllCertsManager() }; - //Setup the ssl instance using tls - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustAllCerts, new SecureRandom()); - - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, TLS, null, new AllHostValidVerifyer()); - - builder = builder.setSSLSocketFactory(sslsf); - - return builder.build(); - } - - /** - * Get the http GET response code - * @param endpoint endpoint to get the response from - * @return responseCode - * @throws Exception general exception - */ - public Integer getGetResponseCode(String endpoint) throws Exception { - String url = this.baseUrl + endpoint; - - HttpResponse response = this.client.execute(new HttpGet(url)); - - HttpEntity entity = response.getEntity(); - - Integer responseCode = response.getStatusLine().getStatusCode(); - - EntityUtils.consume(entity); - - return responseCode; - }; - + + /** + * Get the http GET response code + * + * @param endpoint endpoint to get the response from + * @return responseCode + * @throws Exception general exception + */ + public Integer getResponseCode(String endpoint) throws Exception { + String url = this.baseUrl + endpoint; + + return this.client.execute(new HttpGet(url), response -> { + HttpEntity entity = response.getEntity(); + int responseCode = response.getCode(); + EntityUtils.consume(entity); + return responseCode; + }); + } + + ; + } diff --git a/src/main/java/net/locusworks/common/net/certmanagers/TrustAllCertsManager.java b/src/main/java/net/locusworks/common/net/certmanagers/TrustAllCertsManager.java index d27e2f1..7ef740a 100644 --- a/src/main/java/net/locusworks/common/net/certmanagers/TrustAllCertsManager.java +++ b/src/main/java/net/locusworks/common/net/certmanagers/TrustAllCertsManager.java @@ -8,17 +8,21 @@ import javax.net.ssl.X509TrustManager; public class TrustAllCertsManager implements X509TrustManager { - @Override - public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1) { + } - @Override - public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1) { + } - @Override - public X509Certificate[] getAcceptedIssuers() { return null; } - - public static TrustManager[] trustAllCerts() { - return new TrustManager[] { new TrustAllCertsManager() }; - } + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public static TrustManager[] trustAllCerts() { + return new TrustManager[]{new TrustAllCertsManager()}; + } } diff --git a/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifier.java b/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifier.java new file mode 100644 index 0000000..f098cba --- /dev/null +++ b/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifier.java @@ -0,0 +1,13 @@ +package net.locusworks.common.net.hostverifiers; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +public class AllHostValidVerifier implements HostnameVerifier { + + @Override + public boolean verify(String arg0, SSLSession arg1) { + return true; + } + +} diff --git a/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifyer.java b/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifyer.java deleted file mode 100644 index c385876..0000000 --- a/src/main/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifyer.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.locusworks.common.net.hostverifiers; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; - -public class AllHostValidVerifyer implements HostnameVerifier { - - @Override - public boolean verify(String arg0, SSLSession arg1) { return true; } - -} diff --git a/src/main/java/net/locusworks/common/net/ssl/SSLManager.java b/src/main/java/net/locusworks/common/net/ssl/SSLManager.java index 1309b0b..a2e332b 100644 --- a/src/main/java/net/locusworks/common/net/ssl/SSLManager.java +++ b/src/main/java/net/locusworks/common/net/ssl/SSLManager.java @@ -7,24 +7,35 @@ import java.security.SecureRandom; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.HttpClientBuilder; import net.locusworks.common.net.certmanagers.TrustAllCertsManager; -import net.locusworks.common.net.hostverifiers.AllHostValidVerifyer; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; +import org.apache.hc.client5.http.ssl.TlsSocketStrategy; public class SSLManager { - public static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}; + public static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; public static HttpClient getTrustAllTLSClient() throws NoSuchAlgorithmException, KeyManagementException { - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, new TrustManager[] { new TrustAllCertsManager() }, new SecureRandom()); - - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(context, TLS, null, new AllHostValidVerifyer()); - - return HttpClientBuilder.create().setSSLSocketFactory(sslsf).build(); + TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCertsManager()}; + //Setup the ssl instance using tls + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext); + + HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() + .setTlsSocketStrategy(tlsStrategy) + .build(); + + return HttpClients.custom() + .setConnectionManager(connectionManager) + .build(); } + private SSLManager() { } + } diff --git a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperError.java b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperError.java index 1f90f29..973725e 100644 --- a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperError.java +++ b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperError.java @@ -2,11 +2,12 @@ package net.locusworks.common.objectmapper; /** * Error handler for the object mapper class + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 * */ public interface ObjectMapperError { - void getError(Throwable e); + void getError(Throwable e); } diff --git a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java index 0fa5993..92fe176 100644 --- a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java +++ b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperHelper.java @@ -12,137 +12,146 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; /** - * Object mapper to map to convert objects to json string or + * Object mapper to map to convert objects to json string or * json string back to object + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 */ public class ObjectMapperHelper { - - private static final ObjectMapper mapper; - static { - mapper = new ObjectMapper(); - mapper.setSerializationInclusion(Include.NON_NULL); - mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); - } - - /** - * Write an object out to json - * @param object Object to convert to json - * @return return a string representation of the object converted to json - */ - public static ObjectMapperResults writeValue(Object object) { - String results = ""; - try { - results = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); - } catch (Exception ex) { - try { - GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.setPrettyPrinting(); - Gson gson = gsonBuilder.create(); - results = gson.toJson(object); - } catch (Exception e) { - new ObjectMapperResults<>(e); - } - } - return new ObjectMapperResults<>(results); - } - - /** - * Convert binary data to an object - * @param src Binary data to convert - * @param clazz Class to convert the data to - * @param The expected class of the value - * @return the object populated with the data in the json string - */ - public static ObjectMapperResults readValue(byte[] src, Class clazz) { - try { - return new ObjectMapperResults(mapper.readValue(src, clazz)); - } catch (Exception ex) { - return new ObjectMapperResults(ex); - } - } - - /** - * Convert a json string to an object - * @param src Json String - * @param clazz Class to convert the json string to - * @param The expected class of the value - * @return the object populated with the data in the json string - */ - public static ObjectMapperResults readValue(String src, Class clazz) { - try { - return new ObjectMapperResults(mapper.readValue(src, clazz)); - } catch (Exception ex) { - return new ObjectMapperResults(ex); - } - } - - /** - * Convert an java object to a class - * @param src Object to convert - * @param clazz Class to convert the object to - * @param The expected class of the value - * @return the object populated with the data in the json string - */ - public static ObjectMapperResults readValue(Object src, Class clazz) { - try { - if (src instanceof String) { - return readValue((String)src, clazz); - } - return readValue(mapper.writeValueAsString(src), clazz); - } catch (Exception ex) { - return new ObjectMapperResults(ex); - } - } - - /** - * Converts an object to a list - * @param object Object to convert - * @param objectClass Class to convert the object to - * @param The expected class of the object - * @return the object list populated with the data in the json string - */ - public static > ObjectMapperListResults> readListValue(Object object, Class objectClass) { - return readListValue(object, objectClass, ArrayList.class); - } - - /** - * Converts an object to a list - * @param object Object to convert - * @param objectClass Class to convert the object to - * @param listClass List type to make - * @param The expected class of the object - * @param The expect class of the list - * @return the object list populated with the data in the json string - */ - public static > ObjectMapperListResults> readListValue(Object object, Class objectClass, Class listClass) { - try { - if (object instanceof String) { - return readListValue((String)object, objectClass, listClass); - } - return readListValue(mapper.writeValueAsString(object), objectClass, listClass); - } catch (Exception ex) { - return new ObjectMapperListResults<>(ex); - } - } - - /** - * Converts an object to a list - * @param src Source to convert - * @param objectClass Class to convert the object to - * @param listClass List type to make - * @param The expected class of the object - * @param The expect class of the list - * @return the object list populated with the data in the json string - */ - public static > ObjectMapperListResults> readListValue(String src, Class objectClass, Class listClass) { - try { - List item = mapper.readValue(src, mapper.getTypeFactory().constructCollectionType(listClass, objectClass)); - return new ObjectMapperListResults<>(item); - } catch (Exception ex) { - return new ObjectMapperListResults<>(ex); - } - } + + private static final ObjectMapper mapper; + + static { + mapper = new ObjectMapper(); + mapper.setDefaultPropertyInclusion(Include.NON_NULL); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + } + + /** + * Write an object out to json + * + * @param object Object to convert to json + * @return return a string representation of the object converted to json + */ + public static ObjectMapperResults writeValue(Object object) { + String results = ""; + try { + results = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } catch (Exception ex) { + try { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setPrettyPrinting(); + Gson gson = gsonBuilder.create(); + results = gson.toJson(object); + } catch (Throwable e) { + return new ObjectMapperResults<>(e); + } + } + return new ObjectMapperResults<>(results); + } + + /** + * Convert binary data to an object + * + * @param src Binary data to convert + * @param clazz Class to convert the data to + * @param The expected class of the value + * @return the object populated with the data in the json string + */ + public static ObjectMapperResults readValue(byte[] src, Class clazz) { + try { + return new ObjectMapperResults(mapper.readValue(src, clazz)); + } catch (Exception ex) { + return new ObjectMapperResults(ex); + } + } + + /** + * Convert a json string to an object + * + * @param src Json String + * @param clazz Class to convert the json string to + * @param The expected class of the value + * @return the object populated with the data in the json string + */ + public static ObjectMapperResults readValue(String src, Class clazz) { + try { + return new ObjectMapperResults(mapper.readValue(src, clazz)); + } catch (Exception ex) { + return new ObjectMapperResults(ex); + } + } + + /** + * Convert an java object to a class + * + * @param src Object to convert + * @param clazz Class to convert the object to + * @param The expected class of the value + * @return the object populated with the data in the json string + */ + public static ObjectMapperResults readValue(Object src, Class clazz) { + try { + if (src instanceof String) { + return readValue((String) src, clazz); + } + return readValue(mapper.writeValueAsString(src), clazz); + } catch (Exception ex) { + return new ObjectMapperResults(ex); + } + } + + /** + * Converts an object to a list + * + * @param object Object to convert + * @param objectClass Class to convert the object to + * @param The expected class of the object + * @return the object list populated with the data in the json string + */ + public static > ObjectMapperListResults> readListValue(Object object, Class objectClass) { + return readListValue(object, objectClass, ArrayList.class); + } + + /** + * Converts an object to a list + * + * @param object Object to convert + * @param objectClass Class to convert the object to + * @param listClass List type to make + * @param The expected class of the object + * @param The expect class of the list + * @return the object list populated with the data in the json string + */ + public static > ObjectMapperListResults> readListValue(Object object, Class objectClass, Class listClass) { + try { + if (object instanceof String) { + return readListValue((String) object, objectClass, listClass); + } + return readListValue(mapper.writeValueAsString(object), objectClass, listClass); + } catch (Exception ex) { + return new ObjectMapperListResults<>(ex); + } + } + + /** + * Converts an object to a list + * + * @param src Source to convert + * @param objectClass Class to convert the object to + * @param listClass List type to make + * @param The expected class of the object + * @param The expect class of the list + * @return the object list populated with the data in the json string + */ + public static > ObjectMapperListResults> readListValue(String src, Class objectClass, Class listClass) { + try { + List item = mapper.readValue(src, mapper.getTypeFactory().constructCollectionType(listClass, objectClass)); + return new ObjectMapperListResults<>(item); + } catch (Exception ex) { + return new ObjectMapperListResults<>(ex); + } + } } diff --git a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperListResults.java b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperListResults.java index b1af466..5bbcd8f 100644 --- a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperListResults.java +++ b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperListResults.java @@ -4,48 +4,53 @@ import java.util.Collection; /** * Holds the results from the object mapper list conversion + * + * @param class type of the object mapper * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 - * @param class type of the object mapper */ public class ObjectMapperListResults> extends ObjectMapperResults { - - /** - * Constructor - * @param results results from the conversion - */ - public ObjectMapperListResults(T results) { - this(results, null); - } - - /** - * Constructor - * @param exception exception that was thrown during conversion - */ - public ObjectMapperListResults(Throwable exception) { - this(null, exception); - } - - /** - * Constructor - * @param results results from the conversion - * @param exception exception that was thrown during conversion - */ - public ObjectMapperListResults(T results, Throwable exception) { - super(results, exception); - } - /** - * Add the error handler to the results to retrieve the error that caused - * the exception - * @param error the error handler to use - * @return this - */ - public ObjectMapperListResults withErrorHandler(ObjectMapperError error) { - if (this.hasError() && error != null) { - error.getError(this.getException()); + /** + * Constructor + * + * @param results results from the conversion + */ + public ObjectMapperListResults(T results) { + this(results, null); + } + + /** + * Constructor + * + * @param exception exception that was thrown during conversion + */ + public ObjectMapperListResults(Throwable exception) { + this(null, exception); + } + + /** + * Constructor + * + * @param results results from the conversion + * @param exception exception that was thrown during conversion + */ + public ObjectMapperListResults(T results, Throwable exception) { + super(results, exception); + } + + /** + * Add the error handler to the results to retrieve the error that caused + * the exception + * + * @param error the error handler to use + * @return this + */ + public ObjectMapperListResults withErrorHandler(ObjectMapperError error) { + if (this.hasError() && error != null) { + error.getError(this.getException()); + } + return this; } - return this; - } } \ No newline at end of file diff --git a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperResults.java b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperResults.java index 092be8f..807e5de 100644 --- a/src/main/java/net/locusworks/common/objectmapper/ObjectMapperResults.java +++ b/src/main/java/net/locusworks/common/objectmapper/ObjectMapperResults.java @@ -2,92 +2,102 @@ package net.locusworks.common.objectmapper; /** * Holds the results from the object mapper list conversion + * + * @param class type of the object being converted from json to object * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 - * @param class type of the object being converted from json to object */ public class ObjectMapperResults { - - private Throwable exception; - private T results; - - /** - * Constructor - * @param results results from the conversion - */ - public ObjectMapperResults(T results) { - this(results, null); - } - - /** - * Constructor - * @param exception exception that was thrown during conversion - */ - public ObjectMapperResults(Throwable exception) { - this(null, exception); - } - - /** - * Constructor - * @param results results from the conversion - * @param exception exception that was thrown during conversion - */ - public ObjectMapperResults(T results, Throwable exception) { - this.results = results; - this.exception = exception; - } - /** - * get the exception that happened during conversion - * @return exception - */ - public Throwable getException() { - return exception; - } + private Throwable exception; + private T results; - /** - * Set the exception - * @param exception - */ - public void setException(Throwable exception) { - this.exception = exception; - } - - /** - * Get the result - * @return the converted results - */ - public T getResults() { - return results; - } - - /** - * set the results - * @param results results to set - */ - public void setResults(T results) { - this.results = results; - } - - /** - * Check to see if the conversion caused an error - * @return true if there is an error, false otherwise - */ - public boolean hasError() { - return this.exception != null; - } - - /** - * Add the error handler to the results to retrieve the error that caused - * the exception - * @param error the error handler to use - * @return this - */ - public ObjectMapperResults withErrorHandler(ObjectMapperError error) { - if (this.hasError() && error != null) { - error.getError(this.getException()); + /** + * Constructor + * + * @param results results from the conversion + */ + public ObjectMapperResults(T results) { + this(results, null); + } + + /** + * Constructor + * + * @param exception exception that was thrown during conversion + */ + public ObjectMapperResults(Throwable exception) { + this(null, exception); + } + + /** + * Constructor + * + * @param results results from the conversion + * @param exception exception that was thrown during conversion + */ + public ObjectMapperResults(T results, Throwable exception) { + this.results = results; + this.exception = exception; + } + + /** + * get the exception that happened during conversion + * + * @return exception + */ + public Throwable getException() { + return exception; + } + + /** + * Set the exception + * + * @param exception + */ + public void setException(Throwable exception) { + this.exception = exception; + } + + /** + * Get the result + * + * @return the converted results + */ + public T getResults() { + return results; + } + + /** + * set the results + * + * @param results results to set + */ + public void setResults(T results) { + this.results = results; + } + + /** + * Check to see if the conversion caused an error + * + * @return true if there is an error, false otherwise + */ + public boolean hasError() { + return this.exception != null; + } + + /** + * Add the error handler to the results to retrieve the error that caused + * the exception + * + * @param error the error handler to use + * @return this + */ + public ObjectMapperResults withErrorHandler(ObjectMapperError error) { + if (this.hasError() && error != null) { + error.getError(this.getException()); + } + return this; } - return this; - } } diff --git a/src/main/java/net/locusworks/common/properties/ImmutableProperties.java b/src/main/java/net/locusworks/common/properties/ImmutableProperties.java index 05a1eef..b8fbe2f 100644 --- a/src/main/java/net/locusworks/common/properties/ImmutableProperties.java +++ b/src/main/java/net/locusworks/common/properties/ImmutableProperties.java @@ -5,28 +5,29 @@ import java.util.Properties; public class ImmutableProperties extends Properties { - @Serial private static final long serialVersionUID = 65942088008978137L; - - public ImmutableProperties() { - super(); - } - - public ImmutableProperties(Properties props) { - super(); - if (props == null || props.isEmpty()) return; + @Serial + private static final long serialVersionUID = 65942088008978137L; - this.putAll(props); - } - - @Override - public synchronized Object setProperty(String key, String value) { - return put(key, value); - } - - public synchronized Object put(Object key, Object value) { - if (containsKey(key)) - throw new RuntimeException("Cannot change key value once its set: " + key); - return super.put(key, value); - } + public ImmutableProperties() { + super(); + } + + public ImmutableProperties(Properties props) { + super(); + if (props == null || props.isEmpty()) return; + + this.putAll(props); + } + + @Override + public synchronized Object setProperty(String key, String value) { + return put(key, value); + } + + public synchronized Object put(Object key, Object value) { + if (containsKey(key)) + throw new RuntimeException("Cannot change key value once its set: " + key); + return super.put(key, value); + } } diff --git a/src/main/java/net/locusworks/common/properties/OrderedProperties.java b/src/main/java/net/locusworks/common/properties/OrderedProperties.java index 410d7c9..a8ca4d3 100644 --- a/src/main/java/net/locusworks/common/properties/OrderedProperties.java +++ b/src/main/java/net/locusworks/common/properties/OrderedProperties.java @@ -1,30 +1,7 @@ package net.locusworks.common.properties; -/* - * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Date; import java.util.Enumeration; @@ -59,7 +36,7 @@ import java.util.Set; * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)} * methods load and store properties from and to a character based stream * in a simple line-oriented format specified below. - * + *

* The {@link #load(java.io.InputStream) load(InputStream)} / * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)} * methods work the same way as the load(Reader)/store(Writer, String) pair, except @@ -103,916 +80,898 @@ import java.util.Set; *

This class is thread-safe: multiple threads can share a single * Properties object without the need for external synchronization. * + * @author Arthur van Hoff + * @author Michael McCloskey + * @author Xueming Shen * @see native2ascii tool for Solaris * @see native2ascii tool for Windows - * - * @author Arthur van Hoff - * @author Michael McCloskey - * @author Xueming Shen - * @since JDK1.0 + * @since JDK1.0 */ -public class OrderedProperties extends LinkedHashMap { - /** - * use serialVersionUID from JDK 1.1.X for interoperability - */ - @Serial private static final long serialVersionUID = 4112578634023874840L; +public class OrderedProperties extends LinkedHashMap { + /** + * use serialVersionUID from JDK 1.1.X for interoperability + */ + @Serial + private static final long serialVersionUID = 4112578634023874840L; - /** - * A property list that contains default values for any keys not - * found in this property list. - * - * @serial - */ - protected OrderedProperties defaults; + /** + * A property list that contains default values for any keys not + * found in this property list. + * + * @serial + */ + protected OrderedProperties defaults; - /** - * Creates an empty property list with no default values. - */ - public OrderedProperties() { - this(null); - } - - /** - * Creates an empty property list with the specified defaults. - * - * @param defaults the defaults. - */ - public OrderedProperties(OrderedProperties defaults) { - this.defaults = defaults; - } - - /** - * Calls the Hashtable method {@code put}. Provided for - * parallelism with the getProperty method. Enforces use of - * strings for property keys and values. The value returned is the - * result of the Hashtable call to {@code put}. - * - * @param key the key to be placed into this property list. - * @param value the value corresponding to key. - * @return the previous value of the specified key in this property - * list, or {@code null} if it did not have one. - * @see #getProperty - * @since 1.2 - */ - public synchronized Object setProperty(String key, String value) { - return put(key, value); - } - - - /** - * Reads a property list (key and element pairs) from the input - * character stream in a simple line-oriented format. - *

- * Properties are processed in terms of lines. There are two - * kinds of line, natural lines and logical lines. - * A natural line is defined as a line of - * characters that is terminated either by a set of line terminator - * characters ({@code \n} or {@code \r} or {@code \r\n}) - * or by the end of the stream. A natural line may be either a blank line, - * a comment line, or hold all or some of a key-element pair. A logical - * line holds all the data of a key-element pair, which may be spread - * out across several adjacent natural lines by escaping - * the line terminator sequence with a backslash character - * {@code \}. Note that a comment line cannot be extended - * in this manner; every natural line that is a comment must have - * its own comment indicator, as described below. Lines are read from - * input until the end of the stream is reached. - * - *

- * A natural line that contains only white space characters is - * considered blank and is ignored. A comment line has an ASCII - * {@code '#'} or {@code '!'} as its first non-white - * space character; comment lines are also ignored and do not - * encode key-element information. In addition to line - * terminators, this format considers the characters space - * ({@code ' '}, {@code '\u005Cu0020'}), tab - * ({@code '\t'}, {@code '\u005Cu0009'}), and form feed - * ({@code '\f'}, {@code '\u005Cu000C'}) to be white - * space. - * - *

- * If a logical line is spread across several natural lines, the - * backslash escaping the line terminator sequence, the line - * terminator sequence, and any white space at the start of the - * following line have no affect on the key or element values. - * The remainder of the discussion of key and element parsing - * (when loading) will assume all the characters constituting - * the key and element appear on a single natural line after - * line continuation characters have been removed. Note that - * it is not sufficient to only examine the character - * preceding a line terminator sequence to decide if the line - * terminator is escaped; there must be an odd number of - * contiguous backslashes for the line terminator to be escaped. - * Since the input is processed from left to right, a - * non-zero even number of 2n contiguous backslashes - * before a line terminator (or elsewhere) encodes n - * backslashes after escape processing. - * - *

- * The key contains all of the characters in the line starting - * with the first non-white space character and up to, but not - * including, the first unescaped {@code '='}, - * {@code ':'}, or white space character other than a line - * terminator. All of these key termination characters may be - * included in the key by escaping them with a preceding backslash - * character; for example,

- * - * {@code \:\=}

- * - * would be the two-character key {@code ":="}. Line - * terminator characters can be included using {@code \r} and - * {@code \n} escape sequences. Any white space after the - * key is skipped; if the first non-white space character after - * the key is {@code '='} or {@code ':'}, then it is - * ignored and any white space characters after it are also - * skipped. All remaining characters on the line become part of - * the associated element string; if there are no remaining - * characters, the element is the empty string - * {@code ""}. Once the raw character sequences - * constituting the key and element are identified, escape - * processing is performed as described above. - * - *

- * As an example, each of the following three lines specifies the key - * {@code "Truth"} and the associated element value - * {@code "Beauty"}: - *

-   * Truth = Beauty
-   *  Truth:Beauty
-   * Truth                    :Beauty
-   * 
- * As another example, the following three lines specify a single - * property: - *
-   * fruits                           apple, banana, pear, \
-   *                                  cantaloupe, watermelon, \
-   *                                  kiwi, mango
-   * 
- * The key is {@code "fruits"} and the associated element is: - *
"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"
- * Note that a space appears before each {@code \} so that a space - * will appear after each comma in the final result; the {@code \}, - * line terminator, and leading white space on the continuation line are - * merely discarded and are not replaced by one or more other - * characters. - *

- * As a third example, the line: - *

cheeses
-   * 
- * specifies that the key is {@code "cheeses"} and the associated - * element is the empty string {@code ""}. - *

- * - * Characters in keys and elements can be represented in escape - * sequences similar to those used for character and string literals - * (see sections 3.3 and 3.10.6 of - * The Java™ Language Specification). - * - * The differences from the character escape sequences and Unicode - * escapes used for characters and strings are: - * - *

    - *
  • Octal escapes are not recognized. - * - *
  • The character sequence {@code \b} does not - * represent a backspace character. - * - *
  • The method does not treat a backslash character, - * {@code \}, before a non-valid escape character as an - * error; the backslash is silently dropped. For example, in a - * Java string the sequence {@code "\z"} would cause a - * compile time error. In contrast, this method silently drops - * the backslash. Therefore, this method treats the two character - * sequence {@code "\b"} as equivalent to the single - * character {@code 'b'}. - * - *
  • Escapes are not necessary for single and double quotes; - * however, by the rule above, single and double quote characters - * preceded by a backslash still yield single and double quote - * characters, respectively. - * - *
  • Only a single 'u' character is allowed in a Unicode escape - * sequence. - * - *
- *

- * The specified stream remains open after this method returns. - * - * @param reader the input character stream. - * @throws IOException if an error occurred when reading from the - * input stream. - * @throws IllegalArgumentException if a malformed Unicode escape - * appears in the input. - * @since 1.6 - */ - public synchronized void load(Reader reader) throws IOException { - load0(new LineReader(reader)); - } - - /** - * Reads a property list (key and element pairs) from the input - * byte stream. The input stream is in a simple line-oriented - * format as specified in - * {@link #load(java.io.Reader) load(Reader)} and is assumed to use - * the ISO 8859-1 character encoding; that is each byte is one Latin1 - * character. Characters not in Latin1, and certain special characters, - * are represented in keys and elements using Unicode escapes as defined in - * section 3.3 of - * The Java™ Language Specification. - *

- * The specified stream remains open after this method returns. - * - * @param inStream the input stream. - * @exception IOException if an error occurred when reading from the - * input stream. - * @throws IllegalArgumentException if the input stream contains a - * malformed Unicode escape sequence. - * @since 1.2 - */ - public synchronized void load(InputStream inStream) throws IOException { - load0(new LineReader(inStream)); - } - - private void load0 (LineReader lr) throws IOException { - char[] convtBuf = new char[1024]; - int limit; - int keyLen; - int valueStart; - char c; - boolean hasSep; - boolean precedingBackslash; - - while ((limit = lr.readLine()) >= 0) { - c = 0; - keyLen = 0; - valueStart = limit; - hasSep = false; - - //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">"); - precedingBackslash = false; - while (keyLen < limit) { - c = lr.lineBuf[keyLen]; - //need check if escaped. - if ((c == '=' || c == ':') && !precedingBackslash) { - valueStart = keyLen + 1; - hasSep = true; - break; - } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) { - valueStart = keyLen + 1; - break; - } - if (c == '\\') { - precedingBackslash = !precedingBackslash; - } else { - precedingBackslash = false; - } - keyLen++; - } - while (valueStart < limit) { - c = lr.lineBuf[valueStart]; - if (c != ' ' && c != '\t' && c != '\f') { - if (!hasSep && (c == '=' || c == ':')) { - hasSep = true; - } else { - break; - } - } - valueStart++; - } - String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); - String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); - put(key, value); - } - } - - /* Read in a "logical line" from an InputStream/Reader, skip all comment - * and blank lines and filter out those leading whitespace characters - * (\u0020, \u0009 and \u000c) from the beginning of a "natural line". - * Method returns the char length of the "logical line" and stores - * the line in "lineBuf". - */ - class LineReader { - public LineReader(InputStream inStream) { - this.inStream = inStream; - inByteBuf = new byte[8192]; + /** + * Creates an empty property list with no default values. + */ + public OrderedProperties() { + this(null); } - public LineReader(Reader reader) { - this.reader = reader; - inCharBuf = new char[8192]; + /** + * Creates an empty property list with the specified defaults. + * + * @param defaults the defaults. + */ + public OrderedProperties(OrderedProperties defaults) { + this.defaults = defaults; } - byte[] inByteBuf; - char[] inCharBuf; - char[] lineBuf = new char[1024]; - int inLimit = 0; - int inOff = 0; - InputStream inStream; - Reader reader; + /** + * Calls the Hashtable method {@code put}. Provided for + * parallelism with the getProperty method. Enforces use of + * strings for property keys and values. The value returned is the + * result of the Hashtable call to {@code put}. + * + * @param key the key to be placed into this property list. + * @param value the value corresponding to key. + * @return the previous value of the specified key in this property + * list, or {@code null} if it did not have one. + * @see #getProperty + * @since 1.2 + */ + public synchronized Object setProperty(String key, String value) { + return put(key, value); + } - int readLine() throws IOException { - int len = 0; - char c = 0; - boolean skipWhiteSpace = true; - boolean isCommentLine = false; - boolean isNewLine = true; - boolean appendedLineBegin = false; - boolean precedingBackslash = false; - boolean skipLF = false; + /** + * Reads a property list (key and element pairs) from the input + * character stream in a simple line-oriented format. + *

+ * Properties are processed in terms of lines. There are two + * kinds of line, natural lines and logical lines. + * A natural line is defined as a line of + * characters that is terminated either by a set of line terminator + * characters ({@code \n} or {@code \r} or {@code \r\n}) + * or by the end of the stream. A natural line may be either a blank line, + * a comment line, or hold all or some of a key-element pair. A logical + * line holds all the data of a key-element pair, which may be spread + * out across several adjacent natural lines by escaping + * the line terminator sequence with a backslash character + * {@code \}. Note that a comment line cannot be extended + * in this manner; every natural line that is a comment must have + * its own comment indicator, as described below. Lines are read from + * input until the end of the stream is reached. + * + *

+ * A natural line that contains only white space characters is + * considered blank and is ignored. A comment line has an ASCII + * {@code '#'} or {@code '!'} as its first non-white + * space character; comment lines are also ignored and do not + * encode key-element information. In addition to line + * terminators, this format considers the characters space + * ({@code ' '}, {@code '\u005Cu0020'}), tab + * ({@code '\t'}, {@code '\u005Cu0009'}), and form feed + * ({@code '\f'}, {@code '\u005Cu000C'}) to be white + * space. + * + *

+ * If a logical line is spread across several natural lines, the + * backslash escaping the line terminator sequence, the line + * terminator sequence, and any white space at the start of the + * following line have no affect on the key or element values. + * The remainder of the discussion of key and element parsing + * (when loading) will assume all the characters constituting + * the key and element appear on a single natural line after + * line continuation characters have been removed. Note that + * it is not sufficient to only examine the character + * preceding a line terminator sequence to decide if the line + * terminator is escaped; there must be an odd number of + * contiguous backslashes for the line terminator to be escaped. + * Since the input is processed from left to right, a + * non-zero even number of 2n contiguous backslashes + * before a line terminator (or elsewhere) encodes n + * backslashes after escape processing. + * + *

+ * The key contains all of the characters in the line starting + * with the first non-white space character and up to, but not + * including, the first unescaped {@code '='}, + * {@code ':'}, or white space character other than a line + * terminator. All of these key termination characters may be + * included in the key by escaping them with a preceding backslash + * character; for example,

+ *

+ * {@code \:\=}

+ *

+ * would be the two-character key {@code ":="}. Line + * terminator characters can be included using {@code \r} and + * {@code \n} escape sequences. Any white space after the + * key is skipped; if the first non-white space character after + * the key is {@code '='} or {@code ':'}, then it is + * ignored and any white space characters after it are also + * skipped. All remaining characters on the line become part of + * the associated element string; if there are no remaining + * characters, the element is the empty string + * {@code ""}. Once the raw character sequences + * constituting the key and element are identified, escape + * processing is performed as described above. + * + *

+ * As an example, each of the following three lines specifies the key + * {@code "Truth"} and the associated element value + * {@code "Beauty"}: + *

+     * Truth = Beauty
+     *  Truth:Beauty
+     * Truth: Beauty
+     * 
+ * As another example, the following three lines specify a single + * property: + *
+     * fruits apple, banana, pear, \
+     *                                  cantaloupe, watermelon, \
+     *                                  kiwi, mango
+     * 
+ * The key is {@code "fruits"} and the associated element is: + *
"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"
+ * Note that a space appears before each {@code \} so that a space + * will appear after each comma in the final result; the {@code \}, + * line terminator, and leading white space on the continuation line are + * merely discarded and are not replaced by one or more other + * characters. + *

+ * As a third example, the line: + *

cheeses
+     * 
+ * specifies that the key is {@code "cheeses"} and the associated + * element is the empty string {@code ""}. + *

+ * + * Characters in keys and elements can be represented in escape + * sequences similar to those used for character and string literals + * (see sections 3.3 and 3.10.6 of + * The Java™ Language Specification). + *

+ * The differences from the character escape sequences and Unicode + * escapes used for characters and strings are: + * + *

    + *
  • Octal escapes are not recognized. + * + *
  • The character sequence {@code \b} does not + * represent a backspace character. + * + *
  • The method does not treat a backslash character, + * {@code \}, before a non-valid escape character as an + * error; the backslash is silently dropped. For example, in a + * Java string the sequence {@code "\z"} would cause a + * compile time error. In contrast, this method silently drops + * the backslash. Therefore, this method treats the two character + * sequence {@code "\b"} as equivalent to the single + * character {@code 'b'}. + * + *
  • Escapes are not necessary for single and double quotes; + * however, by the rule above, single and double quote characters + * preceded by a backslash still yield single and double quote + * characters, respectively. + * + *
  • Only a single 'u' character is allowed in a Unicode escape + * sequence. + * + *
+ *

+ * The specified stream remains open after this method returns. + * + * @param reader the input character stream. + * @throws IOException if an error occurred when reading from the + * input stream. + * @throws IllegalArgumentException if a malformed Unicode escape + * appears in the input. + * @since 1.6 + */ + public synchronized void load(Reader reader) throws IOException { + load0(new LineReader(reader)); + } - while (true) { - if (inOff >= inLimit) { - inLimit = (inStream==null)?reader.read(inCharBuf) - :inStream.read(inByteBuf); - inOff = 0; - if (inLimit <= 0) { - if (len == 0 || isCommentLine) { - return -1; - } - if (precedingBackslash) { - len--; - } - return len; - } - } - if (inStream != null) { - //The line below is equivalent to calling a - //ISO8859-1 decoder. - c = (char) (0xff & inByteBuf[inOff++]); - } else { - c = inCharBuf[inOff++]; - } - if (skipLF) { - skipLF = false; - if (c == '\n') { - continue; - } - } - if (skipWhiteSpace) { - if (c == ' ' || c == '\t' || c == '\f') { - continue; - } - if (!appendedLineBegin && (c == '\r' || c == '\n')) { - continue; - } - skipWhiteSpace = false; - appendedLineBegin = false; - } - if (isNewLine) { - isNewLine = false; - if (c == '#' || c == '!') { - isCommentLine = true; - continue; - } - } + /** + * Reads a property list (key and element pairs) from the input + * byte stream. The input stream is in a simple line-oriented + * format as specified in + * {@link #load(java.io.Reader) load(Reader)} and is assumed to use + * the ISO 8859-1 character encoding; that is each byte is one Latin1 + * character. Characters not in Latin1, and certain special characters, + * are represented in keys and elements using Unicode escapes as defined in + * section 3.3 of + * The Java™ Language Specification. + *

+ * The specified stream remains open after this method returns. + * + * @param inStream the input stream. + * @throws IOException if an error occurred when reading from the + * input stream. + * @throws IllegalArgumentException if the input stream contains a + * malformed Unicode escape sequence. + * @since 1.2 + */ + public synchronized void load(InputStream inStream) throws IOException { + load0(new LineReader(inStream)); + } - if (c != '\n' && c != '\r') { - lineBuf[len++] = c; - if (len == lineBuf.length) { - int newLength = lineBuf.length * 2; - if (newLength < 0) { - newLength = Integer.MAX_VALUE; - } - char[] buf = new char[newLength]; - System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length); - lineBuf = buf; - } - //flip the preceding backslash flag - if (c == '\\') { - precedingBackslash = !precedingBackslash; - } else { + private void load0(LineReader lr) throws IOException { + char[] convtBuf = new char[1024]; + int limit; + int keyLen; + int valueStart; + char c; + boolean hasSep; + boolean precedingBackslash; + + while ((limit = lr.readLine()) >= 0) { + c = 0; + keyLen = 0; + valueStart = limit; + hasSep = false; + + //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">"); precedingBackslash = false; - } - } - else { - // reached EOL - if (isCommentLine || len == 0) { - isCommentLine = false; - isNewLine = true; - skipWhiteSpace = true; - len = 0; - continue; - } - if (inOff >= inLimit) { - inLimit = (inStream==null) - ?reader.read(inCharBuf) - :inStream.read(inByteBuf); - inOff = 0; - if (inLimit <= 0) { - if (precedingBackslash) { - len--; - } - return len; + while (keyLen < limit) { + c = lr.lineBuf[keyLen]; + //need check if escaped. + if ((c == '=' || c == ':') && !precedingBackslash) { + valueStart = keyLen + 1; + hasSep = true; + break; + } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) { + valueStart = keyLen + 1; + break; } - } - if (precedingBackslash) { - len -= 1; - //skip the leading whitespace characters in following line - skipWhiteSpace = true; - appendedLineBegin = true; - precedingBackslash = false; - if (c == '\r') { - skipLF = true; + if (c == '\\') { + precedingBackslash = !precedingBackslash; + } else { + precedingBackslash = false; + } + keyLen++; } - } else { - return len; - } + while (valueStart < limit) { + c = lr.lineBuf[valueStart]; + if (c != ' ' && c != '\t' && c != '\f') { + if (!hasSep && (c == '=' || c == ':')) { + hasSep = true; + } else { + break; + } + } + valueStart++; + } + String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); + String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); + put(key, value); } - } } - } - /* - * Converts encoded \uxxxx to unicode chars - * and changes special saved chars to their original forms - */ - private String loadConvert (char[] in, int off, int len, char[] convtBuf) { - if (convtBuf.length < len) { - int newLen = len * 2; - if (newLen < 0) { - newLen = Integer.MAX_VALUE; - } - convtBuf = new char[newLen]; + /* Read in a "logical line" from an InputStream/Reader, skip all comment + * and blank lines and filter out those leading whitespace characters + * (\u0020, \u0009 and \u000c) from the beginning of a "natural line". + * Method returns the char length of the "logical line" and stores + * the line in "lineBuf". + */ + static class LineReader { + public LineReader(InputStream inStream) { + this.inStream = inStream; + inByteBuf = new byte[8192]; + } + + public LineReader(Reader reader) { + this.reader = reader; + inCharBuf = new char[8192]; + } + + byte[] inByteBuf; + char[] inCharBuf; + char[] lineBuf = new char[1024]; + int inLimit = 0; + int inOff = 0; + InputStream inStream; + Reader reader; + + int readLine() throws IOException { + int len = 0; + char c = 0; + + boolean skipWhiteSpace = true; + boolean isCommentLine = false; + boolean isNewLine = true; + boolean appendedLineBegin = false; + boolean precedingBackslash = false; + boolean skipLF = false; + + while (true) { + if (inOff >= inLimit) { + inLimit = (inStream == null) ? reader.read(inCharBuf) + : inStream.read(inByteBuf); + inOff = 0; + if (inLimit <= 0) { + if (len == 0 || isCommentLine) { + return -1; + } + if (precedingBackslash) { + len--; + } + return len; + } + } + if (inStream != null) { + //The line below is equivalent to calling a + //ISO8859-1 decoder. + c = (char) (0xff & inByteBuf[inOff++]); + } else { + c = inCharBuf[inOff++]; + } + if (skipLF) { + skipLF = false; + if (c == '\n') { + continue; + } + } + if (skipWhiteSpace) { + if (c == ' ' || c == '\t' || c == '\f') { + continue; + } + if (!appendedLineBegin && (c == '\r' || c == '\n')) { + continue; + } + skipWhiteSpace = false; + appendedLineBegin = false; + } + if (isNewLine) { + isNewLine = false; + if (c == '#' || c == '!') { + isCommentLine = true; + continue; + } + } + + if (c != '\n' && c != '\r') { + lineBuf[len++] = c; + if (len == lineBuf.length) { + int newLength = lineBuf.length * 2; + char[] buf = new char[newLength]; + System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length); + lineBuf = buf; + } + //flip the preceding backslash flag + if (c == '\\') { + precedingBackslash = !precedingBackslash; + } else { + precedingBackslash = false; + } + } else { + // reached EOL + if (isCommentLine) { + isCommentLine = false; + isNewLine = true; + skipWhiteSpace = true; + len = 0; + continue; + } + if (inOff >= inLimit) { + inLimit = (inStream == null) + ? reader.read(inCharBuf) + : inStream.read(inByteBuf); + inOff = 0; + if (inLimit <= 0) { + if (precedingBackslash) { + len--; + } + return len; + } + } + if (precedingBackslash) { + len -= 1; + //skip the leading whitespace characters in following line + skipWhiteSpace = true; + appendedLineBegin = true; + precedingBackslash = false; + if (c == '\r') { + skipLF = true; + } + } else { + return len; + } + } + } + } } - char aChar; - char[] out = convtBuf; - int outLen = 0; - int end = off + len; - while (off < end) { - aChar = in[off++]; - if (aChar == '\\') { - aChar = in[off++]; - if(aChar == 'u') { - // Read the xxxx - int value=0; - for (int i=0; i<4; i++) { + /* + * Converts encoded \uxxxx to unicode chars + * and changes special saved chars to their original forms + */ + private String loadConvert(char[] in, int off, int len, char[] convtBuf) { + if (convtBuf.length < len) { + int newLen = len * 2; + convtBuf = new char[newLen]; + } + char aChar; + char[] out = convtBuf; + int outLen = 0; + int end = off + len; + + while (off < end) { aChar = in[off++]; - switch (aChar) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - value = (value << 4) + aChar - '0'; - break; - case 'a': case 'b': case 'c': - case 'd': case 'e': case 'f': - value = (value << 4) + 10 + aChar - 'a'; - break; - case 'A': case 'B': case 'C': - case 'D': case 'E': case 'F': - value = (value << 4) + 10 + aChar - 'A'; - break; - default: - throw new IllegalArgumentException( - "Malformed \\uxxxx encoding."); + if (aChar == '\\') { + aChar = in[off++]; + if (aChar == 'u') { + // Read the xxxx + int value = 0; + for (int i = 0; i < 4; i++) { + aChar = in[off++]; + value = switch (aChar) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> (value << 4) + aChar - '0'; + case 'a', 'b', 'c', 'd', 'e', 'f' -> (value << 4) + 10 + aChar - 'a'; + case 'A', 'B', 'C', 'D', 'E', 'F' -> (value << 4) + 10 + aChar - 'A'; + default -> throw new IllegalArgumentException( + "Malformed \\uxxxx encoding."); + }; + } + out[outLen++] = (char) value; + } else { + if (aChar == 't') aChar = '\t'; + else if (aChar == 'r') aChar = '\r'; + else if (aChar == 'n') aChar = '\n'; + else if (aChar == 'f') aChar = '\f'; + out[outLen++] = aChar; + } + } else { + out[outLen++] = aChar; } - } - out[outLen++] = (char)value; - } else { - if (aChar == 't') aChar = '\t'; - else if (aChar == 'r') aChar = '\r'; - else if (aChar == 'n') aChar = '\n'; - else if (aChar == 'f') aChar = '\f'; - out[outLen++] = aChar; } - } else { - out[outLen++] = aChar; - } + return new String(out, 0, outLen); } - return new String (out, 0, outLen); - } - /* - * Converts unicodes to encoded \uxxxx and escapes - * special characters with a preceding slash - */ - private String saveConvert(String theString, - boolean escapeSpace, - boolean escapeUnicode) { - int len = theString.length(); - int bufLen = len * 2; - if (bufLen < 0) { - bufLen = Integer.MAX_VALUE; - } - StringBuffer outBuffer = new StringBuffer(bufLen); + /* + * Converts unicodes to encoded \uxxxx and escapes + * special characters with a preceding slash + */ + private String saveConvert(String theString, + boolean escapeSpace, + boolean escapeUnicode) { + int len = theString.length(); + int bufLen = len * 2; + StringBuffer outBuffer = new StringBuffer(bufLen); - for(int x=0; x 61) && (aChar < 127)) { - if (aChar == '\\') { - outBuffer.append('\\'); outBuffer.append('\\'); - continue; + for (int x = 0; x < len; x++) { + char aChar = theString.charAt(x); + // Handle common case first, selecting largest block that + // avoids the specials below + if ((aChar > 61) && (aChar < 127)) { + if (aChar == '\\') { + outBuffer.append('\\'); + outBuffer.append('\\'); + continue; + } + outBuffer.append(aChar); + continue; + } + switch (aChar) { + case ' ': + if (x == 0 || escapeSpace) + outBuffer.append('\\'); + outBuffer.append(' '); + break; + case '\t': + outBuffer.append('\\'); + outBuffer.append('t'); + break; + case '\n': + outBuffer.append('\\'); + outBuffer.append('n'); + break; + case '\r': + outBuffer.append('\\'); + outBuffer.append('r'); + break; + case '\f': + outBuffer.append('\\'); + outBuffer.append('f'); + break; + case '=': // Fall through + case ':': // Fall through + case '#': // Fall through + case '!': + outBuffer.append('\\'); + outBuffer.append(aChar); + break; + default: + if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) { + outBuffer.append('\\'); + outBuffer.append('u'); + outBuffer.append(toHex((aChar >> 12) & 0xF)); + outBuffer.append(toHex((aChar >> 8) & 0xF)); + outBuffer.append(toHex((aChar >> 4) & 0xF)); + outBuffer.append(toHex(aChar & 0xF)); + } else { + outBuffer.append(aChar); + } + } } - outBuffer.append(aChar); - continue; - } - switch(aChar) { - case ' ': - if (x == 0 || escapeSpace) - outBuffer.append('\\'); - outBuffer.append(' '); - break; - case '\t':outBuffer.append('\\'); outBuffer.append('t'); - break; - case '\n':outBuffer.append('\\'); outBuffer.append('n'); - break; - case '\r':outBuffer.append('\\'); outBuffer.append('r'); - break; - case '\f':outBuffer.append('\\'); outBuffer.append('f'); - break; - case '=': // Fall through - case ':': // Fall through - case '#': // Fall through - case '!': - outBuffer.append('\\'); outBuffer.append(aChar); - break; - default: - if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) { - outBuffer.append('\\'); - outBuffer.append('u'); - outBuffer.append(toHex((aChar >> 12) & 0xF)); - outBuffer.append(toHex((aChar >> 8) & 0xF)); - outBuffer.append(toHex((aChar >> 4) & 0xF)); - outBuffer.append(toHex( aChar & 0xF)); - } else { - outBuffer.append(aChar); - } - } + return outBuffer.toString(); } - return outBuffer.toString(); - } - private static void writeComments(BufferedWriter bw, String comments) - throws IOException { - bw.write("#"); - int len = comments.length(); - int current = 0; - int last = 0; - char[] uu = new char[6]; - uu[0] = '\\'; - uu[1] = 'u'; - while (current < len) { - char c = comments.charAt(current); - if (c > '\u00ff' || c == '\n' || c == '\r') { - if (last != current) - bw.write(comments.substring(last, current)); - if (c > '\u00ff') { - uu[2] = toHex((c >> 12) & 0xf); - uu[3] = toHex((c >> 8) & 0xf); - uu[4] = toHex((c >> 4) & 0xf); - uu[5] = toHex( c & 0xf); - bw.write(new String(uu)); - } else { - bw.newLine(); - if (c == '\r' && - current != len - 1 && - comments.charAt(current + 1) == '\n') { + private static void writeComments(BufferedWriter bw, String comments) + throws IOException { + bw.write("#"); + int len = comments.length(); + int current = 0; + int last = 0; + char[] uu = new char[6]; + uu[0] = '\\'; + uu[1] = 'u'; + while (current < len) { + char c = comments.charAt(current); + if (c > 'ÿ' || c == '\n' || c == '\r') { + if (last != current) + bw.write(comments.substring(last, current)); + if (c > 'ÿ') { + uu[2] = toHex((c >> 12) & 0xf); + uu[3] = toHex((c >> 8) & 0xf); + uu[4] = toHex((c >> 4) & 0xf); + uu[5] = toHex(c & 0xf); + bw.write(new String(uu)); + } else { + bw.newLine(); + if (c == '\r' && + current != len - 1 && + comments.charAt(current + 1) == '\n') { + current++; + } + if (current == len - 1 || + (comments.charAt(current + 1) != '#' && + comments.charAt(current + 1) != '!')) + bw.write("#"); + } + last = current + 1; + } current++; - } - if (current == len - 1 || - (comments.charAt(current + 1) != '#' && - comments.charAt(current + 1) != '!')) - bw.write("#"); } - last = current + 1; - } - current++; - } - if (last != current) - bw.write(comments.substring(last, current)); - bw.newLine(); - } - - /** - * Calls the {@code store(OutputStream out, String comments)} method - * and suppresses IOExceptions that were thrown. - * - * @deprecated This method does not throw an IOException if an I/O error - * occurs while saving the property list. The preferred way to save a - * properties list is via the {@code store(OutputStream out, - * String comments)} method or the - * {@code storeToXML(OutputStream os, String comment)} method. - * - * @param out an output stream. - * @param comments a description of the property list. - * @exception ClassCastException if this {@code Properties} object - * contains any keys or values that are not - * {@code Strings}. - */ - @Deprecated - public void save(OutputStream out, String comments) { - try { - store(out, comments); - } catch (IOException e) { - } - } - - /** - * Writes this property list (key and element pairs) in this - * {@code Properties} table to the output character stream in a - * format suitable for using the {@link #load(java.io.Reader) load(Reader)} - * method. - *

- * Properties from the defaults table of this {@code Properties} - * table (if any) are not written out by this method. - *

- * If the comments argument is not null, then an ASCII {@code #} - * character, the comments string, and a line separator are first written - * to the output stream. Thus, the {@code comments} can serve as an - * identifying comment. Any one of a line feed ('\n'), a carriage - * return ('\r'), or a carriage return followed immediately by a line feed - * in comments is replaced by a line separator generated by the {@code Writer} - * and if the next character in comments is not character {@code #} or - * character {@code !} then an ASCII {@code #} is written out - * after that line separator. - *

- * Next, a comment line is always written, consisting of an ASCII - * {@code #} character, the current date and time (as if produced - * by the {@code toString} method of {@code Date} for the - * current time), and a line separator as generated by the {@code Writer}. - *

- * Then every entry in this {@code Properties} table is - * written out, one per line. For each entry the key string is - * written, then an ASCII {@code =}, then the associated - * element string. For the key, all space characters are - * written with a preceding {@code \} character. For the - * element, leading space characters, but not embedded or trailing - * space characters, are written with a preceding {@code \} - * character. The key and element characters {@code #}, - * {@code !}, {@code =}, and {@code :} are written - * with a preceding backslash to ensure that they are properly loaded. - *

- * After the entries have been written, the output stream is flushed. - * The output stream remains open after this method returns. - *

- * - * @param writer an output character stream writer. - * @param comments a description of the property list. - * @exception IOException if writing this property list to the specified - * output stream throws an IOException. - * @exception ClassCastException if this {@code Properties} object - * contains any keys or values that are not {@code Strings}. - * @exception NullPointerException if {@code writer} is null. - * @since 1.6 - */ - public void store(Writer writer, String comments) - throws IOException - { - store0((writer instanceof BufferedWriter)?(BufferedWriter)writer - : new BufferedWriter(writer), - comments, - false); - } - - /** - * Writes this property list (key and element pairs) in this - * {@code Properties} table to the output stream in a format suitable - * for loading into a {@code Properties} table using the - * {@link #load(InputStream) load(InputStream)} method. - *

- * Properties from the defaults table of this {@code Properties} - * table (if any) are not written out by this method. - *

- * This method outputs the comments, properties keys and values in - * the same format as specified in - * {@link #store(java.io.Writer, java.lang.String) store(Writer)}, - * with the following differences: - *

    - *
  • The stream is written using the ISO 8859-1 character encoding. - * - *
  • Characters not in Latin-1 in the comments are written as - * {@code \u005Cu}xxxx for their appropriate unicode - * hexadecimal value xxxx. - * - *
  • Characters less than {@code \u005Cu0020} and characters greater - * than {@code \u005Cu007E} in property keys or values are written - * as {@code \u005Cu}xxxx for the appropriate hexadecimal - * value xxxx. - *
- *

- * After the entries have been written, the output stream is flushed. - * The output stream remains open after this method returns. - *

- * @param out an output stream. - * @param comments a description of the property list. - * @exception IOException if writing this property list to the specified - * output stream throws an IOException. - * @exception ClassCastException if this {@code Properties} object - * contains any keys or values that are not {@code Strings}. - * @exception NullPointerException if {@code out} is null. - * @since 1.2 - */ - public void store(OutputStream out, String comments) - throws IOException - { - store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), - comments, - true); - } - - private void store0(BufferedWriter bw, String comments, boolean escUnicode) - throws IOException - { - if (comments != null) { - writeComments(bw, comments); - } - bw.write("#" + new Date().toString()); - bw.newLine(); - synchronized (this) { - for (Enumeration e = keys(); e.hasMoreElements();) { - String key = (String)e.nextElement(); - String val = (String)get(key); - key = saveConvert(key, true, escUnicode); - /* No need to escape embedded and trailing spaces for value, hence - * pass false to flag. - */ - val = saveConvert(val, false, escUnicode); - bw.write(key + "=" + val); + if (last != current) + bw.write(comments.substring(last, current)); bw.newLine(); - } } - bw.flush(); - } - /** - * Searches for the property with the specified key in this property list. - * If the key is not found in this property list, the default property list, - * and its defaults, recursively, are then checked. The method returns - * {@code null} if the property is not found. - * - * @param key the property key. - * @return the value in this property list with the specified key value. - * @see #setProperty - * @see #defaults - */ - public String getProperty(String key) { - Object oval = super.get(key); - String sval = (oval instanceof String) ? (String)oval : null; - return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; - } - - /** - * Searches for the property with the specified key in this property list. - * If the key is not found in this property list, the default property list, - * and its defaults, recursively, are then checked. The method returns the - * default value argument if the property is not found. - * - * @param key the hashtable key. - * @param defaultValue a default value. - * - * @return the value in this property list with the specified key value. - * @see #setProperty - * @see #defaults - */ - public String getProperty(String key, String defaultValue) { - String val = getProperty(key); - return (val == null) ? defaultValue : val; - } - - /** - * Returns an enumeration of all the keys in this property list, - * including distinct keys in the default property list if a key - * of the same name has not already been found from the main - * properties list. - * - * @return an enumeration of all the keys in this property list, including - * the keys in the default property list. - * @throws ClassCastException if any key in this property list - * is not a string. - * @see java.util.Enumeration - * @see java.util.Properties#defaults - * @see #stringPropertyNames - */ - public Enumeration propertyNames() { - LinkedHashMap h = new LinkedHashMap<>(); - enumerate(h); - return Collections.enumeration(h.keySet()); - } - - /** - * Returns a set of keys in this property list where - * the key and its corresponding value are strings, - * including distinct keys in the default property list if a key - * of the same name has not already been found from the main - * properties list. Properties whose key or value is not - * of type String are omitted. - *

- * The returned set is not backed by the Properties object. - * Changes to this Properties are not reflected in the set, - * or vice versa. - * - * @return a set of keys in this property list where - * the key and its corresponding value are strings, - * including the keys in the default property list. - * @see java.util.Properties#defaults - * @since 1.6 - */ - public Set stringPropertyNames() { - LinkedHashMap h = new LinkedHashMap<>(); - enumerateStringProperties(h); - return h.keySet(); - } - - /** - * Prints this property list out to the specified output stream. - * This method is useful for debugging. - * - * @param out an output stream. - * @throws ClassCastException if any key in this property list - * is not a string. - */ - public void list(PrintStream out) { - out.println("-- listing properties --"); - LinkedHashMap h = new LinkedHashMap<>(); - enumerate(h); - for (Enumeration e = Collections.enumeration(h.keySet()) ; e.hasMoreElements() ;) { - String key = e.nextElement(); - String val = (String)h.get(key); - if (val.length() > 40) { - val = val.substring(0, 37) + "..."; - } - out.println(key + "=" + val); + /** + * Calls the {@code store(OutputStream out, String comments)} method + * and suppresses IOExceptions that were thrown. + * + * @param out an output stream. + * @throws ClassCastException if this {@code Properties} object + * contains any keys or values that are not + * {@code Strings}. + * @deprecated This method does not throw an IOException if an I/O error + * occurs while saving the property list. The preferred way to save a + * properties list is via the {@code store(OutputStream out, + * String comments)} method or the + * {@code storeToXML(OutputStream os, String comment)} method. + */ + @Deprecated + public void save(OutputStream out, String comments) { + try { + store(out, comments); + } catch (IOException ignored) { + } } - } - /** - * Prints this property list out to the specified output stream. - * This method is useful for debugging. - * - * @param out an output stream. - * @throws ClassCastException if any key in this property list - * is not a string. - * @since JDK1.1 - */ - /* - * Rather than use an anonymous inner class to share common code, this - * method is duplicated in order to ensure that a non-1.1 compiler can - * compile this file. - */ - public void list(PrintWriter out) { - out.println("-- listing properties --"); - LinkedHashMap h = new LinkedHashMap<>(); - enumerate(h); - for (Enumeration e = Collections.enumeration(h.keySet()) ; e.hasMoreElements() ;) { - String key = e.nextElement(); - String val = (String)h.get(key); - if (val.length() > 40) { - val = val.substring(0, 37) + "..."; - } - out.println(key + "=" + val); + /** + * Writes this property list (key and element pairs) in this + * {@code Properties} table to the output character stream in a + * format suitable for using the {@link #load(java.io.Reader) load(Reader)} + * method. + *

+ * Properties from the defaults table of this {@code Properties} + * table (if any) are not written out by this method. + *

+ * If the comments argument is not null, then an ASCII {@code #} + * character, the comments string, and a line separator are first written + * to the output stream. Thus, the {@code comments} can serve as an + * identifying comment. Any one of a line feed ('\n'), a carriage + * return ('\r'), or a carriage return followed immediately by a line feed + * in comments is replaced by a line separator generated by the {@code Writer} + * and if the next character in comments is not character {@code #} or + * character {@code !} then an ASCII {@code #} is written out + * after that line separator. + *

+ * Next, a comment line is always written, consisting of an ASCII + * {@code #} character, the current date and time (as if produced + * by the {@code toString} method of {@code Date} for the + * current time), and a line separator as generated by the {@code Writer}. + *

+ * Then every entry in this {@code Properties} table is + * written out, one per line. For each entry the key string is + * written, then an ASCII {@code =}, then the associated + * element string. For the key, all space characters are + * written with a preceding {@code \} character. For the + * element, leading space characters, but not embedded or trailing + * space characters, are written with a preceding {@code \} + * character. The key and element characters {@code #}, + * {@code !}, {@code =}, and {@code :} are written + * with a preceding backslash to ensure that they are properly loaded. + *

+ * After the entries have been written, the output stream is flushed. + * The output stream remains open after this method returns. + *

+ * + * @param writer an output character stream writer. + * @param comments a description of the property list. + * @throws IOException if writing this property list to the specified + * output stream throws an IOException. + * @throws ClassCastException if this {@code Properties} object + * contains any keys or values that are not {@code Strings}. + * @throws NullPointerException if {@code writer} is null. + * @since 1.6 + */ + public void store(Writer writer, String comments) + throws IOException { + store0((writer instanceof BufferedWriter) ? (BufferedWriter) writer + : new BufferedWriter(writer), + comments, + false); } - } - /** - * Enumerates all key/value pairs in the specified hashtable. - * @param h the hashtable - * @throws ClassCastException if any of the property keys - * is not of String type. - */ - private synchronized void enumerate(LinkedHashMap h) { - if (defaults != null) { - defaults.enumerate(h); + /** + * Writes this property list (key and element pairs) in this + * {@code Properties} table to the output stream in a format suitable + * for loading into a {@code Properties} table using the + * {@link #load(InputStream) load(InputStream)} method. + *

+ * Properties from the defaults table of this {@code Properties} + * table (if any) are not written out by this method. + *

+ * This method outputs the comments, properties keys and values in + * the same format as specified in + * {@link #store(java.io.Writer, java.lang.String) store(Writer)}, + * with the following differences: + *

    + *
  • The stream is written using the ISO 8859-1 character encoding. + * + *
  • Characters not in Latin-1 in the comments are written as + * {@code \u005Cu}xxxx for their appropriate unicode + * hexadecimal value xxxx. + * + *
  • Characters less than {@code \u005Cu0020} and characters greater + * than {@code \u005Cu007E} in property keys or values are written + * as {@code \u005Cu}xxxx for the appropriate hexadecimal + * value xxxx. + *
+ *

+ * After the entries have been written, the output stream is flushed. + * The output stream remains open after this method returns. + *

+ * + * @param out an output stream. + * @param comments a description of the property list. + * @throws IOException if writing this property list to the specified + * output stream throws an IOException. + * @throws ClassCastException if this {@code Properties} object + * contains any keys or values that are not {@code Strings}. + * @throws NullPointerException if {@code out} is null. + * @since 1.2 + */ + public void store(OutputStream out, String comments) + throws IOException { + store0(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.ISO_8859_1)), + comments, + true); } - for (Enumeration e = keys() ; e.hasMoreElements() ;) { - String key = (String)e.nextElement(); - h.put(key, get(key)); + + private void store0(BufferedWriter bw, String comments, boolean escUnicode) + throws IOException { + if (comments != null) { + writeComments(bw, comments); + } + bw.write("#" + new Date().toString()); + bw.newLine(); + synchronized (this) { + for (Enumeration e = keys(); e.hasMoreElements(); ) { + String key = (String) e.nextElement(); + String val = (String) get(key); + key = saveConvert(key, true, escUnicode); + /* No need to escape embedded and trailing spaces for value, hence + * pass false to flag. + */ + val = saveConvert(val, false, escUnicode); + bw.write(key + "=" + val); + bw.newLine(); + } + } + bw.flush(); } - } - private Enumeration keys() { - return Collections.enumeration(keySet()); - } - - /** - * Enumerates all key/value pairs in the specified hashtable - * and omits the property if the key or value is not a string. - * @param h the hashtable - */ - private synchronized void enumerateStringProperties(Map h) { - if (defaults != null) { - defaults.enumerateStringProperties(h); + /** + * Searches for the property with the specified key in this property list. + * If the key is not found in this property list, the default property list, + * and its defaults, recursively, are then checked. The method returns + * {@code null} if the property is not found. + * + * @param key the property key. + * @return the value in this property list with the specified key value. + * @see #setProperty + * @see #defaults + */ + public String getProperty(String key) { + Object oval = super.get(key); + String sval = (oval instanceof String) ? (String) oval : null; + return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; } - for (Enumeration e = keys() ; e.hasMoreElements() ;) { - Object k = e.nextElement(); - Object v = get(k); - if (k instanceof String && v instanceof String) { - h.put((String) k, (String) v); - } + + /** + * Searches for the property with the specified key in this property list. + * If the key is not found in this property list, the default property list, + * and its defaults, recursively, are then checked. The method returns the + * default value argument if the property is not found. + * + * @param key the hashtable key. + * @param defaultValue a default value. + * @return the value in this property list with the specified key value. + * @see #setProperty + * @see #defaults + */ + public String getProperty(String key, String defaultValue) { + String val = getProperty(key); + return (val == null) ? defaultValue : val; } - } - /** - * Convert a nibble to a hex character - * @param nibble the nibble to convert. - */ - private static char toHex(int nibble) { - return hexDigit[(nibble & 0xF)]; - } + /** + * Returns an enumeration of all the keys in this property list, + * including distinct keys in the default property list if a key + * of the same name has not already been found from the main + * properties list. + * + * @return an enumeration of all the keys in this property list, including + * the keys in the default property list. + * @throws ClassCastException if any key in this property list + * is not a string. + * @see java.util.Enumeration + * @see java.util.Properties#defaults + * @see #stringPropertyNames + */ + public Enumeration propertyNames() { + LinkedHashMap h = new LinkedHashMap<>(); + enumerate(h); + return Collections.enumeration(h.keySet()); + } - /** A table of hex digits */ - private static final char[] hexDigit = { - '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' - }; + /** + * Returns a set of keys in this property list where + * the key and its corresponding value are strings, + * including distinct keys in the default property list if a key + * of the same name has not already been found from the main + * properties list. Properties whose key or value is not + * of type String are omitted. + *

+ * The returned set is not backed by the Properties object. + * Changes to this Properties are not reflected in the set, + * or vice versa. + * + * @return a set of keys in this property list where + * the key and its corresponding value are strings, + * including the keys in the default property list. + * @see java.util.Properties#defaults + * @since 1.6 + */ + public Set stringPropertyNames() { + LinkedHashMap h = new LinkedHashMap<>(); + enumerateStringProperties(h); + return h.keySet(); + } + + /** + * Prints this property list out to the specified output stream. + * This method is useful for debugging. + * + * @param out an output stream. + * @throws ClassCastException if any key in this property list + * is not a string. + */ + public void list(PrintStream out) { + PrintWriter writer = new PrintWriter(out); + list(writer); + writer.flush(); + } + + /** + * Prints this property list out to the specified output stream. + * This method is useful for debugging. + * + * @param out an output stream. + * @throws ClassCastException if any key in this property list + * is not a string. + * @since JDK1.1 + */ + /* + * Rather than use an anonymous inner class to share common code, this + * method is duplicated in order to ensure that a non-1.1 compiler can + * compile this file. + */ + public void list(PrintWriter out) { + out.println("-- listing properties --"); + LinkedHashMap h = new LinkedHashMap<>(); + enumerate(h); + for (Enumeration e = Collections.enumeration(h.keySet()); e.hasMoreElements(); ) { + String key = e.nextElement(); + String val = (String) h.get(key); + if (val.length() > 40) { + val = val.substring(0, 37) + "..."; + } + out.println(key + "=" + val); + } + } + + /** + * Lists all key/value pairs in the specified hashtable. + * + * @param h the hashtable + * @throws ClassCastException if any of the property keys + * is not of the String type. + */ + private synchronized void enumerate(LinkedHashMap h) { + if (defaults != null) { + defaults.enumerate(h); + } + for (Enumeration e = keys(); e.hasMoreElements(); ) { + String key = (String) e.nextElement(); + h.put(key, get(key)); + } + } + + private Enumeration keys() { + return Collections.enumeration(keySet()); + } + + /** + * Enumerates all key/value pairs in the specified hashtable + * and omits the property if the key or value is not a string. + * + * @param h the hashtable + */ + private synchronized void enumerateStringProperties(Map h) { + if (defaults != null) { + defaults.enumerateStringProperties(h); + } + for (Enumeration e = keys(); e.hasMoreElements(); ) { + Object k = e.nextElement(); + Object v = get(k); + if (k instanceof String && v instanceof String) { + h.put((String) k, (String) v); + } + } + } + + /** + * Convert a nibble to a hex character + * + * @param nibble the nibble to convert. + */ + private static char toHex(int nibble) { + return hexDigit[(nibble & 0xF)]; + } + + /** + * A table of hex digits + */ + private static final char[] hexDigit = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; } diff --git a/src/main/java/net/locusworks/common/utils/Checks.java b/src/main/java/net/locusworks/common/utils/Checks.java index af2bab6..ddc23bd 100644 --- a/src/main/java/net/locusworks/common/utils/Checks.java +++ b/src/main/java/net/locusworks/common/utils/Checks.java @@ -3,25 +3,25 @@ package net.locusworks.common.utils; import java.util.Objects; public class Checks { - - public static void checkArguments(boolean expression, String error) { - checkArguments(expression, "%s", error); - } - - public static void checkArguments(boolean expression, String errorFmt, Object... args) { - if (!expression) throw new IllegalArgumentException(String.format(errorFmt, args)); - } - - public static void checkState(boolean expression, String error) { - checkState(expression, "%s", error); - } - - public static void checkState(boolean expression, String errorFmt, Object... args) { - if (!expression) throw new IllegalStateException(String.format(errorFmt, args)); - } - - public static void checkNotNull(Object item, String error) { - Objects.requireNonNull(item, error); - } + + public static void checkArguments(boolean expression, String error) { + checkArguments(expression, "%s", error); + } + + public static void checkArguments(boolean expression, String errorFmt, Object... args) { + if (!expression) throw new IllegalArgumentException(String.format(errorFmt, args)); + } + + public static void checkState(boolean expression, String error) { + checkState(expression, "%s", error); + } + + public static void checkState(boolean expression, String errorFmt, Object... args) { + if (!expression) throw new IllegalStateException(String.format(errorFmt, args)); + } + + public static void checkNotNull(Object item, String error) { + Objects.requireNonNull(item, error); + } } diff --git a/src/main/java/net/locusworks/common/utils/Constants.java b/src/main/java/net/locusworks/common/utils/Constants.java index 3b28c0c..86e8911 100644 --- a/src/main/java/net/locusworks/common/utils/Constants.java +++ b/src/main/java/net/locusworks/common/utils/Constants.java @@ -2,18 +2,19 @@ package net.locusworks.common.utils; /** * Class to hold final static constant values used across the system + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 */ public class Constants { - - public static final short TRUE = (short)1; - public static final short FALSE = (short)0; - - public static final short EXIT_SUCCESS = (short)0; - public static final short EXIT_FAIL = (short)1; - - public static final String LOG4J_CONFIG_PROPERTY = "log4j.configurationFile"; - public static final String JUNIT_TEST_CHECK = "junit.test"; + + public static final short TRUE = (short) 1; + public static final short FALSE = (short) 0; + + public static final short EXIT_SUCCESS = (short) 0; + public static final short EXIT_FAIL = (short) 1; + + public static final String LOG4J_CONFIG_PROPERTY = "log4j.configurationFile"; + public static final String JUNIT_TEST_CHECK = "junit.test"; } diff --git a/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java b/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java index e45612f..023089d 100644 --- a/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java +++ b/src/main/java/net/locusworks/common/utils/DataOutputStreamHelper.java @@ -10,41 +10,42 @@ import net.locusworks.common.Charsets; public class DataOutputStreamHelper extends DataOutputStream implements AutoCloseable { - public DataOutputStreamHelper() { - this(new ByteArrayOutputStream()); - } - - public DataOutputStreamHelper(OutputStream out) { - super(out); - } - - public byte[] toByteArray() { - if (super.out == null) { - return new byte[0]; + public DataOutputStreamHelper() { + this(new ByteArrayOutputStream()); } - if (super.out instanceof ByteArrayOutputStream) { - return ((ByteArrayOutputStream)super.out).toByteArray(); + + public DataOutputStreamHelper(OutputStream out) { + super(out); + } + + public byte[] toByteArray() { + if (super.out == null) { + return new byte[0]; + } + if (super.out instanceof ByteArrayOutputStream) { + return ((ByteArrayOutputStream) super.out).toByteArray(); + } + return super.out.toString().getBytes(Charsets.UTF_8); + } + + public String base64Encoded() { + return Base64.getEncoder().encodeToString(this.toByteArray()); + } + + @Override + public String toString() { + return new String(this.toByteArray(), Charsets.UTF_8); + } + + @Override + public void close() throws IOException { + if (super.out != null) { + try { + super.out.close(); + super.out = null; + } catch (Exception ignored) { + } + } } - return super.out.toString().getBytes(Charsets.UTF_8); - } - - public String base64Encoded() { - return Base64.getEncoder().encodeToString(this.toByteArray()); - } - - @Override - public String toString() { - return new String(this.toByteArray(), Charsets.UTF_8); - } - - @Override - public void close() throws IOException { - if (super.out != null) { - try { - super.out.close(); - super.out = null; - } catch (Exception ignored) {} - } - } } diff --git a/src/main/java/net/locusworks/common/utils/DateTimeStampDeserializer.java b/src/main/java/net/locusworks/common/utils/DateTimeStampDeserializer.java index 957b223..2118082 100644 --- a/src/main/java/net/locusworks/common/utils/DateTimeStampDeserializer.java +++ b/src/main/java/net/locusworks/common/utils/DateTimeStampDeserializer.java @@ -18,90 +18,94 @@ import com.fasterxml.jackson.databind.JsonDeserializer; * * Will specify to use this deserializer class when a json field {@code purgeEndDate} is encountered in the json * string and will try to convert the string into a date object and inject the value back into the class + * * @author Isaac Parenteau * @version 1.0 * @date 02/15/2018 * @see com.fasterxml.jackson.databind.annotation.JsonDeserialize */ public class DateTimeStampDeserializer extends JsonDeserializer { - - private static final String DEFAULT = "MM/dd/yyyy"; - private static final String EXPANDED = "MM/dd/yyyy HH:mm:ss z"; - private static final String EXPANDED_WITH_TIMEZONE = "MMM d, yyyy HH:mm:ss z"; - private static final String EXPANDED_WITH_AM_PM = "MMM d, yyyy h:mm:ss a"; - - private static final String[] formats = new String[] { - DEFAULT, - EXPANDED, - EXPANDED_WITH_TIMEZONE, - EXPANDED_WITH_AM_PM, - }; - - private static final Integer[] styles = new Integer[] { - SimpleDateFormat.LONG, - SimpleDateFormat.FULL, - SimpleDateFormat.MEDIUM, - SimpleDateFormat.SHORT - }; - @Override - public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - String value = p.getText(); - - Date date = null; - //First try to see if the value can be parsed into a long - try { - date = new Date(Long.parseLong(value)); - return date; - } catch (Exception ex) { } - - //Next iterate over the built in styles to see if it can be converted - for (Integer style: styles) { - date = formatDate(style, value); - if (date != null) { - return date; - } + private static final String DEFAULT = "MM/dd/yyyy"; + private static final String EXPANDED = "MM/dd/yyyy HH:mm:ss z"; + private static final String EXPANDED_WITH_TIMEZONE = "MMM d, yyyy HH:mm:ss z"; + private static final String EXPANDED_WITH_AM_PM = "MMM d, yyyy h:mm:ss a"; + + private static final String[] formats = new String[]{ + DEFAULT, + EXPANDED, + EXPANDED_WITH_TIMEZONE, + EXPANDED_WITH_AM_PM, + }; + + private static final Integer[] styles = new Integer[]{ + SimpleDateFormat.LONG, + SimpleDateFormat.FULL, + SimpleDateFormat.MEDIUM, + SimpleDateFormat.SHORT + }; + + @Override + public Date deserialize(JsonParser p, DeserializationContext context) throws IOException { + String value = p.getText(); + + Date date; + //First, try to see if the value can be parsed into along + try { + date = new Date(Long.parseLong(value)); + return date; + } catch (Exception ignored) { + } + + //Next, iterate over the built-in styles to see if it can be converted + for (Integer style : styles) { + date = formatDate(style, value); + if (date != null) { + return date; + } + } + + //Lastly, iterate over the custom styles specified in format to see if it can be converted + for (String fmt : formats) { + date = formatDate(fmt, value); + if (date != null) { + return date; + } + } + + //Return null if date format can't be converted + return null; } - - //Lastly iterate over the custom styles specified in format to see if it can be converted - for (String fmt : formats) { - date = formatDate(fmt, value); - if (date != null) { - return date; - } + + /** + * Convert a string value to a date object + * + * @param format The format to use in reference to the source + * @param source the source to convert + * @return Date object if the conversion was success; null otherwise + */ + private static Date formatDate(String format, String source) { + try { + return new SimpleDateFormat(format).parse(source); + } catch (Exception ex) { + return null; + } } - - //Return null if date format can't be converted - return null; - } - - /** - * Convert a string value to a date object - * @param format The format to use in reference to the source - * @param source the source to convert - * @return Date object if the conversion was success; null otherwise - */ - private static Date formatDate(String format, String source) { - try { - return new SimpleDateFormat(format).parse(source); - } catch (Exception ex) { - return null; + + /** + * Convert a string value to a date object using SimpleDateFormats + * built in styles + * + * @param style The style to use + * @param source the source to convert + * @return Date object if the conversion was success; null otherwise + */ + private static Date formatDate(Integer style, String source) { + try { + return SimpleDateFormat.getDateInstance(style).parse(source); + } catch (Exception ex) { + return null; + } } - } - - /** - * Convert a string value to a date object using SimpleDateFormats - * built in styles - * @param style The style to use - * @param source the source to convert - * @return Date object if the conversion was success; null otherwise - */ - private static Date formatDate(Integer style, String source) { - try { - return SimpleDateFormat.getDateInstance(style).parse(source); - } catch (Exception ex) { - return null; - } - } } diff --git a/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java b/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java index 88bef4e..ebb1d96 100644 --- a/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java +++ b/src/main/java/net/locusworks/common/utils/DateTimeStampSerializer.java @@ -19,29 +19,31 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; * * Will specify to use this serializer class when the {@code purgeEndDate} is encountered when converting to json * and will try to convert the date object to its timestamp equivalent + * * @author Isaac Parenteau - * @date 02/15/2018 * @version 1.0 + * @date 02/15/2018 * @see com.fasterxml.jackson.databind.annotation.JsonSerialize * @see com.fasterxml.jackson.databind.ser.std.StdSerializer */ public class DateTimeStampSerializer extends StdSerializer { - /** - * - */ - @Serial private static final long serialVersionUID = -4753139740916300831L; + /** + * + */ + @Serial + private static final long serialVersionUID = -4753139740916300831L; - public DateTimeStampSerializer() { - this(null); - } - - public DateTimeStampSerializer(Class t) { - super(t); - } + public DateTimeStampSerializer() { + this(null); + } - @Override - public void serialize(Date date, JsonGenerator generator, SerializerProvider provider) throws IOException { - generator.writeNumber(date.getTime()); - } + public DateTimeStampSerializer(Class t) { + super(t); + } + + @Override + public void serialize(Date date, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeNumber(date.getTime()); + } } diff --git a/src/main/java/net/locusworks/common/utils/FileReader.java b/src/main/java/net/locusworks/common/utils/FileReader.java index 63211e3..532de61 100644 --- a/src/main/java/net/locusworks/common/utils/FileReader.java +++ b/src/main/java/net/locusworks/common/utils/FileReader.java @@ -1,6 +1,7 @@ package net.locusworks.common.utils; import static net.locusworks.common.Charsets.UTF_8; +import static net.locusworks.common.utils.ObjectUtils.getResourceStream; import java.io.BufferedReader; import java.io.File; @@ -11,204 +12,212 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Objects; import net.locusworks.common.interfaces.AutoCloseableIterator; /** * Class to read in a file that can be used in the try-with-resource block + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 */ public class FileReader implements AutoCloseableIterator, Iterable, AutoCloseable { - private BufferedReader reader; - private LineInfo info; - private Integer lineNumber; - - /** - * Constructor - * @param fileName Name of the file to read - */ - public FileReader(String fileName) { - init(fileName); - } - - /** - * Constructor - * @param file File to read - */ - @Deprecated - public FileReader(File file) { - init(file.toPath()); - } - - /** - * Constructor - * @param file File to read - */ - public FileReader(Path file) { - init(file); - } - - /** - * Constructor - * @param reader Buffered reader to read data from - */ - public FileReader(BufferedReader reader) { - init(reader); - } - - /** - * Initialization helper - * @param fileName Name of the file to load - * This will look into the resources directory if it cannot - * find the file directly. - */ - private void init(String fileName) { - //check to see if the file exists - Path f = Paths.get(fileName); - if (Files.exists(f)) { - init(f); //If it does. load through the file initializer - return; - } - - //Check to see if the file is in the resources directory - InputStream is = this.getClass().getResourceAsStream(fileName); - if (is == null) { - is = this.getClass().getClassLoader().getResourceAsStream(fileName); - } - //If it cant be found, throw a runtime exception - if (is == null) { - throw new IllegalArgumentException("Unable to find resource with name of" + fileName); - } - - BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8)); - init(br); - } - - /** - * Initializer helper to load file - * @param file File to load - */ - private void init(Path file) { - if (file == null) throw new IllegalArgumentException("File cannot be null"); - if (Files.notExists(file)) throw new IllegalArgumentException("File " + file + " does not exist"); - if (!Files.isRegularFile(file)) throw new IllegalArgumentException("File " + file + " is not a file"); - try { - BufferedReader br = Files.newBufferedReader(file); - init(br); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - /** - * Initializer helper for buffered reader - * This is ultimately where all initializers end as a buffered reader - * @param reader buffered reader to load - */ - private void init(BufferedReader reader) { - this.reader = reader; - this.lineNumber = 0; - } - - @Override - public boolean hasNext() { - try { - String line = this.reader.readLine(); - if (line == null) { - this.close(); - this.info = null; - return false; - } - this.lineNumber++; - this.info = new LineInfo(this.lineNumber, line); - return true; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - @Override - public LineInfo next() { - if (this.info == null) { - throw new NoSuchElementException("Call to next was initiated but there are no more elements to read"); - } - return this.info; - } - - @Override - public Iterator iterator() { - return this; - } - - @Override - public void close() { - if (this.reader != null) { - try { - this.reader.close(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - } - - public static class LineInfo { + private BufferedReader reader; + private LineInfo info; private Integer lineNumber; - private Integer lineLength; - private String line; /** - * @param lineNumber the current line number in the file - * @param line the line information from the file + * Constructor + * + * @param fileName Name of the file to read */ - public LineInfo(Integer lineNumber, String line) { - this.lineNumber = lineNumber; - this.line = line; - this.lineLength = line.length(); + public FileReader(String fileName) { + init(fileName); } /** - * @return the lineNumber + * Constructor + * + * @param file File to read */ - public Integer getLineNumber() { - return lineNumber; + @Deprecated + public FileReader(File file) { + init(file.toPath()); } /** - * @param lineNumber the lineNumber to set + * Constructor + * + * @param file File to read */ - public void setLineNumber(Integer lineNumber) { - this.lineNumber = lineNumber; + public FileReader(Path file) { + init(file); } /** - * @return the lineLength + * Constructor + * + * @param reader Buffered reader to read data from */ - public Integer getLineLength() { - return lineLength; + public FileReader(BufferedReader reader) { + init(reader); } /** - * @param lineLength the lineLength to set + * Initialization helper + * + * @param fileName Name of the file to load + * This will look into the resources directory if it cannot + * find the file directly. */ - public void setLineLength(Integer lineLength) { - this.lineLength = lineLength; + private void init(String fileName) { + if (fileName == null) { + throw new IllegalArgumentException("File name cannot be null"); + } + //check to see if the file exists + Path f = Paths.get(fileName); + if (Files.exists(f)) { + init(f); //If it does. load through the file initializer + return; + } + + //Check to see if the file is in the resources directory + InputStream is = getResourceStream(fileName); + + BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8)); + init(br); } /** - * @return the line + * Initializer helper to load file + * + * @param file File to load */ - public String getLine() { - return line; + private void init(Path file) { + if (file == null) throw new IllegalArgumentException("File cannot be null"); + if (Files.notExists(file)) throw new IllegalArgumentException("File " + file + " does not exist"); + if (!Files.isRegularFile(file)) throw new IllegalArgumentException("File " + file + " is not a file"); + try { + BufferedReader br = Files.newBufferedReader(file); + init(br); + } catch (Exception ex) { + throw new RuntimeException(ex); + } } /** - * @param line the line to set + * Initializer helper for buffered reader + * This is ultimately where all initializers end as a buffered reader + * + * @param reader buffered reader to load */ - public void setLine(String line) { - this.line = line; + private void init(BufferedReader reader) { + if (reader == null) { + throw new IllegalArgumentException("Buffered Reader cannot be null"); + } + this.reader = reader; + this.lineNumber = 0; + } + + @Override + public boolean hasNext() { + try { + String line = this.reader.readLine(); + if (line == null) { + this.close(); + this.info = null; + return false; + } + this.lineNumber++; + this.info = new LineInfo(this.lineNumber, line); + return true; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + @Override + public LineInfo next() { + if (this.info == null) { + throw new NoSuchElementException("Call to next was initiated but there are no more elements to read"); + } + return this.info; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public void close() { + if (this.reader != null) { + try { + this.reader.close(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } + + public static class LineInfo { + private Integer lineNumber; + private Integer lineLength; + private String line; + + /** + * @param lineNumber the current line number in the file + * @param line the line information from the file + */ + public LineInfo(Integer lineNumber, String line) { + this.lineNumber = lineNumber; + this.line = line; + this.lineLength = line.length(); + } + + /** + * @return the lineNumber + */ + public Integer getLineNumber() { + return lineNumber; + } + + /** + * @param lineNumber the lineNumber to set + */ + public void setLineNumber(Integer lineNumber) { + this.lineNumber = lineNumber; + } + + /** + * @return the lineLength + */ + public Integer getLineLength() { + return lineLength; + } + + /** + * @param lineLength the lineLength to set + */ + public void setLineLength(Integer lineLength) { + this.lineLength = lineLength; + } + + /** + * @return the line + */ + public String getLine() { + return line; + } + + /** + * @param line the line to set + */ + public void setLine(String line) { + this.line = line; + } } - } } diff --git a/src/main/java/net/locusworks/common/utils/HashUtils.java b/src/main/java/net/locusworks/common/utils/HashUtils.java index 054d180..c5ffc68 100644 --- a/src/main/java/net/locusworks/common/utils/HashUtils.java +++ b/src/main/java/net/locusworks/common/utils/HashUtils.java @@ -13,176 +13,188 @@ import java.security.MessageDigest; /** * Wrapper class that leverages java's MessageDigest to hash files + * * @author Isaac Parenteau * */ public class HashUtils { - - private static final Charset UTF_8 = StandardCharsets.UTF_8; - /** - * Used to build output as Hex - */ - private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + private static final Charset UTF_8 = StandardCharsets.UTF_8; - /** - * Used to build output as Hex - */ - private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - /** - * Size of the streaming buffer - */ - public static final Integer STREAM_BUFFER_LENGTH = 1024; + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - /** - * Hash a string literal - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data String to hash - * @return hash value of the string literal - */ - public static String hash(String hashType, String data) { - return hash(hashType, data, true); - } - - /** - * Hash a string literal - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data String to hash - * @param toLower True to output the hash in lower case. False to output in upper case - * @return hash value of the string literal - */ - public static String hash(String hashType, String data, boolean toLower) { - byte[] stringData = data.getBytes(UTF_8); - return hash(hashType, stringData, toLower); - } - - @Deprecated - public static String hash(String hashType, File data) { - return hash(hashType, data.toPath()); - } + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - /** - * Hash a file - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data File to hash - * @return hash value of the file - */ - public static String hash(String hashType, Path data) { - return hash(hashType, data, true); - } - - @Deprecated - public static String hash(String hashType, File data, boolean toLower) { - return hash(hashType, data.toPath(), toLower); - } + /** + * Size of the streaming buffer + */ + public static final Integer STREAM_BUFFER_LENGTH = 1024; - /** - * Hash a file - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data File to hash - * @param toLower True to output the hash in lower case. False to output in upper case - * @return hash value of the file - */ - public static String hash(String hashType, Path data, boolean toLower) { - try (InputStream stream = Files.newInputStream(data)) { - return hash(stream, hashType, toLower); - } catch (IOException ex) { - throw new IllegalArgumentException(ex.getMessage()); + /** + * Hash a string literal + * + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param data String to hash + * @return hash value of the string literal + */ + public static String hash(String hashType, String data) { + return hash(hashType, data, true); } - } - - /** - * Hash a byte array - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data data to hash - * @return hash value of the data - */ - public static String hash(String hashType, byte[] data) { - return hash(hashType, data, true); - } - - /** - * Hash a byte array - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param data data to hash - * @param toLower True to output the hash in lower case. False to output in upper case - * @return hash value of the data - */ - public static String hash(String hashType, byte[] data, boolean toLower) { - return hash(new BufferedInputStream(new ByteArrayInputStream(data)), hashType, toLower); - } - - /** - * Hash an input stream - * @param stream Stream with the data to hash - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @return Hash value of the input stream - */ - public static String hash(InputStream stream, String hashType) { - return hash(stream, hashType, true); - } - - /** - * Hash an input stream - * @param stream Stream with the data to hash - * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) - * @param toLower True to output the hash in lower case. False to output in upper case - * @return Hash value of the input stream - */ - public static String hash(InputStream stream, String hashType, boolean toLower) { - MessageDigest digest = null; - try(InputStream is = stream) { - digest = MessageDigest.getInstance(hashType); - - byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; - int read = is.read(buffer, 0, STREAM_BUFFER_LENGTH); - while (read > -1) { - digest.update(buffer, 0, read); - read = is.read(buffer, 0, STREAM_BUFFER_LENGTH); - } - - return encodeHexString(digest.digest(), toLower); - } catch (Exception ex) { - throw new IllegalArgumentException(ex.getMessage()); - } - } - - /** - * Encode the hash data back to a string - * @param data Data to encode - * @param toLower output to lower case - * @return - */ - private static String encodeHexString(byte[] data, boolean toLower) { - return new String(encodeHex(data, toLower)); - } - - /** - * Encode the hash data to a character array - * @param data Data to encode - * @param toLower output to lower case - * @return - */ - private static char[] encodeHex(byte[] data, boolean toLower) { - return encodeHex(data, toLower ? DIGITS_LOWER : DIGITS_UPPER); - } - - /** - * Encode the hex to a character array - * @param data Data to encode - * @param toDigits digits to use - * @return - */ - private static char[] encodeHex(byte[] data, char[] toDigits) { - int l = data.length; - char[] out = new char[l << 1]; - // two characters form the hex value. - for (int i = 0, j = 0; i < l; i++) { - out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; - out[j++] = toDigits[0x0F & data[i]]; + /** + * Hash a string literal + * + * @param hashType Hash types supported by MessageDigest (i.e., MD5, SHA-1, SHA-512) + * @param data String to hash + * @param toLower True to output the hash in lower case. False to output in the upper case + * @return hash value of the string literal + */ + public static String hash(String hashType, String data, boolean toLower) { + byte[] stringData = data.getBytes(UTF_8); + return hash(hashType, stringData, toLower); + } + + @Deprecated + public static String hash(String hashType, File data) { + return hash(hashType, data.toPath()); + } + + /** + * Hash a file + * + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param data File to hash + * @return hash value of the file + */ + public static String hash(String hashType, Path data) { + return hash(hashType, data, true); + } + + @Deprecated + public static String hash(String hashType, File data, boolean toLower) { + return hash(hashType, data.toPath(), toLower); + } + + /** + * Hash a file + * + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param data File to hash + * @param toLower True to output the hash in lower case. False to output in upper case + * @return hash value of the file + */ + public static String hash(String hashType, Path data, boolean toLower) { + try (InputStream stream = Files.newInputStream(data)) { + return hash(stream, hashType, toLower); + } catch (IOException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } + } + + /** + * Hash a byte array + * + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param data data to hash + * @return hash value of the data + */ + public static String hash(String hashType, byte[] data) { + return hash(hashType, data, true); + } + + /** + * Hash a byte array + * + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param data data to hash + * @param toLower True to output the hash in lower case. False to output in upper case + * @return hash value of the data + */ + public static String hash(String hashType, byte[] data, boolean toLower) { + return hash(new BufferedInputStream(new ByteArrayInputStream(data)), hashType, toLower); + } + + /** + * Hash an input stream + * + * @param stream Stream with the data to hash + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @return Hash value of the input stream + */ + public static String hash(InputStream stream, String hashType) { + return hash(stream, hashType, true); + } + + /** + * Hash an input stream + * + * @param stream Stream with the data to hash + * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) + * @param toLower True to output the hash in lower case. False to output in upper case + * @return Hash value of the input stream + */ + public static String hash(InputStream stream, String hashType, boolean toLower) { + MessageDigest digest = null; + try (InputStream is = stream) { + digest = MessageDigest.getInstance(hashType); + + byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; + int read = is.read(buffer, 0, STREAM_BUFFER_LENGTH); + + while (read > -1) { + digest.update(buffer, 0, read); + read = is.read(buffer, 0, STREAM_BUFFER_LENGTH); + } + + return encodeHexString(digest.digest(), toLower); + } catch (Exception ex) { + throw new IllegalArgumentException(ex.getMessage()); + } + } + + /** + * Encode the hash data back to a string + * + * @param data Data to encode + * @param toLower output to lower case + * @return encoded string + */ + private static String encodeHexString(byte[] data, boolean toLower) { + return new String(encodeHex(data, toLower)); + } + + /** + * Encode the hash data to a character array + * + * @param data Data to encode + * @param toLower output to lower case + * @return encoded character array + */ + private static char[] encodeHex(byte[] data, boolean toLower) { + return encodeHex(data, toLower ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Encode the hex to a character array + * + * @param data Data to encode + * @param toDigits digits to use + * @return encoded character array + */ + private static char[] encodeHex(byte[] data, char[] toDigits) { + int l = data.length; + char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; } - return out; - } } diff --git a/src/main/java/net/locusworks/common/utils/ObjectUtils.java b/src/main/java/net/locusworks/common/utils/ObjectUtils.java new file mode 100644 index 0000000..88164ef --- /dev/null +++ b/src/main/java/net/locusworks/common/utils/ObjectUtils.java @@ -0,0 +1,38 @@ +package net.locusworks.common.utils; + +import java.io.InputStream; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Object Utils. Functions to perform on objects + */ +public class ObjectUtils { + + /** + * Perform an action if an object is not null otherwise do nothing. + * + * @param object the object to test + * @param consumer the consumer to perform. + * @param the type + */ + public static void performIfNotNull(T object, Consumer consumer) { + if (Objects.isNull(object)) { + return; + } + consumer.accept(object); + } + + public static InputStream getResourceStream(String resourceName) { + InputStream is = ObjectUtils.class.getResourceAsStream(resourceName); + if (is == null) { + is = ObjectUtils.class.getClassLoader().getResourceAsStream(resourceName); + } + + if (is == null) { + throw new NullPointerException("Unable to find resource with name of " + resourceName); + } + return is; + } + +} diff --git a/src/main/java/net/locusworks/common/utils/RandomString.java b/src/main/java/net/locusworks/common/utils/RandomString.java index 86b041f..a238633 100644 --- a/src/main/java/net/locusworks/common/utils/RandomString.java +++ b/src/main/java/net/locusworks/common/utils/RandomString.java @@ -9,68 +9,68 @@ import static net.locusworks.common.Charsets.UTF_8; public class RandomString { - public static final String LOWER = "abcdefghijklmnopqrstuvwxyz"; - public static final String UPPER = LOWER.toUpperCase(); - public static final String DIGITS = "0123456789"; - - public static final String ALPHA_NUMERIC = LOWER + UPPER + DIGITS; - - private Random random; - - private char[] symbols; + public static final String LOWER = "abcdefghijklmnopqrstuvwxyz"; + public static final String UPPER = LOWER.toUpperCase(); + public static final String DIGITS = "0123456789"; - private static RandomString instance; + public static final String ALPHA_NUMERIC = LOWER + UPPER + DIGITS; - private RandomString() { - Random random; - try { - random = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - random = new SecureRandom(); - } - init(random); - } + private Random random; - private RandomString(Random random) { - init(random); - } + private char[] symbols; - private void init(Random random) { - this.random = Objects.requireNonNull(random, "Random generator cannot be null"); - this.symbols = ALPHA_NUMERIC.toCharArray(); - } + private static RandomString instance; - private String nextString(int length) { - if (length < 1) throw new IllegalArgumentException("String Length has to be greater than 0"); - char[] buffer = new char[length]; - for (int index = 0; index < buffer.length; index++) { - buffer[index] = symbols[random.nextInt(symbols.length)]; + private RandomString() { + Random random; + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + random = new SecureRandom(); + } + init(random); } - return new String(buffer); - } - - public String getString(Integer length) { - return this.nextString(length); - } - public byte[] getBytes(Integer length) { - return getString(length).getBytes(UTF_8); - } - - public static RandomString getInstance() { - if (instance == null) { - instance = new RandomString(); + private RandomString(Random random) { + init(random); } - return instance; - } - public static RandomString newInstance() { - instance = new RandomString(); - return instance; - } + private void init(Random random) { + this.random = Objects.requireNonNull(random, "Random generator cannot be null"); + this.symbols = ALPHA_NUMERIC.toCharArray(); + } - public static RandomString newInstance(Random random) { - instance = new RandomString(random); - return instance; - } + private String nextString(int length) { + if (length < 1) throw new IllegalArgumentException("String Length has to be greater than 0"); + char[] buffer = new char[length]; + for (int index = 0; index < buffer.length; index++) { + buffer[index] = symbols[random.nextInt(symbols.length)]; + } + return new String(buffer); + } + + public String getString(Integer length) { + return this.nextString(length); + } + + public byte[] getBytes(Integer length) { + return getString(length).getBytes(UTF_8); + } + + public static RandomString getInstance() { + if (instance == null) { + instance = new RandomString(); + } + return instance; + } + + public static RandomString newInstance() { + instance = new RandomString(); + return instance; + } + + public static RandomString newInstance(Random random) { + instance = new RandomString(random); + return instance; + } } diff --git a/src/main/java/net/locusworks/common/utils/Splitter.java b/src/main/java/net/locusworks/common/utils/Splitter.java index 5b2ba8f..168584d 100644 --- a/src/main/java/net/locusworks/common/utils/Splitter.java +++ b/src/main/java/net/locusworks/common/utils/Splitter.java @@ -14,172 +14,182 @@ import static net.locusworks.common.utils.Checks.checkNotNull; * it can also split on new line or not. */ public class Splitter { - private enum SplitterType { - PARTITION, - SEQUENCE - } - private String splitSeq; - private boolean omitEmptyStrings = false; - private int partition; - private int limit; - private final SplitterType splitterType; - private static Splitter splitter; - - private Splitter(String seq) { - this.splitSeq = seq; - this.splitterType = SplitterType.SEQUENCE; - } - - private Splitter(int partition) { - this.partition = partition; - this.splitterType = SplitterType.PARTITION; - } - - /** - * Remove empty string from the resulting lists - * @return this - */ - public Splitter omitEmptyStrings() { - this.omitEmptyStrings = true; - return this; - } - - /** - * Return a subset of the resulting list - * @param limit how many items to retrieve - * @return this - */ - public Splitter withLimit(int limit) { - this.limit = limit; - return this; - } - - /** - * Return an array instead of a list - * @param sentence the string sentence to split - * @return this - */ - public String[] splitToArray(String sentence) { - List list = split(sentence); - return list.toArray(new String[0]); - } - - /** - * Split the string - * @param sentence the string to split - * @return the resulting list. - */ - public List split(String sentence) { - checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); - List list = new ArrayList<>(); - - if (splitterType == SplitterType.PARTITION) { - populateForFixedWidth(sentence, list); - } else { - populateForTrimmer(sentence, list); + private enum SplitterType { + PARTITION, + SEQUENCE } - return limit > 0 ? list.subList(0, limit) : list; - } + private String splitSeq; + private boolean omitEmptyStrings = false; + private int partition; + private int limit; + private final SplitterType splitterType; + private static Splitter splitter; - private void populateForFixedWidth(String sentence, List list) { - checkArguments(partition > 0, "Partition should be greater than 0"); - int strLength = sentence.length(); - for (int i = 0; i < strLength; i += partition) { - list.add(sentence.substring(i, Math.min(strLength, i + partition))); - } - } - - private void populateForTrimmer(String sentence, List list) { - checkNotNull(splitSeq, "Split value provided was null"); - for (String s : sentence.split(splitSeq)) { - if (omitEmptyStrings && s.trim().isEmpty()) - continue; - list.add(s.trim()); - } - } - - /** - * Split the string on fixed length partitions - * @param partition the length to split the string on - * @return this - */ - public static Splitter fixedLengthSplit(int partition) { - splitter = new Splitter(partition); - return splitter; - } - - /** - * Split the length on a specified string sequence - * @param split the sequence to split - * @return this - */ - public static Splitter on(String split) { - splitter = new Splitter(split); - return splitter; - } - - /** - * Split on new line sequence - * @return this - */ - public static Splitter onNewLine() { - return on("\\r?\\n"); - } - - /** - * Split on spaces - * @return this - */ - public static Splitter onSpace() { - return on(" "); - } - - /** - * Separator on what the key value is. return map - * @param separator the separator value - * @return this - */ - public MapSplitter withKeyValueSeparator(String separator) { - return new MapSplitter(this, separator); - } - - public static class MapSplitter { - - private final Splitter splitter; - private final String separator; - private boolean skipInvalid = false; - - private MapSplitter(Splitter splitter, String separator) { - checkNotNull(splitter, "Splitter cannot be null"); - checkArguments(!Utils.isEmptyString(separator), - "Key value separator cannot be empty or null"); - this.splitter = splitter; - this.separator = separator; + private Splitter(String seq) { + this.splitSeq = seq; + this.splitterType = SplitterType.SEQUENCE; } - public MapSplitter skipInvalidKeyValues() { - this.skipInvalid = true; - return this; + private Splitter(int partition) { + this.partition = partition; + this.splitterType = SplitterType.PARTITION; } - public Map split(String sentence) { - checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); - Map map = new LinkedHashMap<>(); + /** + * Remove empty string from the resulting lists + * + * @return this + */ + public Splitter omitEmptyStrings() { + this.omitEmptyStrings = true; + return this; + } - for (String s : splitter.split(sentence)) { - String[] keyValue = s.split(separator); - try { - checkArguments(keyValue.length == 2, "invalid length found for key value mapping"); - } catch (IllegalArgumentException ex) { - if (!skipInvalid) - throw ex; - continue; + /** + * Return a subset of the resulting list + * + * @param limit how many items to retrieve + * @return this + */ + public Splitter withLimit(int limit) { + this.limit = limit; + return this; + } + + /** + * Return an array instead of a list + * + * @param sentence the string sentence to split + * @return this + */ + public String[] splitToArray(String sentence) { + List list = split(sentence); + return list.toArray(new String[0]); + } + + /** + * Split the string + * + * @param sentence the string to split + * @return the resulting list. + */ + public List split(String sentence) { + checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); + List list = new ArrayList<>(); + + if (splitterType == SplitterType.PARTITION) { + populateForFixedWidth(sentence, list); + } else { + populateForTrimmer(sentence, list); } - map.put(keyValue[0], keyValue[1]); - } - return map; + return limit > 0 ? list.subList(0, limit) : list; + } + + private void populateForFixedWidth(String sentence, List list) { + checkArguments(partition > 0, "Partition should be greater than 0"); + int strLength = sentence.length(); + for (int i = 0; i < strLength; i += partition) { + list.add(sentence.substring(i, Math.min(strLength, i + partition))); + } + } + + private void populateForTrimmer(String sentence, List list) { + checkNotNull(splitSeq, "Split value provided was null"); + for (String s : sentence.split(splitSeq)) { + if (omitEmptyStrings && s.trim().isEmpty()) + continue; + list.add(s.trim()); + } + } + + /** + * Split the string on fixed length partitions + * + * @param partition the length to split the string on + * @return this + */ + public static Splitter fixedLengthSplit(int partition) { + splitter = new Splitter(partition); + return splitter; + } + + /** + * Split the length on a specified string sequence + * + * @param split the sequence to split + * @return this + */ + public static Splitter on(String split) { + splitter = new Splitter(split); + return splitter; + } + + /** + * Split on new line sequence + * + * @return this + */ + public static Splitter onNewLine() { + return on("\\r?\\n"); + } + + /** + * Split on spaces + * + * @return this + */ + public static Splitter onSpace() { + return on(" "); + } + + /** + * Separator on what the key value is. return map + * + * @param separator the separator value + * @return this + */ + public MapSplitter withKeyValueSeparator(String separator) { + return new MapSplitter(this, separator); + } + + public static class MapSplitter { + + private final Splitter splitter; + private final String separator; + private boolean skipInvalid = false; + + private MapSplitter(Splitter splitter, String separator) { + checkNotNull(splitter, "Splitter cannot be null"); + checkArguments(!Utils.isEmptyString(separator), + "Key value separator cannot be empty or null"); + this.splitter = splitter; + this.separator = separator; + } + + public MapSplitter skipInvalidKeyValues() { + this.skipInvalid = true; + return this; + } + + public Map split(String sentence) { + checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); + Map map = new LinkedHashMap<>(); + + for (String s : splitter.split(sentence)) { + String[] keyValue = s.split(separator); + try { + checkArguments(keyValue.length == 2, "invalid length found for key value mapping"); + } catch (IllegalArgumentException ex) { + if (!skipInvalid) + throw ex; + continue; + } + map.put(keyValue[0], keyValue[1]); + } + + return map; + } } - } } diff --git a/src/main/java/net/locusworks/common/utils/StreamUtils.java b/src/main/java/net/locusworks/common/utils/StreamUtils.java index 9cadb6e..9014f15 100644 --- a/src/main/java/net/locusworks/common/utils/StreamUtils.java +++ b/src/main/java/net/locusworks/common/utils/StreamUtils.java @@ -7,60 +7,65 @@ import java.util.stream.StreamSupport; /** * Utility class to make iterators streamable + * * @author Isaac Parenteau * @version 1.0.0 * @date 02/15/2018 */ public class StreamUtils { - - /** - * Convert a iterator to a stream - * @param iterator the iterator to convert - * @param the class type - * @return stream of the iterator - */ - public static Stream asStream(Iterator iterator) { - return asStream(iterator, false); - } - - public static Stream asStream(Iterable iterable) { - return asStream(iterable, false); - } - - /** - * Converts an array to a stream - * @param items the items to convert - * @param the class type - * @return stream of the array - */ - public static Stream asStream(T[] items) { - return asStream(Arrays.asList(items).iterator(), false); - } - - /** - * Converts an array to a stream - * @param items the items to convert - * @param parallel make the stream parallel if set to true - * @param the class type - * @return stream of the array - */ - public static Stream asStream(T[] items, boolean parallel) { - return asStream(Arrays.asList(items).iterator(), parallel); - } - - public static Stream asStream(Iterable iterable, boolean parallel) { - return StreamSupport.stream(iterable.spliterator(), parallel); - } - - /** - * Convert an iterator to a stream - * @param iterator iterator to convert - * @param parallel make the stream parallel if set to true. - * @param the class type - * @return stream of the iterator - */ - public static Stream asStream(Iterator iterator, boolean parallel) { - Iterable iterable = () -> iterator; - return StreamSupport.stream(iterable.spliterator(), parallel); - } + + /** + * Convert an iterator to a stream + * + * @param iterator the iterator to convert + * @param the class type + * @return stream of the iterator + */ + public static Stream asStream(Iterator iterator) { + return asStream(iterator, false); + } + + public static Stream asStream(Iterable iterable) { + return asStream(iterable, false); + } + + /** + * Converts an array to a stream + * + * @param items the items to convert + * @param the class type + * @return stream of the array + */ + public static Stream asStream(T[] items) { + return asStream(Arrays.asList(items).iterator(), false); + } + + /** + * Converts an array to a stream + * + * @param items the items to convert + * @param parallel make the stream parallel if set to true + * @param the class type + * @return stream of the array + */ + public static Stream asStream(T[] items, boolean parallel) { + return asStream(Arrays.asList(items).iterator(), parallel); + } + + public static Stream asStream(Iterable iterable, boolean parallel) { + return StreamSupport.stream(iterable.spliterator(), parallel); + } + + /** + * Convert an iterator to a stream + * + * @param iterator iterator to convert + * @param parallel make the stream parallel if set to true. + * @param the class type + * @return stream of the iterator + */ + public static Stream asStream(Iterator iterator, boolean parallel) { + Iterable iterable = () -> iterator; + return StreamSupport.stream(iterable.spliterator(), parallel); + } } diff --git a/src/main/java/net/locusworks/common/utils/Success.java b/src/main/java/net/locusworks/common/utils/Success.java index 0d6a2db..0560ef2 100644 --- a/src/main/java/net/locusworks/common/utils/Success.java +++ b/src/main/java/net/locusworks/common/utils/Success.java @@ -7,35 +7,35 @@ package net.locusworks.common.utils; * @date 02/15/2018 */ public class Success { - private boolean success = true; - private Object body; + private boolean success = true; + private final Object body; - protected Success() { - this(true, true); - } + protected Success() { + this(true, true); + } - protected Success(boolean success) { - this(success, success); - } - - public Success(boolean success, Object body) { - this.success = success; - this.body = body; - } + protected Success(boolean success) { + this(success, success); + } - public Object getBody() { - return body; - } + public Success(boolean success, Object body) { + this.success = success; + this.body = body; + } - public boolean getSuccess() { - return success; - } - - public static Success success() { - return new Success(); - } - - public static Success fail() { - return new Success(false); - } + public Object getBody() { + return body; + } + + public boolean getSuccess() { + return success; + } + + public static Success success() { + return new Success(); + } + + public static Success fail() { + return new Success(false); + } } diff --git a/src/main/java/net/locusworks/common/utils/Utils.java b/src/main/java/net/locusworks/common/utils/Utils.java index 14ad53e..bf501f2 100644 --- a/src/main/java/net/locusworks/common/utils/Utils.java +++ b/src/main/java/net/locusworks/common/utils/Utils.java @@ -23,970 +23,983 @@ import net.locusworks.common.interfaces.ThrowingConsumer; public class Utils { - /** - * Finds if all the values are equal - * @param or first value is equal to at least one other value - * @param values the values to compare - * @return true if the first value is equal to at least one other value; false otherwise - */ - @SafeVarargs - public static boolean areEqual(Boolean or, E... values) { - if (values.length < 2) { - throw new IllegalArgumentException("Not enough values to compare"); - } - - E firstVal = values[0]; - boolean equal = !or; - - for (int i = 1; i < values.length; i++) { - if (or) - equal |= firstVal.equals(values[i]); - else - equal &= firstVal.equals(values[i]); - } - - return equal; - } - - /** - * Checks to see if values entered are all equal - * @param the type parameter - * @param values the values - * - * @return boolean - */ - @SafeVarargs - public static boolean areEqual(E... values) { - return areEqual(false, values); - } - - /** - * Checks to see if a group of values are not valid. All values have to be invalid to return true - * One valid value will return false - * @param values Values to check - * @param the expected class type of the objects passed in. All objects need to be of same type - * @return true if all values are invalid, false otherwise - */ - @SuppressWarnings("unchecked") - public static boolean areNotValid(V... values) { - return !areValid(values); - } - - /** - * Checks to see if a group of values are valid. All values have to be valid to return true - * One invalid value will return false - * @param the expected class type of the objects passed in. All objects need to be of same type - * @param values the values to check - * @return true if the values are valid, false otherwise - */ - @SuppressWarnings("unchecked") - public static boolean areValid(V... values) { - return validateValues(values); - } - - /** - * Build a map class - * - * @param the type parameter - * @param the type parameter - * @param mapClass The map class to map to i.e. HashMap, TreeMap etc - * @param mapKey the map key - * @param mapValue the map value - * @param data The data to place in the map - * - * @return map The map with the objects - */ - @SuppressWarnings("unchecked") - public static Map buildMap(Class mapClass, Class mapKey, Class mapValue, Object... data) { - Objects.requireNonNull(mapKey, "Null key value"); - Objects.requireNonNull(mapValue, "Null map value"); - - Map results; - try { - results = (Map) mapClass.getDeclaredConstructor().newInstance(); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to create instance of " + mapClass.getSimpleName() + " e: " + ex.getMessage()); - } - - if (data.length % 2 != 0) { - throw new IllegalArgumentException("Odd number of arguments provided"); - } - - for(int i = 0; i < data.length; i+=2) { - Object key = data[i]; - Object value = data[i + 1]; - - if (!mapKey.isInstance(key)) { - throw new IllegalArgumentException("Key is not the correct instance. Expecting " + mapKey.getName() + " Received " + key.getClass().getName()); - } - - if (!mapValue.isInstance(value)) { - throw new IllegalArgumentException("Value is not the correct instance. Expecting " + mapValue.getName() + " Received " + value.getClass().getName()); - } - results.put((K) key, (V) value); - - } - return results; - } - - /** - * Builds a set of objects - * @param setClass Class type of the set to create - * @param setValue Class type of the objects being placed in the set - * @param Class type of the return object (should be the same as setValue) - * @param data Data to place inside the set - * @return Set filled with the data - * @deprecated since JDk11 can use Set.of - */ - @SuppressWarnings("unchecked") - public static Set buildSet(Class setClass, Class setValue, E... data) { - return buildSet(data); - } - - /** - * Builds a set of objects - * @param data the data to set - * @return the set - * @param the type - */ - @SafeVarargs public static Set buildSet(E... data) { - return Set.of(data); - } - - /** - * Public method of type HashMap called "buildStringHashMap". - * The method takes String parameters and a data parameter. - * The method returns a new HashMap that uses String types as key / value pairs. - * @param data - The data - * @return - Returns a HashMap using the data. - */ - public static HashMap buildStringHashMap(String... data) { - return new HashMap<>(buildMap(HashMap.class, String.class, String.class, (Object[]) data)); - } - - /** - * Clone list and makes it unmodifiable. - * - * @return unmodifiable list - */ - public static List cloneList(List list) { - return Collections.unmodifiableList(list); - } - - /** - * Clone object object. - * @param obj the obj - * @return the object - * @throws IllegalAccessException thrown when the filed cannot be access - * @throws InstantiationException Thrown when the object cannot be instantiated - */ - @SuppressWarnings("unchecked") - public static O cloneObject(O obj) - throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - Object clone = obj.getClass().getDeclaredConstructor().newInstance(); - for (Field field : obj.getClass().getDeclaredFields()) { - field.setAccessible(true); - if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())){ - continue; - } - - Class type = field.getType(); - - if (isPrimitiveOrWrapper(type)) { - field.set(clone, field.get(obj)); - } else { - field.set(clone, cloneObject(field.get(obj))); - } - } - return (O) clone; - } - - /*** - * Convert an object to a map. Checks to see if there is a MapValue annotation - * and uses the values found to convert the fields to the key and the field value as the value - * @param obj Object to convert - * @return A map representation of the object - * @throws Exception exception - */ - public static Map convertToMap(Object obj) throws Exception { - Class clazz = obj.getClass(); - Map map = new LinkedHashMap<>(); - - - - for (Field field : clazz.getDeclaredFields()) { - field.setAccessible(true); - boolean hasAnnotations = field.isAnnotationPresent(MapValue.class); - - String key = field.getName(); - Object value = field.get(obj); - if (value == null) continue; - - if (hasAnnotations) { - MapValue annotation = field.getDeclaredAnnotation(MapValue.class); - if (annotation.ignore()) { - continue; + /** + * Finds if all the values are equal + * + * @param or first value is equal to at least one other value + * @param values the values to compare + * @return true if the first value is equal to at least one other value; false otherwise + */ + @SafeVarargs + public static boolean areEqual(Boolean or, E... values) { + if (values.length < 2) { + throw new IllegalArgumentException("Not enough values to compare"); } - if (!isEmptyString(annotation.value())) { - key = annotation.value(); - } - } - map.put(key, value); - } + E firstVal = values[0]; + boolean equal = !or; - return map; - } - - /** - * Convert a generic map to Stringed map where the key and value are both strings - * @param map map to convert - * @param The expected class of the key - * @param The expected class of the value - * @return convertedMap - */ - public static Map convertToStringMap(Map map) { - Map convert = new LinkedHashMap<>(); - for (Entry item : map.entrySet()) { - convert.put(String.valueOf(item.getKey()), String.valueOf(item.getValue())); - } - - return convert; - } - - /** - * Converts an object toa string map; - * @param obj the object to convert - * @return the map - * @throws Exception thrown if something happens - */ - public static Map convertToStringMap(Object obj) throws Exception { - return convertToStringMap(convertToMap(obj)); - } - - /*** - * Takes an input list and converts it to another list denoted by what is grabbed from the fieldName - * @param fieldName Name of the field in the object to retrieve - * @param list the list to retrieve values from - * @param The expected class of the List - * @param The expected class of the Iterable list - * @return The converted list - */ - public static Set extractFieldToSet(String fieldName, Iterable list) { - return listToSet(extractFieldToList(fieldName, list)); - } - - /** - * Extract a field from an iterable object and place them in a set - * @param fieldName field to check - * @param list list to iterate - * @param The expected class of the Set - * @param The Expected class of the iterable list - * @return set with the desired items - */ - @SuppressWarnings("unchecked") - public static List extractFieldToList(String fieldName, Iterable list) { - List newSet = new ArrayList<>(); - Field field; - for (K item : Utils.safeIterable(list)) { - try { - field = getField(item.getClass(), fieldName); - - if (field == null) { - throw new IllegalArgumentException("Unable to find field with name " + fieldName); + for (int i = 1; i < values.length; i++) { + if (or) + equal |= firstVal.equals(values[i]); + else + equal &= firstVal.equals(values[i]); } - E val = (E)field.get(item); - newSet.add(val); - - } catch (Exception ex) { - throw new IllegalArgumentException(ex); - } + return equal; } - return newSet; - } - /** - * Filters out items in a list with the given parameters - * @param fieldName the field name to inspect for the filter value - * @param filter the filter value to retrieve - * @param list the list to inspect - * @param the expected class of the iterable list - * @return List of items that met the criteria of the filter - */ - @SuppressWarnings("unchecked") - public static List filterList(String fieldName, Object filter, Iterable list) { - List newList = new ArrayList<>(); + /** + * Checks to see if values entered are all equal + * + * @param the type parameter + * @param values the values + * @return boolean + */ + @SafeVarargs + public static boolean areEqual(E... values) { + return areEqual(false, values); + } - Field field; + /** + * Checks to see if a group of values are not valid. All values have to be invalid to return true + * One valid value will return false + * + * @param values Values to check + * @param the expected class type of the objects passed in. All objects need to be of same type + * @return true if all values are invalid, false otherwise + */ + @SuppressWarnings("unchecked") + public static boolean areNotValid(V... values) { + return !areValid(values); + } - for (E item : safeIterable(list)) { - try { - field = getField(item.getClass(), fieldName); + /** + * Checks to see if a group of values are valid. All values have to be valid to return true + * One invalid value will return false + * + * @param the expected class type of the objects passed in. All objects need to be of same type + * @param values the values to check + * @return true if the values are valid, false otherwise + */ + @SuppressWarnings("unchecked") + public static boolean areValid(V... values) { + return validateValues(values); + } - if (field == null) { - throw new IllegalArgumentException("Unable to find field with name " + fieldName); + /** + * Build a map class + * + * @param the type parameter + * @param the type parameter + * @param mapClass The map class to map to i.e. HashMap, TreeMap etc + * @param mapKey the map key + * @param mapValue the map value + * @param data The data to place in the map + * @return map The map with the objects + */ + @SuppressWarnings("unchecked") + public static Map buildMap(Class mapClass, Class mapKey, Class mapValue, Object... data) { + Objects.requireNonNull(mapKey, "Null key value"); + Objects.requireNonNull(mapValue, "Null map value"); + + Map results; + try { + results = (Map) mapClass.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to create instance of " + mapClass.getSimpleName() + " e: " + ex.getMessage()); } - E val = (E)field.get(item); - if (val.equals(filter)) { - newList.add(item); + if (data.length % 2 != 0) { + throw new IllegalArgumentException("Odd number of arguments provided"); } - } catch (Exception ex) { - throw new IllegalArgumentException(ex); - } + + for (int i = 0; i < data.length; i += 2) { + Object key = data[i]; + Object value = data[i + 1]; + + if (!mapKey.isInstance(key)) { + throw new IllegalArgumentException("Key is not the correct instance. Expecting " + mapKey.getName() + " Received " + key.getClass().getName()); + } + + if (!mapValue.isInstance(value)) { + throw new IllegalArgumentException("Value is not the correct instance. Expecting " + mapValue.getName() + " Received " + value.getClass().getName()); + } + results.put((K) key, (V) value); + + } + return results; } - return newList; - } - - /** - * Find a value within the list. - * Public method of type E called "findValue". - * The method takes a parameter of type E called "itemToFind" and a String parameter called "fieldName" and a parameter of type List called "list". - * The method loops through each item in the list and tries to get the class name and return it as an item. - * The method the takes each item and searches for it in the list. - * @param itemToFind - The Item to find - * @param fieldName - The field name where the item is in - * @param list - The list to search - * @param - The expected class of item to be found - * @param - The expected class of the Iterable list - * @return - Returns the value of the specified field or null if there is no value. - * - */ - @SuppressWarnings({"unlikely-arg-type" }) - public static E findValue(E itemToFind, String fieldName, List list) { - return findValues(itemToFind, fieldName, list).stream().findFirst().orElse(null); - } - - /** - * Find values list. - * Public method of type List called "findValues". - * The method takes an E type parameter called "valueToFind" and a String parameter called "fieldName" and a List parameter called "list". - * The method creates a new array list called "tmpList". - * The method then iterates through the list and gets all the classes as items. - * If it can't find the fields, it will throw an illegal argument exception. - * @param valueToFind - The value to find - * @param fieldName - The field name - * @param list - The list - * @param - The expected class of item to be found - * @param - The expected class of the Iterable list - * @return - Returns the list. - */ - @SuppressWarnings({"unlikely-arg-type" }) - public static List findValues(E valueToFind, String fieldName, List list) { - List extracted = extractFieldToList(fieldName, list); - return extracted.stream() - .filter(Objects::nonNull) - .filter(v -> v.equals(valueToFind)).collect(Collectors.toList()); - } - - /** - * Use reflection to get the field values - * @param clazz the class to find the field - * @param fieldName the field name - * @return field - */ - private static Field getField(Class clazz, String fieldName) { - Field field = null; - while(clazz != null) { - try { - field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); - break; - } catch(Exception ex) { - clazz = clazz.getSuperclass(); - } + /** + * Builds a set of objects + * + * @param setClass Class type of the set to create + * @param setValue Class type of the objects being placed in the set + * @param Class type of the return object (should be the same as setValue) + * @param data Data to place inside the set + * @return Set filled with the data + * @deprecated since JDk11 can use Set.of + */ + @SuppressWarnings("unchecked") + public static Set buildSet(Class setClass, Class setValue, E... data) { + return buildSet(data); } - return field; - } - - /** - * A replacement for String.format. Allows for to many parameters or too few - * Replaces {} in the string in order. - * - * @param message the message - * @param args the args - * - * @return string - */ - public static String formatString(String message, Object... args) { - String tmp = message; - - for(Object arg : args) { - tmp = tmp.replaceFirst("\\{\\}", String.valueOf(arg)); + /** + * Builds a set of objects + * + * @param data the data to set + * @param the type + * @return the set + */ + @SafeVarargs + public static Set buildSet(E... data) { + return Set.of(data); } - return tmp; - } - - /** - * Get a class name - * - * @param the type parameter - * @param obj the obj - * - * @return class name - */ - public static String getClassName(V obj) { - return getClassName(obj, false); - } - - /** - * Get a class name - * - * @param the type parameter - * @param obj the obj - * @param verbose the verbose - * - * @return class name - */ - public static String getClassName(V obj, boolean verbose) { - return obj == null ? "Unknown" : (verbose ? obj.getClass().getName() : obj.getClass().getSimpleName()); - } - - /** - * Get a value from a map - * - * @param the type parameter - * @param the type parameter - * @param map the map with values - * @param key the key to find - * - * @return map value - */ - public static T getMapValue(Map map, K key) { - return getMapValue(map, key, null); - } - - /** - * Get a value from a map - * - * @param the type parameter - * @param the type parameter - * @param map the map with the values - * @param key the key to find - * @param defaultValue the default value if the key is not found - * - * @return map value - */ - public static T getMapValue(Map map, K key, T defaultValue) { - return map.getOrDefault(key, defaultValue); - } - - /** - * Checks to see if the proved string is empty - * new String(null) = true - * new String("") = true - * new String(" ") = true - * new String("foo") = false - * new String(" bar ") = false - * @param string String to check - * @return true if the string is empty false otherwise - */ - public static boolean isEmptyString(String string) { - return string == null || string.trim().isEmpty(); - } - - /** - * Checks to see if a passed in value is not a valid value - * For Strings checks to see if they are empty or not null - * For collections checks to see if they are null or empty - * for all others just checks if they are null - * @param the expected class type of the object - * @param values The value to check - * @return true if the value is not valid, false otherwise - */ - @SafeVarargs - public static boolean isNotValid(V... values) { - return !validateValues(values); - } - - /** - * Checks to see if a passed in value is a valid value - * For Strings checks to see if they are not empty or not null - * For collections checks to see if they are not null and not empty - * for all others just checks if they are not null - * @param the expected class type of the object - * @param values The value to check - * @return true if the value is valid, false otherwise - */ - @SafeVarargs - public static boolean isValid(V... values) { - return validateValues(values); - } - - /** - * Is primitive or wrapper boolean. - * - * @param clazz the clazz - * - * @return the boolean - */ - public static boolean isPrimitiveOrWrapper(Class clazz) { - return clazz.isPrimitive() || getWrapperTypes().contains(clazz); - } - - /** - * Converts a list of items into a map. - * - * @param the type parameter - * @param the type parameter - * @param keyFieldName the name of the field that will be used as the key - * @param list the list to convert - * - * @return a map - */ - @SuppressWarnings("unchecked") - public static Map listToMap(String keyFieldName, List list) { - Map map = new HashMap<>(); - - for(V value : Utils.safeList(list)) { - try { - Field field = getField(value.getClass(), keyFieldName); - K key = (K) field.get(value); - map.put(key, value); - } catch(Exception e) { - throw new IllegalArgumentException(String.format("Unable to find field %s. -> %s", keyFieldName, e.getMessage())); - } + /** + * Public method of type HashMap called "buildStringHashMap". + * The method takes String parameters and a data parameter. + * The method returns a new HashMap that uses String types as key / value pairs. + * + * @param data - The data + * @return - Returns a HashMap using the data. + */ + public static HashMap buildStringHashMap(String... data) { + return new HashMap<>(buildMap(HashMap.class, String.class, String.class, (Object[]) data)); } - return map; - } - - /** - * List to set. - * - * @param the type parameter - * @param valueSet the value set - * - * @return the set - */ - public static Set listToSet(Collection valueSet) { - return toSet(valueSet); - } - - /** - * Creates an array from a collection - * Public method of type array called "makeArray". - * The method takes a Collection object parameter called "collection" and a Class object parameter called "clazz". - * The method first checks to see if the Collection object is null. If so, it returns null. - * The method then creates an array called "results" and populates it with a new instance of "clazz" and the size of the collection. - * The method then creates an Int variable called "index" and sets the value to zero. - * The method then iterates through the collection and adds all the items to the results array. - * The method then returns the results array. - * @param - The type parameter - * @param collection - The collection - * @param clazz - The clazz - * @return e[] - Returns the array of results. - */ - @SuppressWarnings("unchecked") - public static E[] makeArray(Collection collection, Class clazz) { - if (collection == null) { - return null; + /** + * Clone list and makes it unmodifiable. + * + * @return unmodifiable list + */ + public static List cloneList(List list) { + return Collections.unmodifiableList(list); } - E[] results = (E[])Array.newInstance(clazz, collection.size()); + /** + * Clone object object. + * + * @param obj the obj + * @return the object + * @throws IllegalAccessException thrown when the filed cannot be access + * @throws InstantiationException Thrown when the object cannot be instantiated + */ + @SuppressWarnings("unchecked") + public static O cloneObject(O obj) + throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Object clone = obj.getClass().getDeclaredConstructor().newInstance(); + for (Field field : obj.getClass().getDeclaredFields()) { + field.setAccessible(true); + if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())) { + continue; + } - int index = 0; - for (E item : collection) { - results[index++] = item; + Class type = field.getType(); + + if (isPrimitiveOrWrapper(type)) { + field.set(clone, field.get(obj)); + } else { + field.set(clone, cloneObject(field.get(obj))); + } + } + return (O) clone; } - return results; - } + /*** + * Convert an object to a map. Checks to see if there is a MapValue annotation + * and uses the values found to convert the fields to the key and the field value as the value + * @param obj Object to convert + * @return A map representation of the object + * @throws Exception exception + */ + public static Map convertToMap(Object obj) throws Exception { + Class clazz = obj.getClass(); + Map map = new LinkedHashMap<>(); - /** - * Creates a list from a collection. - * Public method of type List called "makeList". - * The method takes a Collection object parameter called "collection". - * The method checks to see if the Collection Object is null. If so, it returns null. - * Otherwise, it creates an ArrayList object called "list". - * It then iterates through the Collection Object and adds all the collection items to that list. - * The method then returns the list. - * @param - The type parameter - * @param collection - The collection - * @return - Returns the list. - */ - public static List makeList(Collection collection) { - if (collection == null) { - return null; - } - return new ArrayList<>(collection); - } - /** - * Public method of type List called "makeList". - * The method takes a parameter of type E array called "array". - * The method checks to see if the array is null. If so, it returns null. - * Otherwise, it creates an ArrayList object called "newList". - * It then iterates through the array and adds all the items to the new list. - * The method then returns the list. - * @param - The type parameter - * @param array - The array - * @return - Returns the list. - */ - public static List makeList(E[] array) { - if (array == null) { - return null; + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + boolean hasAnnotations = field.isAnnotationPresent(MapValue.class); + + String key = field.getName(); + Object value = field.get(obj); + if (value == null) continue; + + if (hasAnnotations) { + MapValue annotation = field.getDeclaredAnnotation(MapValue.class); + if (annotation.ignore()) { + continue; + } + + if (!isEmptyString(annotation.value())) { + key = annotation.value(); + } + } + map.put(key, value); + } + + return map; } - return List.of(array); - } + /** + * Convert a generic map to Stringed map where the key and value are both strings + * + * @param map map to convert + * @param The expected class of the key + * @param The expected class of the value + * @return convertedMap + */ + public static Map convertToStringMap(Map map) { + Map convert = new LinkedHashMap<>(); + for (Entry item : map.entrySet()) { + convert.put(String.valueOf(item.getKey()), String.valueOf(item.getValue())); + } - /** - * Creates a list from an iterable object - * Public method of type List called "makeList". - * The method takes an Iterable object parameter called "iter". - * The method checks to see if the object is null. If so, it returns null. - * Otherwise, it creates a new ArrayList object called "list". - * It then iterates through the Iterable object and adds items to it. - * After that it returns the list. - * @param iter - The object to iterate over. - * @param - The expected class of the Iterable object - * @return - Returns the list. - */ - public static List makeList(Iterable iter) { - if (iter == null) { - return null; - } - List list = new ArrayList<>(); - for (E item : iter) { - list.add(item); - } - return list; - } - - /** - * Public method of type Set called "makeSet". - * The method takes an Iterable object parameter called "collection". - * The method first creates a new HashSet called "set". - * The method then iterates through the collection and adds all the items from it to the HashSet. - * The method then returns the HashSet. - * @param - The type parameter - * @param collection - The iterable object name. - * @return - Returns the HashSet with all the items of the collection inside of it. - */ - public static Set makeSet(Iterable collection) { - Set set = new HashSet<>(); - for (E item : safeIterable(collection)) { - set.add(item); - } - return set; - } - - /** - * Map to list. - * - * @param the type parameter - * @param the type parameter - * @param map the map - * - * @return the list - */ - public static List mapToList(Map map) { - - return new ArrayList<>(map.values()); - } - - /** - * Map to set. - * - * @param the type parameter - * @param the type parameter - * @param map the map - * - * @return the set - */ - public static Set mapToSet(Map map) { - - return new HashSet<>(map.values()); - } - - /** - * Reverse a map switching key value pairs - * - * @param the type parameter - * @param the type parameter - * @param map the map - * - * @return map with reverse key value pairs - */ - public static Map reverseMap(Map map) { - return map.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey)); - } - - /** - * create safe array e [ ]. - * - * @param the type parameter - * @param list the list - * - * @return the e [ ] - */ - @SuppressWarnings("unchecked") - public static E[] safeArray(E[] list) { - if (!validateValue(list)) { - return (E[]) new Object[0]; - } - return list; - } - - - /** - * Creates a safe list - * - * @param the type parameter - * @param list The list to check - * - * @return an empty list if list is invalid or the list if its valid - */ - public static Collection safeList(Collection list) { - if (!validateValue(list)) { - return new ArrayList<>(); - } - return list; - } - - - /** - * Creates a safe list - * - * @param the type parameter - * @param list The list to check - * - * @return an empty list if list is invalid or the list if its valid - */ - public static Iterable safeIterable(Iterable list) { - if (!validateValue(list)) { - return new ArrayList<>(); - } - return list; - } - - /** - * Creates a safe set - * @param the type parameter - * @param set the set to check - * @return an empty set if the set is null otherwise the set - */ - public static Set safeSet(Set set) { - if (set == null) { - return new HashSet<>(); - } - return set; - } - - /** - * Checks to see if a string is not blank or null. - * if it's not blank it will return the string - * otherwise it will return an empty string - * @param string The string to check - * @return the passed in string if it's not null either empty string - */ - public static String safeString(String string) { - return isEmptyString(string) ? "" : string; - } - - /** - * Sets to list. - * - * @param the type parameter - * @param valueSet the value set - * - * @return the to list - */ - public static List setToList(Set valueSet) { - return new ArrayList<>(valueSet); - } - - /** - * Converts a String into an integer without exception - * @param value The string value to convert - * @param defaultValue The default value to return if the string cant be converted - * @return the integer representation of the passed in string or the default value if an exception occurred - */ - public static Integer toInteger(String value, Integer defaultValue) { - try { - return Integer.parseInt(value); - } catch (Exception ex) { - return defaultValue; - } - } - - /** - * Create a list from given values - * - * @param the type parameter - * @param values the values to add the list - * - * @return List containing said values - */ - @SafeVarargs - public static List toList(E... values) { - return Arrays.asList(values); - } - - public static List toList(Iterable iterable) { - List list = new ArrayList<>(); - for (E item : iterable) { - list.add(item); - } - return list; - } - - /** - * Create a set from given values - * - * @param the type parameter - * @param values the values to add the set - * - * @return Set containing said values - */ - @SafeVarargs - public static Set toSet(E... values) { - return new LinkedHashSet<>(Arrays.asList(values)); - } - - public static List toByteList(byte[] bytes) { - return IntStream.range(0, bytes.length) - .mapToObj(index -> bytes[index]) - .collect(Collectors.toList()); - } - - /** - * Converts an iterable object to a hash set - * @param iterable The Iterable object to convert - * @param the class type of the set - * @return set with the iterable items inside - */ - public static Set toSet(Iterable iterable) { - Set set = new HashSet<>(); - for (E value : iterable) { - set.add(value); - } - return set; - } - - public static long size(Iterable iterable) { - long count = 0; - for (Iterator iter = iterable.iterator(); iter.hasNext(); iter.next()) { count++; } - return count; - } - - public static E get(Iterable iterable, int index) { - AtomicInteger count = new AtomicInteger(0); - Optional test = StreamUtils.asStream(iterable) - .filter(item -> count.getAndIncrement() == index) - .findFirst(); - return test.orElse(null); - } - - public static Consumer handleExceptionWrapper(ThrowingConsumer consumer) { - return i -> { - try { - consumer.accept(i); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - }; - } - - /** - * Public method of type Boolean called "validateValue". - * The method takes an Object parameter called "value". - * The method loops through and checks to see if value is empty. - * If so, it will return that the value is not null and that it is not empty. - * Otherwise, it will check to see the value is an instance of Map. - * If so, it will return that the value is not null and that it is not empty. - * Otherwise, it will check to see if value is an instance of a String. - * If so, it will return that the value is not null and that it is not empty. - * Otherwise, it will check to see if value is an instance of a Collection Object. - * If so, it will return that the value is not null and if the value size is greater than zero. - * Otherwise, it will just return that the value is not null and not empty. - * @param - The type parameter - * @param value - Value to validate - * @return - Returns true if it is valid; false otherwise - */ - @SuppressWarnings("rawtypes") - public static boolean validateValue(V value) { - if (value == null) { - return false; - } - if (value instanceof Collection) { - return !((Collection)value).isEmpty(); - } else if (value instanceof Map){ - return !((Map)value).isEmpty(); - } else if (value instanceof Boolean) { - return ((Boolean) value); + return convert; } - return !value.toString().trim().isEmpty(); - } - - /** - * Validates all given values. - * Public method of type Boolean called "validateValues". - * The method takes multiple V type objects called "objectToValidate". - * The method creates a boolean type variable called "valid" and sets it to true. - * The method then loops through all the objects and validates each value in the object. - * The method sets valid equal to the result of true and false. - * If the method cannot validate the object values, it will throw an exception. If so, it returns false. - * Otherwise, the method will return the variable called "valid". - * @param - The type parameter - * @param objectsToValidate - Objects to validate - * @return - Returns true if all objects are valid false otherwise. - */ - @SafeVarargs - public static boolean validateValues(V... objectsToValidate) { - return validateValues(false, toList(objectsToValidate)); - } - - @SafeVarargs - public static boolean validateValuesOr(V... objectsToValidate) { - return validateValues(true, toList(objectsToValidate)); - } - - /** - * Validates all given values to - * @param or Set the or flag to check for at least one validated value - * @param objectsToValidate objects to validate. - * @param the expected class type of the objects passed in. All objects need to be of same type - * @return true if the values are valid, false otherwise - */ - public static boolean validateValues(boolean or, List objectsToValidate) { - boolean valid = !or; - for (Object obj : objectsToValidate) { - if (or && validateValue(obj)) { - return true; - } - valid &= validateValue(obj); + /** + * Converts an object toa string map; + * + * @param obj the object to convert + * @return the map + * @throws Exception thrown if something happens + */ + public static Map convertToStringMap(Object obj) throws Exception { + return convertToStringMap(convertToMap(obj)); } - return valid; - } - - public static boolean isJUnitRunning() { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - Optional junit = StreamUtils.asStream(stackTrace) - .map(StackTraceElement::getClassName) - .filter(className -> className.startsWith("org.junit.")) - .findFirst(); - return junit.isPresent(); - } + /*** + * Takes an input list and converts it to another list denoted by what is grabbed from the fieldName + * @param fieldName Name of the field in the object to retrieve + * @param list the list to retrieve values from + * @param The expected class of the List + * @param The expected class of the Iterable list + * @return The converted list + */ + public static Set extractFieldToSet(String fieldName, Iterable list) { + return listToSet(extractFieldToList(fieldName, list)); + } - private static Set> getWrapperTypes() { - return Set.of( - Boolean.class, - Character.class, - Byte.class, - Short.class, - Integer.class, - Long.class, - Float.class, - Double.class, - String.class - ); - } + /** + * Extract a field from an iterable object and place them in a set + * + * @param fieldName field to check + * @param list list to iterate + * @param The expected class of the Set + * @param The Expected class of the iterable list + * @return set with the desired items + */ + @SuppressWarnings("unchecked") + public static List extractFieldToList(String fieldName, Iterable list) { + List newSet = new ArrayList<>(); + Field field; + for (K item : Utils.safeIterable(list)) { + try { + field = getField(item.getClass(), fieldName); + + if (field == null) { + throw new IllegalArgumentException("Unable to find field with name " + fieldName); + } + + E val = (E) field.get(item); + newSet.add(val); + + } catch (Exception ex) { + throw new IllegalArgumentException(ex); + } + } + return newSet; + } + + /** + * Filters out items in a list with the given parameters + * + * @param fieldName the field name to inspect for the filter value + * @param filter the filter value to retrieve + * @param list the list to inspect + * @param the expected class of the iterable list + * @return List of items that met the criteria of the filter + */ + @SuppressWarnings("unchecked") + public static List filterList(String fieldName, Object filter, Iterable list) { + List newList = new ArrayList<>(); + + Field field; + + for (E item : safeIterable(list)) { + try { + field = getField(item.getClass(), fieldName); + + if (field == null) { + throw new IllegalArgumentException("Unable to find field with name " + fieldName); + } + + E val = (E) field.get(item); + if (val.equals(filter)) { + newList.add(item); + } + } catch (Exception ex) { + throw new IllegalArgumentException(ex); + } + } + + return newList; + } + + /** + * Find a value within the list. + * Public method of type E called "findValue". + * The method takes a parameter of type E called "itemToFind" and a String parameter called "fieldName" and a parameter of type List called "list". + * The method loops through each item in the list and tries to get the class name and return it as an item. + * The method the takes each item and searches for it in the list. + * + * @param itemToFind - The Item to find + * @param fieldName - The field name where the item is in + * @param list - The list to search + * @param - The expected class of item to be found + * @param - The expected class of the Iterable list + * @return - Returns the value of the specified field or null if there is no value. + * + */ + @SuppressWarnings({"unlikely-arg-type"}) + public static E findValue(E itemToFind, String fieldName, List list) { + return findValues(itemToFind, fieldName, list).stream().findFirst().orElse(null); + } + + /** + * Find values list. + * Public method of type List called "findValues". + * The method takes an E type parameter called "valueToFind" and a String parameter called "fieldName" and a List parameter called "list". + * The method creates a new array list called "tmpList". + * The method then iterates through the list and gets all the classes as items. + * If it can't find the fields, it will throw an illegal argument exception. + * + * @param valueToFind - The value to find + * @param fieldName - The field name + * @param list - The list + * @param - The expected class of item to be found + * @param - The expected class of the Iterable list + * @return - Returns the list. + */ + @SuppressWarnings({"unlikely-arg-type"}) + public static List findValues(E valueToFind, String fieldName, List list) { + List extracted = extractFieldToList(fieldName, list); + return extracted.stream() + .filter(Objects::nonNull) + .filter(v -> v.equals(valueToFind)).collect(Collectors.toList()); + } + + /** + * Use reflection to get the field values + * + * @param clazz the class to find the field + * @param fieldName the field name + * @return field + */ + private static Field getField(Class clazz, String fieldName) { + Field field = null; + while (clazz != null) { + try { + field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + break; + } catch (Exception ex) { + clazz = clazz.getSuperclass(); + } + } + + return field; + } + + /** + * A replacement for String.format. Allows for to many parameters or too few + * Replaces {} in the string in order. + * + * @param message the message + * @param args the args + * @return string + */ + public static String formatString(String message, Object... args) { + String tmp = message; + + for (Object arg : args) { + tmp = tmp.replaceFirst("\\{\\}", String.valueOf(arg)); + } + + return tmp; + } + + /** + * Get a class name + * + * @param the type parameter + * @param obj the obj + * @return class name + */ + public static String getClassName(V obj) { + return getClassName(obj, false); + } + + /** + * Get a class name + * + * @param the type parameter + * @param obj the obj + * @param verbose the verbose + * @return class name + */ + public static String getClassName(V obj, boolean verbose) { + return obj == null ? "Unknown" : (verbose ? obj.getClass().getName() : obj.getClass().getSimpleName()); + } + + /** + * Get a value from a map + * + * @param the type parameter + * @param the type parameter + * @param map the map with values + * @param key the key to find + * @return map value + */ + public static T getMapValue(Map map, K key) { + return getMapValue(map, key, null); + } + + /** + * Get a value from a map + * + * @param the type parameter + * @param the type parameter + * @param map the map with the values + * @param key the key to find + * @param defaultValue the default value if the key is not found + * @return map value + */ + public static T getMapValue(Map map, K key, T defaultValue) { + return map.getOrDefault(key, defaultValue); + } + + /** + * Checks to see if the proved string is empty + * new String(null) = true + * new String("") = true + * new String(" ") = true + * new String("foo") = false + * new String(" bar ") = false + * + * @param string String to check + * @return true if the string is empty false otherwise + */ + public static boolean isEmptyString(String string) { + return string == null || string.trim().isEmpty(); + } + + /** + * Checks to see if a passed in value is not a valid value + * For Strings checks to see if they are empty or not null + * For collections checks to see if they are null or empty + * for all others just checks if they are null + * + * @param the expected class type of the object + * @param values The value to check + * @return true if the value is not valid, false otherwise + */ + @SafeVarargs + public static boolean isNotValid(V... values) { + return !validateValues(values); + } + + /** + * Checks to see if a passed in value is a valid value + * For Strings checks to see if they are not empty or not null + * For collections checks to see if they are not null and not empty + * for all others just checks if they are not null + * + * @param the expected class type of the object + * @param values The value to check + * @return true if the value is valid, false otherwise + */ + @SafeVarargs + public static boolean isValid(V... values) { + return validateValues(values); + } + + /** + * Is primitive or wrapper boolean. + * + * @param clazz the clazz + * @return the boolean + */ + public static boolean isPrimitiveOrWrapper(Class clazz) { + return clazz.isPrimitive() || getWrapperTypes().contains(clazz); + } + + /** + * Converts a list of items into a map. + * + * @param the type parameter + * @param the type parameter + * @param keyFieldName the name of the field that will be used as the key + * @param list the list to convert + * @return a map + */ + @SuppressWarnings("unchecked") + public static Map listToMap(String keyFieldName, List list) { + Map map = new HashMap<>(); + + for (V value : Utils.safeList(list)) { + try { + Field field = getField(value.getClass(), keyFieldName); + K key = (K) field.get(value); + map.put(key, value); + } catch (Exception e) { + throw new IllegalArgumentException(String.format("Unable to find field %s. -> %s", keyFieldName, e.getMessage())); + } + } + + return map; + } + + /** + * List to set. + * + * @param the type parameter + * @param valueSet the value set + * @return the set + */ + public static Set listToSet(Collection valueSet) { + return toSet(valueSet); + } + + /** + * Creates an array from a collection + * Public method of type array called "makeArray". + * The method takes a Collection object parameter called "collection" and a Class object parameter called "clazz". + * The method first checks to see if the Collection object is null. If so, it returns null. + * The method then creates an array called "results" and populates it with a new instance of "clazz" and the size of the collection. + * The method then creates an Int variable called "index" and sets the value to zero. + * The method then iterates through the collection and adds all the items to the results array. + * The method then returns the results array. + * + * @param - The type parameter + * @param collection - The collection + * @param clazz - The clazz + * @return e[] - Returns the array of results. + */ + @SuppressWarnings("unchecked") + public static E[] makeArray(Collection collection, Class clazz) { + if (collection == null) { + return null; + } + + E[] results = (E[]) Array.newInstance(clazz, collection.size()); + + int index = 0; + for (E item : collection) { + results[index++] = item; + } + + return results; + } + + /** + * Creates a list from a collection. + * Public method of type List called "makeList". + * The method takes a Collection object parameter called "collection". + * The method checks to see if the Collection Object is null. If so, it returns null. + * Otherwise, it creates an ArrayList object called "list". + * It then iterates through the Collection Object and adds all the collection items to that list. + * The method then returns the list. + * + * @param - The type parameter + * @param collection - The collection + * @return - Returns the list. + */ + public static List makeList(Collection collection) { + if (collection == null) { + return null; + } + return new ArrayList<>(collection); + } + + /** + * Public method of type List called "makeList". + * The method takes a parameter of type E array called "array". + * The method checks to see if the array is null. If so, it returns null. + * Otherwise, it creates an ArrayList object called "newList". + * It then iterates through the array and adds all the items to the new list. + * The method then returns the list. + * + * @param - The type parameter + * @param array - The array + * @return - Returns the list. + */ + public static List makeList(E[] array) { + if (array == null) { + return null; + } + + return List.of(array); + } + + /** + * Creates a list from an iterable object + * Public method of type List called "makeList". + * The method takes an Iterable object parameter called "iter". + * The method checks to see if the object is null. If so, it returns null. + * Otherwise, it creates a new ArrayList object called "list". + * It then iterates through the Iterable object and adds items to it. + * After that it returns the list. + * + * @param iter - The object to iterate over. + * @param - The expected class of the Iterable object + * @return - Returns the list. + */ + public static List makeList(Iterable iter) { + if (iter == null) { + return null; + } + List list = new ArrayList<>(); + for (E item : iter) { + list.add(item); + } + return list; + } + + /** + * Public method of type Set called "makeSet". + * The method takes an Iterable object parameter called "collection". + * The method first creates a new HashSet called "set". + * The method then iterates through the collection and adds all the items from it to the HashSet. + * The method then returns the HashSet. + * + * @param - The type parameter + * @param collection - The iterable object name. + * @return - Returns the HashSet with all the items of the collection inside of it. + */ + public static Set makeSet(Iterable collection) { + Set set = new HashSet<>(); + for (E item : safeIterable(collection)) { + set.add(item); + } + return set; + } + + /** + * Map to list. + * + * @param the type parameter + * @param the type parameter + * @param map the map + * @return the list + */ + public static List mapToList(Map map) { + + return new ArrayList<>(map.values()); + } + + /** + * Map to set. + * + * @param the type parameter + * @param the type parameter + * @param map the map + * @return the set + */ + public static Set mapToSet(Map map) { + + return new HashSet<>(map.values()); + } + + /** + * Reverse a map switching key value pairs + * + * @param the type parameter + * @param the type parameter + * @param map the map + * @return map with reverse key value pairs + */ + public static Map reverseMap(Map map) { + return map.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey)); + } + + /** + * create safe array e [ ]. + * + * @param the type parameter + * @param list the list + * @return the e [ ] + */ + @SuppressWarnings("unchecked") + public static E[] safeArray(E[] list) { + if (!validateValue(list)) { + return (E[]) new Object[0]; + } + return list; + } + + + /** + * Creates a safe list + * + * @param the type parameter + * @param list The list to check + * @return an empty list if list is invalid or the list if its valid + */ + public static Collection safeList(Collection list) { + if (!validateValue(list)) { + return new ArrayList<>(); + } + return list; + } + + + /** + * Creates a safe list + * + * @param the type parameter + * @param list The list to check + * @return an empty list if list is invalid or the list if its valid + */ + public static Iterable safeIterable(Iterable list) { + if (!validateValue(list)) { + return new ArrayList<>(); + } + return list; + } + + /** + * Creates a safe set + * + * @param the type parameter + * @param set the set to check + * @return an empty set if the set is null otherwise the set + */ + public static Set safeSet(Set set) { + if (set == null) { + return new HashSet<>(); + } + return set; + } + + /** + * Checks to see if a string is not blank or null. + * if it's not blank it will return the string + * otherwise it will return an empty string + * + * @param string The string to check + * @return the passed in string if it's not null either empty string + */ + public static String safeString(String string) { + return isEmptyString(string) ? "" : string; + } + + /** + * Sets to list. + * + * @param the type parameter + * @param valueSet the value set + * @return the to list + */ + public static List setToList(Set valueSet) { + return new ArrayList<>(valueSet); + } + + /** + * Converts a String into an integer without exception + * + * @param value The string value to convert + * @param defaultValue The default value to return if the string cant be converted + * @return the integer representation of the passed in string or the default value if an exception occurred + */ + public static Integer toInteger(String value, Integer defaultValue) { + try { + return Integer.parseInt(value); + } catch (Exception ex) { + return defaultValue; + } + } + + /** + * Create a list from given values + * + * @param the type parameter + * @param values the values to add the list + * @return List containing said values + */ + @SafeVarargs + public static List toList(E... values) { + return Arrays.asList(values); + } + + public static List toList(Iterable iterable) { + List list = new ArrayList<>(); + for (E item : iterable) { + list.add(item); + } + return list; + } + + /** + * Create a set from given values + * + * @param the type parameter + * @param values the values to add the set + * @return Set containing said values + */ + @SafeVarargs + public static Set toSet(E... values) { + return new LinkedHashSet<>(Arrays.asList(values)); + } + + public static List toByteList(byte[] bytes) { + return IntStream.range(0, bytes.length) + .mapToObj(index -> bytes[index]) + .collect(Collectors.toList()); + } + + /** + * Converts an iterable object to a hash set + * + * @param iterable The Iterable object to convert + * @param the class type of the set + * @return set with the iterable items inside + */ + public static Set toSet(Iterable iterable) { + Set set = new HashSet<>(); + for (E value : iterable) { + set.add(value); + } + return set; + } + + public static long size(Iterable iterable) { + long count = 0; + for (Iterator iter = iterable.iterator(); iter.hasNext(); iter.next()) { + count++; + } + return count; + } + + public static E get(Iterable iterable, int index) { + AtomicInteger count = new AtomicInteger(0); + Optional test = StreamUtils.asStream(iterable) + .filter(item -> count.getAndIncrement() == index) + .findFirst(); + return test.orElse(null); + } + + public static Consumer handleExceptionWrapper(ThrowingConsumer consumer) { + return i -> { + try { + consumer.accept(i); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }; + } + + /** + * Public method of type Boolean called "validateValue". + * The method takes an Object parameter called "value". + * The method loops through and checks to see if value is empty. + * If so, it will return that the value is not null and that it is not empty. + * Otherwise, it will check to see the value is an instance of Map. + * If so, it will return that the value is not null and that it is not empty. + * Otherwise, it will check to see if value is an instance of a String. + * If so, it will return that the value is not null and that it is not empty. + * Otherwise, it will check to see if value is an instance of a Collection Object. + * If so, it will return that the value is not null and if the value size is greater than zero. + * Otherwise, it will just return that the value is not null and not empty. + * + * @param - The type parameter + * @param value - Value to validate + * @return - Returns true if it is valid; false otherwise + */ + @SuppressWarnings("rawtypes") + public static boolean validateValue(V value) { + if (value == null) { + return false; + } + if (value instanceof Collection) { + return !((Collection) value).isEmpty(); + } else if (value instanceof Map) { + return !((Map) value).isEmpty(); + } else if (value instanceof Boolean) { + return ((Boolean) value); + } + + return !value.toString().trim().isEmpty(); + } + + /** + * Validates all given values. + * Public method of type Boolean called "validateValues". + * The method takes multiple V type objects called "objectToValidate". + * The method creates a boolean type variable called "valid" and sets it to true. + * The method then loops through all the objects and validates each value in the object. + * The method sets valid equal to the result of true and false. + * If the method cannot validate the object values, it will throw an exception. If so, it returns false. + * Otherwise, the method will return the variable called "valid". + * + * @param - The type parameter + * @param objectsToValidate - Objects to validate + * @return - Returns true if all objects are valid false otherwise. + */ + @SafeVarargs + public static boolean validateValues(V... objectsToValidate) { + return validateValues(false, toList(objectsToValidate)); + } + + @SafeVarargs + public static boolean validateValuesOr(V... objectsToValidate) { + return validateValues(true, toList(objectsToValidate)); + } + + /** + * Validates all given values to + * + * @param or Set the or flag to check for at least one validated value + * @param objectsToValidate objects to validate. + * @param the expected class type of the objects passed in. All objects need to be of same type + * @return true if the values are valid, false otherwise + */ + public static boolean validateValues(boolean or, List objectsToValidate) { + boolean valid = !or; + for (Object obj : objectsToValidate) { + if (or && validateValue(obj)) { + return true; + } + valid &= validateValue(obj); + } + + return valid; + } + + public static boolean isJUnitRunning() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + Optional junit = StreamUtils.asStream(stackTrace) + .map(StackTraceElement::getClassName) + .filter(className -> className.startsWith("org.junit.")) + .findFirst(); + return junit.isPresent(); + } + + private static Set> getWrapperTypes() { + return Set.of( + Boolean.class, + Character.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + String.class + ); + } } diff --git a/src/test/java/net/locusworks/common/configuration/ConfigurationCoverageTest.java b/src/test/java/net/locusworks/common/configuration/ConfigurationCoverageTest.java new file mode 100644 index 0000000..c20e127 --- /dev/null +++ b/src/test/java/net/locusworks/common/configuration/ConfigurationCoverageTest.java @@ -0,0 +1,165 @@ +package net.locusworks.common.configuration; + +import net.locusworks.common.exceptions.ApplicationException; +import net.locusworks.common.interfaces.PersistableRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mockStatic; + +import org.mockito.MockedStatic; + +class ConfigurationCoverageTest { + @TempDir + Path tempDir; + + static class Manager extends ConfigurationManager { + void initialize(Path base, byte[] key, ConfigurationCallback callback) throws Exception { + init(base.toString(), "test.properties", key, callback); + } + + void initialize(Path base, ConfigurationCallback callback) throws Exception { + init(base.toString(), "test.properties", callback); + } + } + + static class Request implements PersistableRequest { + private String dbHost; + private String dbPort; + private String ignored; + + Request(String dbHost, String dbPort, String ignored) { + this.dbHost = dbHost; + this.dbPort = dbPort; + this.ignored = ignored; + } + } + + @Test + void managerCreatesLoadsReconcilesSavesAndReportsConfiguration() throws Exception { + List messages = new ArrayList<>(); + Manager manager = new Manager(); + manager.initialize(tempDir, "seed".getBytes(), messages::add); + + assertEquals("localhost", manager.getPropertyValue("dbHost")); + assertEquals("fallback", manager.getPropertyValue("missing", "fallback")); + assertNull(manager.getPropertyValue("missing")); + assertEquals(4, manager.getConfiguration().size()); + assertTrue(Files.exists(tempDir.resolve("test.properties"))); + assertTrue(messages.stream().anyMatch(m -> m.contains("Added new configuration"))); + + Properties changed = new Properties(); + changed.putAll(manager.getConfiguration()); + changed.setProperty("dbHost", "remote"); + changed.setProperty("obsolete", "remove me"); + manager.saveToConf(changed); + assertEquals("remote", manager.getPropertyValue("dbHost")); + assertFalse(manager.getConfiguration().containsKey("obsolete")); + assertTrue(messages.stream().anyMatch(m -> m.contains("Saved config file"))); + } + + @Test + void managerPersistsSelectedPlainAndEncryptedFieldsAndSkipsUnchangedOnes() throws Exception { + Manager manager = new Manager(); + manager.initialize(tempDir, new byte[0], null); + + assertThrows(ApplicationException.class, + () -> manager.saveConfiguration(new Request("x", "1", "x"), null, null)); + assertThrows(ApplicationException.class, + () -> manager.saveConfiguration(new Request("x", "1", "x"), Set.of(), null)); + + manager.saveConfiguration(new Request("new-host", "3306", "ignored"), + Set.of("dbHost", "dbPort"), null); + assertEquals("new-host", manager.getPropertyValue("dbHost")); + assertEquals("3306", manager.getPropertyValue("dbPort")); + + manager.saveConfiguration(new Request("encrypted-host", "3306", "ignored"), + Set.of("dbHost", "dbPort", "ignored"), Set.of("dbHost")); + assertNotEquals("encrypted-host", manager.getPropertyValue("dbHost")); + + assertThrows(ApplicationException.class, + () -> manager.saveConfiguration(null, Set.of("dbHost"), Set.of())); + + assertDoesNotThrow(() -> manager.saveConfiguration( + new Request("ignored", "3306", "ignored"), Set.of("dbPort"), Set.of())); + } + + @Test + void managerConvenienceInitializationAndBothLoadFailuresAreCovered() throws Exception { + Path convenience = Files.createDirectory(tempDir.resolve("convenience")); + Manager normal = new Manager(); + normal.initialize(convenience, null); + assertNotNull(normal.getConfiguration()); + + try (MockedStatic properties = mockStatic(PropertiesManager.class, CALLS_REAL_METHODS)) { + properties.when(() -> PropertiesManager.loadConfiguration(Manager.class, "test.properties")) + .thenThrow(new java.io.IOException("template failed")); + assertThrows(java.io.IOException.class, + () -> new Manager().initialize(tempDir, "seed".getBytes(), null)); + } + + Path activeFailure = Files.createDirectory(tempDir.resolve("active-failure")); + try (MockedStatic properties = mockStatic(PropertiesManager.class, CALLS_REAL_METHODS)) { + Path activeFile = activeFailure.resolve("test.properties"); + properties.when(() -> PropertiesManager.loadConfiguration(activeFile)) + .thenThrow(new java.io.IOException("active failed")); + Manager recovered = new Manager(); + recovered.initialize(activeFailure, "seed".getBytes(), null); + assertEquals(4, recovered.getConfiguration().size()); + } + } + + @SuppressWarnings("deprecation") + @Test + void propertiesManagerCoversResourcesFilesReadersMergeRemovalAndSaveFailures() throws Exception { + assertNotNull(new PropertiesManager()); + assertNull(PropertiesManager.loadConfiguration(getClass(), "missing.properties")); + Properties resource = PropertiesManager.loadConfiguration(getClass(), "/test.properties"); + assertEquals(4, resource.size()); + + Path missing = tempDir.resolve("missing.properties"); + assertTrue(PropertiesManager.loadConfiguration(missing).isEmpty()); + + Properties parsed = PropertiesManager.loadConfiguration( + new BufferedReader(new StringReader("one=1\ntwo=2"))); + assertEquals("1", parsed.getProperty("one")); + + Properties destination = new Properties(); + destination.setProperty("one", "existing"); + Properties additions = new Properties(); + additions.setProperty("one", "replacement"); + additions.setProperty("two", "2"); + assertEquals(1, PropertiesManager.addConfiguration(destination, additions).size()); + assertEquals("existing", destination.getProperty("one")); + + destination.setProperty("obsolete", "old"); + Properties expected = new Properties(); + expected.setProperty("one", "existing"); + assertEquals(2, PropertiesManager.removeConfiguration(destination, expected).size()); + assertEquals(Set.of("one"), destination.keySet()); + + Path saved = tempDir.resolve("saved.properties"); + PropertiesManager.saveConfiguration(expected, saved.toFile(), null); + assertEquals("existing", PropertiesManager.loadConfiguration(saved.toFile()).getProperty("one")); + assertThrows(RuntimeException.class, + () -> PropertiesManager.saveConfiguration(expected, tempDir, "failure")); + } +} diff --git a/src/test/java/net/locusworks/common/crypto/AESAndHashSaltCoverageTest.java b/src/test/java/net/locusworks/common/crypto/AESAndHashSaltCoverageTest.java new file mode 100644 index 0000000..1c5f324 --- /dev/null +++ b/src/test/java/net/locusworks/common/crypto/AESAndHashSaltCoverageTest.java @@ -0,0 +1,80 @@ +package net.locusworks.common.crypto; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +class AESAndHashSaltCoverageTest { + @Test + void aesFactoriesSeedChangesEmptyValuesAndFailures() throws Exception { + AES bytes = AES.createInstance("byte seed".getBytes()); + assertEquals("byte seed", bytes.getSeed()); + assertEquals("", bytes.decrypt(bytes.encrypt(null))); + assertEquals("", bytes.decrypt(null)); + + String encrypted = bytes.encrypt("value"); + assertSame(bytes, bytes.withSeed("byte seed")); + assertEquals("value", bytes.decrypt(encrypted)); + assertSame(bytes, bytes.withSeed("different seed")); + assertEquals("different seed", bytes.getSeed()); + assertThrows(IllegalArgumentException.class, () -> bytes.decrypt(encrypted)); + assertThrows(IllegalArgumentException.class, () -> bytes.decrypt("AA==")); + assertNotNull(AES.createInstance()); + assertThrows(IllegalArgumentException.class, () -> AES.createInstance((String) null)); + + AES uninitialized = new AES(); + assertThrows(IllegalArgumentException.class, () -> uninitialized.encrypt("value")); + Method init = AES.class.getDeclaredMethod("init", byte[].class); + init.setAccessible(true); + InvocationTargetException initFailure = assertThrows(InvocationTargetException.class, + () -> init.invoke(uninitialized, (Object) new byte[16])); + assertInstanceOf(IllegalArgumentException.class, initFailure.getCause()); + } + + @Test + void aesMainValidatesArgumentsAndSupportsEncryptAndDecryptModes() throws Exception { + assertThrows(IllegalArgumentException.class, () -> AES.main(null)); + assertThrows(IllegalArgumentException.class, () -> AES.main(new String[0])); + assertDoesNotThrow(() -> AES.main(new String[]{"plain"})); + + AES aes = AES.createInstance("seed"); + assertDoesNotThrow(() -> AES.main(new String[]{aes.encrypt("plain"), "seed"})); + } + + @Test + void hashSaltSupportsStringAndCharacterPasswordsAndInvalidInputs() throws Exception { + assertNotNull(new HashSalt()); + String hash = HashSalt.createHash("password".toCharArray()); + assertTrue(HashSalt.validatePassword("password".toCharArray(), hash)); + assertFalse(HashSalt.validatePassword("wrong", hash)); + assertFalse(HashSalt.validatePassword("wrong".toCharArray(), hash)); + assertThrows(RuntimeException.class, () -> HashSalt.validatePassword("password", "bad")); + + Method toHex = HashSalt.class.getDeclaredMethod("toHex", byte[].class); + toHex.setAccessible(true); + assertEquals("0001", toHex.invoke(null, (Object) new byte[]{0, 1})); + + Method slowEquals = HashSalt.class.getDeclaredMethod("slowEquals", byte[].class, byte[].class); + slowEquals.setAccessible(true); + assertEquals(false, slowEquals.invoke(null, new byte[]{1}, new byte[]{1, 2})); + assertEquals(false, slowEquals.invoke(null, new byte[]{1, 2}, new byte[]{1})); + } + + @Test + void hashSaltMainValidatesArgumentsAndPrintsHash() { + assertThrows(IllegalArgumentException.class, () -> HashSalt.main(null)); + assertThrows(IllegalArgumentException.class, () -> HashSalt.main(new String[0])); + assertDoesNotThrow(() -> HashSalt.main(new String[]{"password"})); + } +} diff --git a/src/test/java/net/locusworks/common/crypto/CryptoCoverageTest.java b/src/test/java/net/locusworks/common/crypto/CryptoCoverageTest.java new file mode 100644 index 0000000..f3b6371 --- /dev/null +++ b/src/test/java/net/locusworks/common/crypto/CryptoCoverageTest.java @@ -0,0 +1,227 @@ +package net.locusworks.common.crypto; + +import net.locusworks.common.crypto.KeyFile.EncryptionType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mockStatic; + +import org.mockito.MockedStatic; + +class CryptoCoverageTest { + @TempDir + Path tempDir; + + @Test + void aesKeyAndSpecRoundTripAndRejectMalformedData() throws Exception { + AESKey key = new AESKey("repeatable seed"); + assertEquals("aes", key.getAlgorithm()); + assertEquals("aes-seed", key.getFormat()); + assertArrayEquals("repeatable seed".getBytes(StandardCharsets.UTF_8), key.getEncoded()); + + String encoded = "aes-seed " + Base64.getEncoder().encodeToString(key.getEncoded()); + AESKeySpec spec = new AESKeySpec(encoded.getBytes(StandardCharsets.UTF_8)); + assertEquals("aes", spec.getFormat()); + assertArrayEquals(key.getEncoded(), spec.generateKey().getEncoded()); + assertThrows(InvalidKeySpecException.class, + () -> new AESKeySpec("wrong value".getBytes(StandardCharsets.UTF_8)).generateKey()); + assertThrows(InvalidKeySpecException.class, + () -> new AESKeySpec("aes-seed !!!".getBytes(StandardCharsets.UTF_8)).generateKey()); + } + + @Test + void encryptionKeyFactoryHandlesAesRsaAndSshSpecs() throws Exception { + EncryptionKeyFactory factory = EncryptionKeyFactory.getInstance("RSA"); + String aes = "aes-seed " + Base64.getEncoder().encodeToString("seed".getBytes(StandardCharsets.UTF_8)); + assertInstanceOf(AESKey.class, factory.generatePrivateKey(new AESKeySpec(aes.getBytes(StandardCharsets.UTF_8)))); + + KeyPair pair = RSA.generateKeyPair(1024); + assertEquals(pair.getPrivate(), factory.generatePrivateKey( + new java.security.spec.PKCS8EncodedKeySpec(pair.getPrivate().getEncoded()))); + assertEquals(pair.getPublic(), factory.generatePublicKey( + new java.security.spec.X509EncodedKeySpec(pair.getPublic().getEncoded()))); + + Path ssh = tempDir.resolve("public.ssh"); + try (KeyFile file = new KeyFile(pair.getPublic(), "test", EncryptionType.SSH)) { + file.write(ssh.toString()); + } + SSHEncodedKeySpec sshSpec = new SSHEncodedKeySpec(Files.readAllBytes(ssh)); + assertNull(sshSpec.getFormat()); + assertEquals(pair.getPublic(), factory.generatePublicKey(sshSpec)); + assertEquals(pair.getPublic(), KeyFile.read(ssh.toString()).getKey()); + assertThrows(InvalidKeySpecException.class, + () -> new SSHEncodedKeySpec("ssh-ed25519 bad".getBytes(StandardCharsets.UTF_8)).convertToRSAPubKeySpec()); + assertThrows(InvalidKeySpecException.class, + () -> new SSHEncodedKeySpec("ssh-rsa !!!".getBytes(StandardCharsets.UTF_8)).convertToRSAPubKeySpec()); + } + + @Test + void rsaGeneratesCalculatesEncryptsDecryptsAndRejectsOversizedMessages() { + assertNotNull(new RSA()); + KeyPair defaultPair = RSA.generateKeyPair(); + assertNotNull(defaultPair.getPrivate()); + KeyPair pair = RSA.generateKeyPair(1024); + assertEquals(("hello".getBytes(StandardCharsets.UTF_8).length + 11) * 8, + RSA.calculateRequiredKeyLength("hello")); + + String encrypted = RSA.encrypt(pair.getPublic(), "hello RSA"); + assertEquals("hello RSA", RSA.decrypt(pair.getPrivate(), encrypted)); + assertThrows(RuntimeException.class, () -> RSA.decrypt(pair.getPrivate(), "not base64")); + assertThrows(RuntimeException.class, () -> RSA.encrypt(pair.getPublic(), "x".repeat(200))); + assertThrows(RuntimeException.class, () -> RSA.generateKeyPair(1)); + } + + @Test + void keyFileWritesAesPemAndSshFormatsAndProvidesDescriptions() throws Exception { + AESKey aesKey = new AESKey("seed"); + Path aesPath = tempDir.resolve("aes.key"); + try (KeyFile file = new KeyFile(aesKey)) { + assertEquals("PRIVATE KEY", file.getDescription()); + file.setDescription("AES KEY"); + assertEquals("AES KEY", file.getDescription()); + assertSame(aesKey, file.getKey()); + file.write(aesPath.toString()); + } + assertEquals("aes-seed c2VlZA==", Files.readString(aesPath)); + + KeyPair pair = RSA.generateKeyPair(1024); + Path privatePath = tempDir.resolve("private.pem"); + try (KeyFile file = new KeyFile(pair.getPrivate())) { + file.write(privatePath.toString()); + } + assertTrue(Files.readString(privatePath).contains("BEGIN RSA PRIVATE KEY")); + + Path publicPath = tempDir.resolve("public.pem"); + try (KeyFile file = new KeyFile(pair.getPublic(), "CUSTOM")) { + file.write(publicPath.toString()); + } + assertTrue(Files.readString(publicPath).contains("BEGIN RSA CUSTOM")); + assertEquals("PUBLIC KEY", new KeyFile(pair.getPublic()).getDescription()); + + Path sshPath = tempDir.resolve("id.pub"); + try (KeyFile file = new KeyFile(pair.getPublic(), "comment", EncryptionType.SSH)) { + file.write(sshPath.toString()); + } + assertTrue(Files.readString(sshPath).startsWith("ssh-rsa ")); + assertTrue(Files.readString(sshPath).endsWith(" comment")); + } + + @Test + void keyFileReadHandlesEmptyMissingRecognizedAndInvalidFiles() throws Exception { + assertNull(KeyFile.read(null).getKey()); + RuntimeException missing = assertThrows(RuntimeException.class, + () -> KeyFile.read(tempDir.resolve("missing").toString())); + assertInstanceOf(IllegalArgumentException.class, missing.getCause()); + + Path aesPath = tempDir.resolve("recognized-aes.key"); + new KeyFile(new AESKey("seed")).write(aesPath.toString()); + assertArrayEquals("seed".getBytes(StandardCharsets.UTF_8), KeyFile.read(aesPath.toString()).getKey().getEncoded()); + + Path invalid = tempDir.resolve("invalid.key"); + Files.writeString(invalid, "not a key"); + assertThrows(RuntimeException.class, () -> KeyFile.read(invalid.toString())); + } + + @Test + void rsaWriteConvenienceOverloadsUseRequestedPaths() { + KeyPair pair = RSA.generateKeyPair(1024); + Path privatePath = tempDir.resolve("private.key"); + Path publicPath = tempDir.resolve("public.key"); + assertTrue(RSA.writePrivateKey(pair, privatePath.toString(), "PRIVATE KEY")); + assertTrue(RSA.writePublicKey(pair, publicPath.toString(), "PUBLIC KEY")); + assertTrue(Files.exists(privatePath)); + assertTrue(Files.exists(publicPath)); + + Path sshPath = tempDir.resolve("rsa-ssh.pub"); + assertTrue(RSA.writePublicKey(pair, sshPath.toString(), "comment", true)); + + assertTrue(RSA.generateAndWriteKeyPair(tempDir.resolve("generated").toString(), 1024)); + assertNotNull(RSA.loadKeyPair(publicPath.toString(), privatePath.toString())); + assertNotNull(RSA.loadPublicKey(publicPath.toString()).getPublic()); + assertNotNull(RSA.loadPrivateKey(privatePath.toString()).getPrivate()); + } + + @Test + void keyFileWrapsWritePemAndCloseFailures() throws Exception { + KeyPair pair = RSA.generateKeyPair(1024); + KeyFile invalidWrite = new KeyFile(pair.getPrivate()); + assertThrows(IllegalArgumentException.class, () -> invalidWrite.write(tempDir.toString())); + + KeyFile closeFailure = new KeyFile(pair.getPrivate()); + Field writerField = KeyFile.class.getDeclaredField("writer"); + writerField.setAccessible(true); + writerField.set(closeFailure, new Writer() { + @Override + public void write(char[] cbuf, int off, int len) { + } + + @Override + public void flush() throws IOException { + throw new IOException("flush failed"); + } + + @Override + public void close() { + } + }); + assertThrows(RuntimeException.class, closeFailure::close); + } + + @Test + void rsaConvenienceDelegatesAndShortCircuitPaths() { + KeyPair pair = RSA.generateKeyPair(1024); + try (MockedStatic rsa = mockStatic(RSA.class, CALLS_REAL_METHODS)) { + rsa.when(RSA::generateKeyPair).thenReturn(pair); + rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(true); + rsa.when(() -> RSA.writePublicKey(pair, true)).thenReturn(true); + assertTrue(RSA.generateAndWriteSSHKeys()); + rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(false); + assertFalse(RSA.generateAndWriteSSHKeys()); + rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(true); + rsa.when(() -> RSA.writePublicKey(pair, true)).thenReturn(false); + assertFalse(RSA.generateAndWriteSSHKeys()); + + rsa.when(() -> RSA.generateAndWriteKeyPair(2048)).thenReturn(true); + assertTrue(RSA.generateAndWriteKeyPair()); + rsa.when(() -> RSA.generateAndWriteKeyPair("named", 2048)).thenReturn(true); + assertTrue(RSA.generateAndWriteKeyPair("named")); + rsa.when(() -> RSA.generateAndWriteKeyPair("id_rsa", 1024)).thenReturn(true); + assertTrue(RSA.generateAndWriteKeyPair(1024)); + + rsa.when(() -> RSA.generateKeyPair(512)).thenReturn(pair); + rsa.when(() -> RSA.writePrivateKey(pair, "short", "PRIVATE KEY")).thenReturn(false); + assertFalse(RSA.generateAndWriteKeyPair("short", 512)); + rsa.when(() -> RSA.writePrivateKey(pair, "short", "PRIVATE KEY")).thenReturn(true); + rsa.when(() -> RSA.writePublicKey(pair, "short.pub", "PUBLIC KEY")).thenReturn(false); + assertFalse(RSA.generateAndWriteKeyPair("short", 512)); + + rsa.when(() -> RSA.writePrivateKey(pair, "id_rsa", "PRIVATE KEY")).thenReturn(true); + assertTrue(RSA.writePrivateKey(pair)); + rsa.when(() -> RSA.writePublicKey(pair, false)).thenReturn(true); + assertTrue(RSA.writePublicKey(pair)); + rsa.when(() -> RSA.writePublicKey(pair, "id_rsa.pub", "PUBLIC KEY", true)).thenReturn(true); + rsa.when(() -> RSA.writePublicKey(pair, true)).thenCallRealMethod(); + assertTrue(RSA.writePublicKey(pair, true)); + } + } +} diff --git a/src/test/java/net/locusworks/common/exceptions/ApplicationExceptionTest.java b/src/test/java/net/locusworks/common/exceptions/ApplicationExceptionTest.java new file mode 100644 index 0000000..4d430fb --- /dev/null +++ b/src/test/java/net/locusworks/common/exceptions/ApplicationExceptionTest.java @@ -0,0 +1,43 @@ +package net.locusworks.common.exceptions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +class ApplicationExceptionTest { + @Test + void factoryMethodsExposeExpectedCodesAndMessages() { + List exceptions = List.of( + ApplicationException.egregiousServer(), ApplicationException.invalidCreds(), + ApplicationException.notLoggedIn(), ApplicationException.invalidEmailAddress(), + ApplicationException.actionNotPermitted(), ApplicationException.actionNotPermitted("delete"), + ApplicationException.passwordsNotEqual(), ApplicationException.unAuthorized(), + ApplicationException.duplicateEntry("duplicate"), ApplicationException.duplicateEntry("duplicate %s", "item"), + ApplicationException.noEntryExists("missing"), ApplicationException.noEntryExists("missing %s", "item"), + ApplicationException.constraintViolation("constraint"), ApplicationException.constraintViolation("constraint %s", "x"), + ApplicationException.illegalArgument("illegal"), ApplicationException.generic("generic")); + + assertTrue(exceptions.stream().noneMatch(ApplicationException::getSuccess)); + assertTrue(exceptions.stream().allMatch(e -> e.getCode() != null && e.getMessage() != null)); + } + + @Test + void constructorsAndThrowableFactoryPreserveCause() { + IllegalStateException cause = new IllegalStateException("failure"); + ApplicationException fromCause = ApplicationException.fromException(cause); + assertEquals(9999, fromCause.getCode()); + assertSame(cause, fromCause.getCause()); + + ApplicationException direct = new ApplicationException(42, cause); + assertEquals("failure", direct.getMessage()); + assertSame(cause, direct.getCause()); + + ApplicationException explicit = new ApplicationException(43, "message", cause); + assertEquals(43, explicit.getCode()); + assertSame(cause, explicit.getCause()); + } +} diff --git a/src/test/java/net/locusworks/common/immutables/ImmutablesTest.java b/src/test/java/net/locusworks/common/immutables/ImmutablesTest.java new file mode 100644 index 0000000..6b68f34 --- /dev/null +++ b/src/test/java/net/locusworks/common/immutables/ImmutablesTest.java @@ -0,0 +1,69 @@ +package net.locusworks.common.immutables; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + + +class ImmutablesTest { + + @Test + void testUnit() { + Unit unit1 = new Unit<>(); + unit1.setValue1("123"); + + Unit unit2 = new Unit<>("123"); + Unit unit3 = new Unit<>("1234"); + + assertEquals(unit1, unit2); + + assertNotEquals(null, unit1); + assertNotEquals(new Object(), unit1); + assertNotEquals(unit3, unit1); + + assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value")); + } + + @Test + void testPair() { + Pair unit1 = new Pair<>(); + unit1.setValue2("value2"); + unit1.setValue1("123"); + + Pair unit2 = new Pair<>("123", "value2"); + Pair unit3 = new Pair<>("1234", "value2"); + + assertEquals(unit1, unit2); + + assertNotEquals(null, unit1); + assertNotEquals(new Object(), unit1); + assertNotEquals(unit3, unit1); + + assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value")); + assertThrows(IllegalArgumentException.class, () -> unit1.setValue2("value")); + } + + @Test + void testTriplet() { + Triplet unit1 = new Triplet<>(); + unit1.setValue3("value3"); + unit1.setValue2("value2"); + unit1.setValue1("123"); + + Triplet unit2 = new Triplet<>("123", "value2", "value3"); + Triplet unit3 = new Triplet<>("1234", "value2", "value3"); + + assertEquals(unit1, unit2); + + assertNotEquals(null, unit1); + assertNotEquals(new Object(), unit1); + assertNotEquals(unit3, unit1); + + assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value")); + assertThrows(IllegalArgumentException.class, () -> unit1.setValue2("value")); + assertThrows(IllegalArgumentException.class, () -> unit1.setValue3("value")); + } + +} diff --git a/src/test/java/net/locusworks/common/io/IOUtilsTest.java b/src/test/java/net/locusworks/common/io/IOUtilsTest.java new file mode 100644 index 0000000..6c81a65 --- /dev/null +++ b/src/test/java/net/locusworks/common/io/IOUtilsTest.java @@ -0,0 +1,158 @@ +package net.locusworks.common.io; + +import net.locusworks.common.Charsets; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.MockedStatic; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mockStatic; + +class IOUtilsTest { + @TempDir + Path tempDir; + + @Test + void readsLinesFromStreamsAndBothReaderKinds() throws Exception { + byte[] text = "first\nsecond\nthird".getBytes(StandardCharsets.UTF_8); + assertEquals(List.of("first", "second", "third"), + IOUtils.readLines(new ByteArrayInputStream(text), StandardCharsets.UTF_8)); + assertEquals(List.of("first", "second", "third"), IOUtils.readLines(new StringReader(new String(text)))); + + BufferedReader buffered = new BufferedReader(new StringReader("line")); + assertSame(buffered, IOUtils.toBufferedReader(buffered)); + assertNotNull(IOUtils.toBufferedReader(new StringReader("line"))); + assertThrows(NullPointerException.class, () -> IOUtils.readLines((Reader) null)); + } + + @Test + void convertsStreamsAndCopiesBytesThroughEveryOverload() throws Exception { + byte[] source = "some binary data".getBytes(StandardCharsets.UTF_8); + assertArrayEquals(source, IOUtils.toByteArray(new ByteArrayInputStream(source))); + + ByteArrayOutputStream defaultCopy = new ByteArrayOutputStream(); + assertEquals(source.length, IOUtils.copy(new ByteArrayInputStream(source), defaultCopy)); + assertArrayEquals(source, defaultCopy.toByteArray()); + + ByteArrayOutputStream sizedCopy = new ByteArrayOutputStream(); + assertEquals(source.length, IOUtils.copy(new ByteArrayInputStream(source), sizedCopy, 2)); + assertArrayEquals(source, sizedCopy.toByteArray()); + + ByteArrayOutputStream bufferedCopy = new ByteArrayOutputStream(); + assertEquals(source.length, + IOUtils.copyLarge(new ByteArrayInputStream(source), bufferedCopy, new byte[3])); + assertArrayEquals(source, bufferedCopy.toByteArray()); + + ByteArrayOutputStream largeCopy = new ByteArrayOutputStream(); + assertEquals(source.length, IOUtils.copyLarge(new ByteArrayInputStream(source), largeCopy)); + assertArrayEquals(source, largeCopy.toByteArray()); + assertEquals(0, IOUtils.copyLarge(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream())); + } + + @Test + void copiesCharactersThroughEveryOverload() throws Exception { + String source = "characters-å"; + StringWriter encoded = new StringWriter(); + IOUtils.copy(new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)), encoded, + StandardCharsets.UTF_8); + assertEquals(source, encoded.toString()); + + StringWriter regular = new StringWriter(); + assertEquals(source.length(), IOUtils.copy(new StringReader(source), regular)); + assertEquals(source, regular.toString()); + + StringWriter buffered = new StringWriter(); + assertEquals(source.length(), IOUtils.copyLarge(new StringReader(source), buffered, new char[2])); + assertEquals(source, buffered.toString()); + + StringWriter large = new StringWriter(); + assertEquals(source.length(), IOUtils.copyLarge(new StringReader(source), large)); + assertEquals(source, large.toString()); + assertEquals(0, IOUtils.copyLarge(new StringReader(""), new StringWriter())); + assertEquals(source, IOUtils.toString( + new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)); + } + + @Test + void integerCopyMethodsReturnEofMarkerForCountsOverIntegerMaximum() throws Exception { + try (MockedStatic mocked = mockStatic(IOUtils.class, CALLS_REAL_METHODS)) { + mocked.when(() -> IOUtils.copyLarge(any(InputStream.class), any(OutputStream.class))) + .thenReturn((long) Integer.MAX_VALUE + 1); + assertEquals(IOUtils.EOF, + IOUtils.copy(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream())); + + mocked.when(() -> IOUtils.copyLarge(any(Reader.class), any(Writer.class))) + .thenReturn((long) Integer.MAX_VALUE + 1); + assertEquals(IOUtils.EOF, IOUtils.copy(new StringReader(""), new StringWriter())); + } + } + + @SuppressWarnings("deprecation") + @Test + void writesFilesUsingEveryOverload() throws Exception { + Path byName = tempDir.resolve("name.txt"); + IOUtils.writeStringToFile(byName.toString(), "default"); + assertEquals("default", Files.readString(byName)); + + Path namedCharset = tempDir.resolve("name-charset.txt"); + IOUtils.writeStringToFile(namedCharset.toString(), "named", StandardCharsets.UTF_16); + assertEquals("named", Files.readString(namedCharset, StandardCharsets.UTF_16)); + + Path byPath = tempDir.resolve("path.txt"); + IOUtils.writeStringToFile(byPath, "path"); + assertEquals("path", Files.readString(byPath, Charsets.UTF_8)); + + Path byFile = tempDir.resolve("file.txt"); + IOUtils.writeStringToFile(byFile.toFile(), "file", StandardCharsets.UTF_8); + assertEquals("file", Files.readString(byFile)); + } + + @SuppressWarnings("deprecation") + @Test + void deletesFilesUsingEveryOverloadAndWrapsIoFailures() throws Exception { + Path byName = Files.createFile(tempDir.resolve("name-delete.txt")); + IOUtils.deleteFile(byName.toString()); + assertFalse(Files.exists(byName)); + + Path byFile = Files.createFile(tempDir.resolve("file-delete.txt")); + IOUtils.deleteFile(byFile.toFile()); + assertFalse(Files.exists(byFile)); + + Path first = Files.createFile(tempDir.resolve("first.txt")); + Path second = Files.createFile(tempDir.resolve("second.txt")); + IOUtils.deleteFiles(first.toString(), second.toString()); + assertFalse(Files.exists(first)); + assertFalse(Files.exists(second)); + + Path nonEmptyDirectory = Files.createDirectory(tempDir.resolve("non-empty")); + Files.createFile(nonEmptyDirectory.resolve("child")); + IllegalArgumentException error = assertThrows(IllegalArgumentException.class, + () -> IOUtils.deleteFile(nonEmptyDirectory)); + assertInstanceOf(IOException.class, error.getCause()); + } + + @Test + void constructorAndInvalidInputsAreCovered() { + assertNotNull(new IOUtils()); + assertThrows(NullPointerException.class, () -> IOUtils.toByteArray(null)); + assertThrows(NegativeArraySizeException.class, + () -> IOUtils.copy(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream(), -1)); + } +} diff --git a/src/test/java/net/locusworks/common/migration/MigrationItemTest.java b/src/test/java/net/locusworks/common/migration/MigrationItemTest.java new file mode 100644 index 0000000..35d613a --- /dev/null +++ b/src/test/java/net/locusworks/common/migration/MigrationItemTest.java @@ -0,0 +1,83 @@ +package net.locusworks.common.migration; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationInfo; +import org.flywaydb.core.api.MigrationInfoService; +import org.flywaydb.core.api.configuration.Configuration; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + + +class MigrationItemTest { + private static Flyway flyway(MigrationInfo[] all, MigrationInfo[] pending) { + Flyway flyway = mock(Flyway.class); + MigrationInfoService info = mock(MigrationInfoService.class); + Configuration configuration = mock(Configuration.class); + when(flyway.info()).thenReturn(info); + when(info.all()).thenReturn(all); + when(info.pending()).thenReturn(pending); + when(flyway.getConfiguration()).thenReturn(configuration); + when(configuration.getSchemas()).thenReturn(new String[]{"first", "second"}); + return flyway; + } + + @Test + void exposesSchemasPendingCountAndMigrationLog() { + Flyway flyway = flyway(new MigrationInfo[0], new MigrationInfo[]{mock(MigrationInfo.class)}); + MigrationItem item = new MigrationItem(flyway); + assertArrayEquals(new String[]{"first", "second"}, item.getSchemas()); + assertEquals(1, item.qtyPending()); + assertNotNull(item.getAllMigrationsLog()); + } + + @Test + void repairAndMigrateInvokeCallbackAndFlyway() { + Flyway flyway = flyway(new MigrationInfo[0], new MigrationInfo[0]); + List messages = new ArrayList<>(); + MigrationItem item = new MigrationItem(flyway, messages::add); + item.repair(); + item.migrate(); + verify(flyway).repair(); + verify(flyway).migrate(); + assertEquals(2, messages.size()); + assertTrue(messages.stream().allMatch(s -> s.contains("first, second"))); + } + + @Test + void repairAndMigrateWrapFlywayFailures() { + Flyway repairFlyway = flyway(new MigrationInfo[0], new MigrationInfo[0]); + doThrow(new IllegalStateException("repair failed")).when(repairFlyway).repair(); + RuntimeException repair = assertThrows(RuntimeException.class, () -> new MigrationItem(repairFlyway).repair()); + assertTrue(repair.getMessage().contains("repair failed")); + + Flyway migrateFlyway = flyway(new MigrationInfo[0], new MigrationInfo[0]); + doThrow(new IllegalStateException("migrate failed")).when(migrateFlyway).migrate(); + RuntimeException migrate = assertThrows(RuntimeException.class, () -> new MigrationItem(migrateFlyway).migrate()); + assertTrue(migrate.getMessage().contains("migrate failed")); + } + + @Test + void baseManagerInitializesMutableMigrationList() { + class Manager extends BaseMigrationManager { + @Override + public void migrate() { + } + } + Manager manager = new Manager(); + assertNotNull(manager.migrations); + assertDoesNotThrow(manager::migrate); + } +} diff --git a/src/test/java/net/locusworks/common/net/HttpClientHelperTest.java b/src/test/java/net/locusworks/common/net/HttpClientHelperTest.java new file mode 100644 index 0000000..610f3d7 --- /dev/null +++ b/src/test/java/net/locusworks/common/net/HttpClientHelperTest.java @@ -0,0 +1,45 @@ +package net.locusworks.common.net; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.sun.net.httpserver.HttpServer; +import org.junit.jupiter.api.Test; + +import java.net.InetSocketAddress; + +class HttpClientHelperTest { + @Test + void schemaLookupHandlesBothProtocolsAndUnknownValue() { + assertEquals(HttpClientHelper.HttpSchema.HTTP, HttpClientHelper.HttpSchema.findEnum("http")); + assertEquals(HttpClientHelper.HttpSchema.HTTPS, HttpClientHelper.HttpSchema.findEnum("HtTpS")); + assertNull(HttpClientHelper.HttpSchema.findEnum("ftp")); + } + + @Test + void constructorRejectsUnknownProtocol() { + Exception error = assertThrows(Exception.class, () -> new HttpClientHelper("ftp", "localhost", "21")); + assertEquals("Unable to find http schema of ftp", error.getMessage()); + } + + @Test + void executesHttpGetAndConsumesResponse() throws Exception { + HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 0), 0); + server.createContext("/status", exchange -> { + byte[] body = "ok".getBytes(); + exchange.sendResponseHeaders(202, body.length); + exchange.getResponseBody().write(body); + exchange.close(); + }); + server.start(); + try { + HttpClientHelper helper = new HttpClientHelper("http", "localhost", Integer.toString(server.getAddress().getPort())); + assertEquals(202, helper.getResponseCode("/status")); + assertNotNull(new HttpClientHelper("https", "localhost", "443")); + } finally { + server.stop(0); + } + } +} diff --git a/src/test/java/net/locusworks/common/net/certmanagers/TrustAllCertsManagerTest.java b/src/test/java/net/locusworks/common/net/certmanagers/TrustAllCertsManagerTest.java new file mode 100644 index 0000000..ac285bd --- /dev/null +++ b/src/test/java/net/locusworks/common/net/certmanagers/TrustAllCertsManagerTest.java @@ -0,0 +1,25 @@ +package net.locusworks.common.net.certmanagers; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +import javax.net.ssl.TrustManager; + + +class TrustAllCertsManagerTest { + @Test + void trustsClientsAndServersAndBuildsTrustManagerArray() throws Exception { + TrustAllCertsManager manager = new TrustAllCertsManager(); + assertDoesNotThrow(() -> manager.checkClientTrusted(null, null)); + assertDoesNotThrow(() -> manager.checkServerTrusted(null, null)); + assertNull(manager.getAcceptedIssuers()); + + TrustManager[] managers = TrustAllCertsManager.trustAllCerts(); + assertEquals(1, managers.length); + assertInstanceOf(TrustAllCertsManager.class, managers[0]); + } +} diff --git a/src/test/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifierTest.java b/src/test/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifierTest.java new file mode 100644 index 0000000..2c13522 --- /dev/null +++ b/src/test/java/net/locusworks/common/net/hostverifiers/AllHostValidVerifierTest.java @@ -0,0 +1,17 @@ +package net.locusworks.common.net.hostverifiers; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AllHostValidVerifierTest { + + @Test + public void testVerify() { + AllHostValidVerifier verifier = new AllHostValidVerifier(); + assertTrue(verifier.verify("localhost", null)); + assertTrue(verifier.verify("127.0.0.1", null)); + assertTrue(verifier.verify("::1", null)); + } + +} \ No newline at end of file diff --git a/src/test/java/net/locusworks/common/net/ssl/SSLManagerTest.java b/src/test/java/net/locusworks/common/net/ssl/SSLManagerTest.java new file mode 100644 index 0000000..66266e0 --- /dev/null +++ b/src/test/java/net/locusworks/common/net/ssl/SSLManagerTest.java @@ -0,0 +1,25 @@ +package net.locusworks.common.net.ssl; + +import org.junit.jupiter.api.Test; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +class SSLManagerTest { + + @Test + public void testCreateSSLContext() throws NoSuchAlgorithmException, KeyManagementException { + assertNotNull(SSLManager.getTrustAllTLSClient()); + } + + @Test + public void testTLS() { + assertNotNull(SSLManager.TLS); + assertEquals(4, SSLManager.TLS.length); + } + +} \ No newline at end of file diff --git a/src/test/java/net/locusworks/common/objectmapper/ObjectMapperCoverageTest.java b/src/test/java/net/locusworks/common/objectmapper/ObjectMapperCoverageTest.java new file mode 100644 index 0000000..a56d2d5 --- /dev/null +++ b/src/test/java/net/locusworks/common/objectmapper/ObjectMapperCoverageTest.java @@ -0,0 +1,119 @@ +package net.locusworks.common.objectmapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + + +class ObjectMapperCoverageTest { + static class Value { + public String name; + + Value() { + } + + Value(String name) { + this.name = name; + } + } + + static class SelfReference { + SelfReference self = this; + } + + static class CyclicReference { + Object next; + } + + static class ThrowingGetter { + String name = "field value"; + + public String getName() { + throw new IllegalStateException("Jackson getter failure"); + } + } + + @Test + void resultContainersSupportEveryConstructorMutationAndHandlerPath() { + RuntimeException error = new RuntimeException("bad json"); + ObjectMapperResults result = new ObjectMapperResults<>("value", null); + assertEquals("value", result.getResults()); + assertFalse(result.hasError()); + assertSame(result, result.withErrorHandler(e -> fail("handler must not run"))); + + result.setResults("changed"); + result.setException(error); + AtomicReference handled = new AtomicReference<>(); + assertSame(result, result.withErrorHandler(handled::set)); + assertSame(error, handled.get()); + assertSame(result, result.withErrorHandler(null)); + + assertEquals("simple", new ObjectMapperResults<>("simple").getResults()); + assertSame(error, new ObjectMapperResults(error).getException()); + + ObjectMapperListResults> list = new ObjectMapperListResults<>(List.of("a"), null); + assertEquals(List.of("a"), list.getResults()); + assertFalse(list.hasError()); + assertSame(list, list.withErrorHandler(e -> fail("handler must not run"))); + + ObjectMapperListResults> failed = new ObjectMapperListResults<>(error); + handled.set(null); + assertSame(failed, failed.withErrorHandler(handled::set)); + assertSame(error, handled.get()); + assertSame(failed, failed.withErrorHandler(null)); + assertEquals(List.of("b"), new ObjectMapperListResults<>(List.of("b")).getResults()); + } + + @Test + void readsBytesStringsObjectsAndCollectionImplementations() { + Value fromBytes = ObjectMapperHelper.readValue("{\"name\":\"bytes\"}".getBytes(), Value.class).getResults(); + assertEquals("bytes", fromBytes.name); + assertEquals("string", ObjectMapperHelper.readValue("{\"name\":\"string\"}", Value.class).getResults().name); + assertEquals("object", ObjectMapperHelper.readValue(new Value("object"), Value.class).getResults().name); + Object delegatedJson = "{\"name\":\"delegated\"}"; + assertEquals("delegated", ObjectMapperHelper.readValue(delegatedJson, Value.class).getResults().name); + + List values = List.of(new Value("one"), new Value("two")); + assertEquals(2, ObjectMapperHelper.readListValue(values, Value.class).getResults().size()); + assertEquals(2, ObjectMapperHelper.readListValue("[{\"name\":\"one\"},{\"name\":\"two\"}]", Value.class, LinkedList.class).getResults().size()); + assertInstanceOf(LinkedList.class, + ObjectMapperHelper.readListValue(values, Value.class, LinkedList.class).getResults()); + } + + @Test + void malformedInputReturnsErrorsForEveryReadOverload() { + assertTrue(ObjectMapperHelper.readValue(new byte[]{1, 2}, Value.class).hasError()); + assertTrue(ObjectMapperHelper.readValue("not-json", Value.class).hasError()); + assertTrue(ObjectMapperHelper.readValue(new Object(), Thread.class).hasError()); + assertTrue(ObjectMapperHelper.readListValue("not-json", Value.class, ArrayList.class).hasError()); + assertTrue(ObjectMapperHelper.readListValue(new Object(), Value.class, List.class).hasError()); + } + + @Test + void writeFallsBackWhenJacksonAndGsonRejectCircularObject() { + CyclicReference first = new CyclicReference(); + CyclicReference second = new CyclicReference(); + first.next = second; + second.next = first; + ObjectMapperResults result = ObjectMapperHelper.writeValue(first); + assertTrue(result.hasError()); + + ObjectMapperResults fallback = ObjectMapperHelper.writeValue(new ThrowingGetter()); + assertFalse(fallback.hasError()); + assertTrue(fallback.getResults().contains("field value")); + + assertNotNull(new ObjectMapperHelper()); + assertTrue(ObjectMapperHelper.readValue(new SelfReference(), Value.class).hasError()); + } +} diff --git a/src/test/java/net/locusworks/common/properties/ImmutablePropertiesTest.java b/src/test/java/net/locusworks/common/properties/ImmutablePropertiesTest.java new file mode 100644 index 0000000..f2182cd --- /dev/null +++ b/src/test/java/net/locusworks/common/properties/ImmutablePropertiesTest.java @@ -0,0 +1,41 @@ +package net.locusworks.common.properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + + +class ImmutablePropertiesTest { + + @Test + public void testImmutableProperties() { + assertNotNull(new ImmutableProperties()); + } + + @Test + public void testImmutablePropertiesWithEmptyOrNullProperties() { + assertNotNull(new ImmutableProperties(new Properties())); + assertNotNull(new ImmutableProperties(null)); + } + + @Test + public void testImmutablePropertiesWthProperties() { + final Properties properties = new Properties(); + properties.put("Hello", "World"); + assertNotNull(new ImmutableProperties(properties)); + } + + @Test + public void testSetProperties() { + final ImmutableProperties properties = new ImmutableProperties(new Properties()); + properties.setProperty("Hello", "World"); + assertEquals("World", properties.getProperty("Hello")); + + assertThrows(RuntimeException.class, () -> properties.setProperty("Hello", "World")); + } + +} \ No newline at end of file diff --git a/src/test/java/net/locusworks/common/properties/OrderedPropertiesCoverageTest.java b/src/test/java/net/locusworks/common/properties/OrderedPropertiesCoverageTest.java new file mode 100644 index 0000000..038af67 --- /dev/null +++ b/src/test/java/net/locusworks/common/properties/OrderedPropertiesCoverageTest.java @@ -0,0 +1,196 @@ +package net.locusworks.common.properties; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +class OrderedPropertiesCoverageTest { + @Test + void loadsAllPropertySyntaxFromReaderAndInputStream() throws Exception { + String source = "# comment\n! another\n" + + "simple=value\ncolon:value2\nspace value3\n" + + "spaced = spaced-value\n" + + "tabbed\t:\tvalue5\nform\f=\fvalue6\n" + + "slashes\\\\key:value4\n" + + "escaped\\:colon=value7\n" + + "escaped\\ key=escaped\\ value\n" + + "unicode=\\u0041\\tB\\nC\\rD\\fE\n" + + "continued=first\\\n second\n" + + "emptyKey\n=empty-key-value\n"; + + OrderedProperties readerProperties = new OrderedProperties(); + readerProperties.load(new StringReader(source)); + assertEquals("value", readerProperties.getProperty("simple")); + assertEquals("value2", readerProperties.getProperty("colon")); + assertEquals("value3", readerProperties.getProperty("space")); + assertEquals("spaced-value", readerProperties.getProperty("spaced")); + assertEquals("value4", readerProperties.getProperty("slashes\\key")); + assertEquals("value5", readerProperties.getProperty("tabbed")); + assertEquals("value6", readerProperties.getProperty("form")); + assertEquals("value7", readerProperties.getProperty("escaped:colon")); + assertEquals("escaped value", readerProperties.getProperty("escaped key")); + assertEquals("A\tB\nC\rD\fE", readerProperties.getProperty("unicode")); + assertEquals("firstsecond", readerProperties.getProperty("continued")); + assertEquals("", readerProperties.getProperty("emptyKey")); + assertEquals("empty-key-value", readerProperties.getProperty("")); + + OrderedProperties streamProperties = new OrderedProperties(); + streamProperties.load(new ByteArrayInputStream(source.getBytes(StandardCharsets.ISO_8859_1))); + assertEquals(readerProperties, streamProperties); + assertThrows(IllegalArgumentException.class, + () -> streamProperties.load(new StringReader("bad=\\u12G4"))); + } + + @Test + void storesToWriterAndStreamWithCommentsUnicodeAndEscapes() throws Exception { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty(" leading:=#!", " value\t\n\r\f\\é"); + + StringWriter writer = new StringWriter(); + properties.store(writer, "first\nsecond\rthird\r\nfourth\u0100"); + String writerText = writer.toString(); + assertTrue(writerText.contains("#first")); + assertTrue(writerText.contains("#second")); + assertTrue(writerText.contains("\\ leading\\:\\=\\#\\!")); + assertTrue(writerText.contains("é")); + + StringWriter bufferedTarget = new StringWriter(); + properties.store(new BufferedWriter(bufferedTarget), "\u0100first\rsolo\n#already\n!also\nlast\r\nend\r"); + assertTrue(bufferedTarget.toString().contains("#already")); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + properties.store(bytes, null); + String streamText = bytes.toString(StandardCharsets.ISO_8859_1); + assertTrue(streamText.contains("\\u00E9")); + + assertDoesNotThrow(() -> properties.save(new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException("expected"); + } + }, "ignored error")); + } + + @Test + void defaultsEnumerationListingAndNonStringValuesAreHandled() { + OrderedProperties defaults = new OrderedProperties(); + defaults.setProperty("default", "value"); + defaults.put("nonStringDefault", 1); + OrderedProperties properties = new OrderedProperties(defaults); + properties.setProperty("local", "x".repeat(50)); + properties.put("nonString", 2); + + assertEquals("value", properties.getProperty("default")); + assertEquals("fallback", properties.getProperty("missing", "fallback")); + assertEquals("value", properties.getProperty("default", "fallback")); + Enumeration names = properties.propertyNames(); + assertTrue(java.util.Collections.list(names).containsAll(List.of("default", "local"))); + properties.put(3, "nonStringKey"); + assertTrue(properties.stringPropertyNames().containsAll(java.util.Set.of("default", "local"))); + + properties.remove("nonString"); + properties.remove(3); + defaults.remove("nonStringDefault"); + + ByteArrayOutputStream printBytes = new ByteArrayOutputStream(); + properties.list(new PrintStream(printBytes)); + assertTrue(printBytes.toString().contains("...")); + StringWriter output = new StringWriter(); + properties.list(new PrintWriter(output)); + assertTrue(output.toString().contains("-- listing properties --")); + } + + @Test + void lineReaderHandlesCommentsWhitespaceContinuationLongLinesAndEndings() throws Exception { + String longValue = "x".repeat(9000); + String input = " #comment\r\n\t!comment\n key=value\\\r\n continued\r" + longValue; + OrderedProperties.LineReader reader = new OrderedProperties.LineReader(new StringReader(input)); + int length = reader.readLine(); + assertEquals("key=valuecontinued", new String(reader.lineBuf, 0, length)); + assertEquals(9000, reader.readLine()); + + OrderedProperties.LineReader stream = new OrderedProperties.LineReader( + new ByteArrayInputStream(("a=b\n" + longValue).getBytes(StandardCharsets.ISO_8859_1))); + assertEquals(3, stream.readLine()); + assertEquals(9000, stream.readLine()); + assertEquals(-1, stream.readLine()); + + OrderedProperties.LineReader trailingReader = new OrderedProperties.LineReader(new StringReader("key=value\\")); + assertEquals("key=value", new String(trailingReader.lineBuf, 0, trailingReader.readLine())); + OrderedProperties.LineReader trailingStream = new OrderedProperties.LineReader( + new ByteArrayInputStream("key=value\\\n".getBytes(StandardCharsets.ISO_8859_1))); + assertEquals("key=value", new String(trailingStream.lineBuf, 0, trailingStream.readLine())); + OrderedProperties.LineReader whiteContinuation = new OrderedProperties.LineReader( + new StringReader("key=value\\\n \t\fcontinued\n")); + int continuedLength = whiteContinuation.readLine(); + assertEquals("key=valuecontinued", new String(whiteContinuation.lineBuf, 0, continuedLength)); + assertEquals(-1, new OrderedProperties.LineReader(new StringReader("#comment without newline")).readLine()); + + OrderedProperties.LineReader crWithoutLf = new OrderedProperties.LineReader(new StringReader("a\\\rb\n")); + int crLength = crWithoutLf.readLine(); + assertEquals("ab", new String(crWithoutLf.lineBuf, 0, crLength)); + + OrderedProperties.LineReader blankEndings = new OrderedProperties.LineReader(new StringReader("\n\r\nvalue\n")); + assertEquals("value", new String(blankEndings.lineBuf, 0, blankEndings.readLine())); + + String boundaryInput = "z".repeat(8191) + "\nnext\n"; + OrderedProperties.LineReader boundary = new OrderedProperties.LineReader(new StringReader(boundaryInput)); + assertEquals(8191, boundary.readLine()); + assertEquals(4, boundary.readLine()); + } + + @Test + void privateConversionHelpersCoverEveryEscapeAndHexPath() throws Exception { + OrderedProperties properties = new OrderedProperties(); + Method load = OrderedProperties.class.getDeclaredMethod("loadConvert", char[].class, int.class, int.class, char[].class); + Method save = OrderedProperties.class.getDeclaredMethod("saveConvert", String.class, boolean.class, boolean.class); + Method hex = OrderedProperties.class.getDeclaredMethod("toHex", int.class); + load.setAccessible(true); + save.setAccessible(true); + hex.setAccessible(true); + + String escaped = "a\\tb\\nc\\rd\\fe\\\\f\\ g\\:h\\=i\\#j\\!k\\u0041"; + assertEquals("a\tb\nc\rd\fe\\f g:h=i#j!kA", + load.invoke(properties, escaped.toCharArray(), 0, escaped.length(), new char[1])); + String hexCases = "\\u00af\\u00AF"; + assertEquals("¯¯", load.invoke(properties, hexCases.toCharArray(), 0, hexCases.length(), new char[1])); + assertEquals("\\ a\\:\\=\\#\\!\\t\\n\\r\\f\\\\\\u0100", + save.invoke(properties, " a:=#!\t\n\r\f\\\u0100", true, true)); + assertEquals("\\ a\\:\\=\\#\\!\\t\\n\\r\\f\\\\\u0100", + save.invoke(properties, " a:=#!\t\n\r\f\\\u0100", false, false)); + assertEquals("a\\ b", save.invoke(properties, "a b", true, false)); + assertEquals("a b", save.invoke(properties, "a b", false, false)); + assertEquals("\\u0001", save.invoke(properties, "\u0001", false, true)); + assertEquals("\u0001", save.invoke(properties, "\u0001", false, false)); + assertEquals("0", save.invoke(properties, "0", false, true)); + assertEquals('0', hex.invoke(null, 0)); + assertEquals('F', hex.invoke(null, 15)); + } + + @Test + void immutablePropertiesCopiesValuesAndRejectsReplacement() { + Properties source = new Properties(); + source.setProperty("key", "value"); + ImmutableProperties immutable = new ImmutableProperties(source); + assertEquals("value", immutable.getProperty("key")); + assertThrows(RuntimeException.class, () -> immutable.setProperty("key", "changed")); + } +} diff --git a/src/test/java/net/locusworks/common/properties/OrderedPropertiesTest.java b/src/test/java/net/locusworks/common/properties/OrderedPropertiesTest.java new file mode 100644 index 0000000..49bebbd --- /dev/null +++ b/src/test/java/net/locusworks/common/properties/OrderedPropertiesTest.java @@ -0,0 +1,75 @@ +package net.locusworks.common.properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + + +class OrderedPropertiesTest { + + @Test + void testSetProperty() { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + assertEquals("World", properties.getProperty("Hello")); + } + + @Test + void testLoad() throws IOException { + OrderedProperties properties = new OrderedProperties(); + properties.load(new ByteArrayInputStream("Hello=World".getBytes())); + assertEquals("World", properties.getProperty("Hello")); + } + + @Test + void testSave() { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + properties.save(new ByteArrayOutputStream(), ""); + } + + @Test + void testStore() { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + try { + properties.store(new ByteArrayOutputStream(), ""); + } catch (IOException e) { + fail("IOException should not be thrown"); + } + } + + @Test + void testGetProperty() { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + assertEquals("World", properties.getProperty("Hello")); + } + + @Test + void testPropertyNames() { + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + var propertyNames = properties.stringPropertyNames(); + assertFalse(propertyNames.isEmpty()); + assertTrue(propertyNames.contains("Hello")); + assertEquals(1, propertyNames.size()); + } + + @Test + void testList() { + PrintStream ps = new PrintStream(new ByteArrayOutputStream()); + OrderedProperties properties = new OrderedProperties(); + properties.setProperty("Hello", "World"); + properties.list(ps); + ps.flush(); + } +} \ No newline at end of file diff --git a/src/test/java/net/locusworks/common/utils/ChecksTest.java b/src/test/java/net/locusworks/common/utils/ChecksTest.java new file mode 100644 index 0000000..891668b --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/ChecksTest.java @@ -0,0 +1,28 @@ +package net.locusworks.common.utils; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + + +class ChecksTest { + + @Test + void testCheckArgument() { + Throwable ex = assertThrows(IllegalArgumentException.class, () -> Checks.checkArguments(false, "One does not equal 2")); + assertEquals("One does not equal 2", ex.getMessage()); + + assertDoesNotThrow(() -> Checks.checkArguments(true, "One does not equal 2")); + } + + @Test + void testCheckState() { + Throwable ex = assertThrows(IllegalStateException.class, () -> Checks.checkState(false, "One does not equal 2")); + assertEquals("One does not equal 2", ex.getMessage()); + + assertDoesNotThrow(() -> Checks.checkState(true, "One does not equal 2")); + } + +} diff --git a/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java b/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java index feb7c8d..f2268d0 100644 --- a/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java +++ b/src/test/java/net/locusworks/common/utils/DateTimeStampSerializerTest.java @@ -1,7 +1,17 @@ package net.locusworks.common.utils; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import com.fasterxml.jackson.annotation.ObjectIdGenerator; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.Base64Variant; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonStreamContext; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.introspect.Annotated; @@ -15,219 +25,262 @@ import java.math.BigInteger; import java.util.Date; import java.util.concurrent.atomic.AtomicLong; -import static org.junit.jupiter.api.Assertions.*; class DateTimeStampSerializerTest { - static AtomicLong atomicLong = new AtomicLong(0); + static AtomicLong atomicLong = new AtomicLong(0); - @Test - void testSerializer(){ - DateTimeStampSerializer serializer = new DateTimeStampSerializer(); - assertDoesNotThrow(() -> serializer.serialize(new Date(), new MyJsonGenerator(), new MySerializerProvider())); - assertTrue(atomicLong.get() > 0); - } - - private static class MyJsonGenerator extends JsonGenerator { - @Override public JsonGenerator setCodec(ObjectCodec objectCodec) { - return null; + @Test + void testSerializer() { + DateTimeStampSerializer serializer = new DateTimeStampSerializer(); + assertDoesNotThrow(() -> serializer.serialize(new Date(), new MyJsonGenerator(), new MySerializerProvider())); + assertTrue(atomicLong.get() > 0); } - @Override public ObjectCodec getCodec() { - return null; + private static class MyJsonGenerator extends JsonGenerator { + @Override + public JsonGenerator setCodec(ObjectCodec objectCodec) { + return null; + } + + @Override + public ObjectCodec getCodec() { + return null; + } + + @Override + public Version version() { + return null; + } + + @Override + public JsonStreamContext getOutputContext() { + return null; + } + + @Override + public JsonGenerator enable(Feature feature) { + return null; + } + + @Override + public JsonGenerator disable(Feature feature) { + return null; + } + + @Override + public boolean isEnabled(Feature feature) { + return false; + } + + @Override + public int getFeatureMask() { + return 0; + } + + @Override + public JsonGenerator setFeatureMask(int i) { + return null; + } + + @Override + public JsonGenerator useDefaultPrettyPrinter() { + return null; + } + + @Override + public void writeStartArray() { + fail(); + } + + @Override + public void writeEndArray() { + fail(); + } + + @Override + public void writeStartObject() { + fail(); + } + + @Override + public void writeEndObject() { + fail(); + } + + @Override + public void writeFieldName(String s) { + fail(); + } + + @Override + public void writeFieldName(SerializableString serializableString) { + fail(); + } + + @Override + public void writeString(String s) { + fail(); + } + + @Override + public void writeString(char[] chars, int i, int i1) { + fail(); + } + + @Override + public void writeString(SerializableString serializableString) { + fail(); + } + + @Override + public void writeRawUTF8String(byte[] bytes, int i, int i1) { + fail(); + } + + @Override + public void writeUTF8String(byte[] bytes, int i, int i1) { + fail(); + } + + @Override + public void writeRaw(String s) { + fail(); + } + + @Override + public void writeRaw(String s, int i, int i1) { + fail(); + } + + @Override + public void writeRaw(char[] chars, int i, int i1) { + fail(); + } + + @Override + public void writeRaw(char c) { + fail(); + } + + @Override + public void writeRawValue(String s) { + fail(); + } + + @Override + public void writeRawValue(String s, int i, int i1) { + fail(); + } + + @Override + public void writeRawValue(char[] chars, int i, int i1) { + fail(); + } + + @Override + public void writeBinary(Base64Variant base64Variant, byte[] bytes, int i, int i1) { + fail(); + } + + @Override + public int writeBinary(Base64Variant base64Variant, InputStream inputStream, int i) { + fail(); + return 0; + } + + @Override + public void writeNumber(int i) { + fail(); + } + + @Override + public void writeNumber(long l) { + atomicLong.set(l); + } + + @Override + public void writeNumber(BigInteger bigInteger) { + fail(); + } + + @Override + public void writeNumber(double v) { + fail(); + } + + @Override + public void writeNumber(float v) { + fail(); + } + + @Override + public void writeNumber(BigDecimal bigDecimal) { + fail(); + } + + @Override + public void writeNumber(String s) { + fail(); + } + + @Override + public void writeBoolean(boolean b) { + fail(); + } + + @Override + public void writeNull() { + fail(); + } + + @Override + public void writeObject(Object o) { + fail(); + } + + @Override + public void writeTree(TreeNode treeNode) { + fail(); + } + + @Override + public void flush() { + fail(); + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void close() { + fail(); + } } - @Override public Version version() { - return null; - } - @Override public JsonStreamContext getOutputContext() { - return null; - } + private static class MySerializerProvider extends SerializerProvider { + @Override + public WritableObjectId findObjectId(Object o, ObjectIdGenerator objectIdGenerator) { + return null; + } - @Override public JsonGenerator enable(Feature feature) { - return null; - } + @Override + public JsonSerializer serializerInstance(Annotated annotated, Object o) { + return null; + } - @Override public JsonGenerator disable(Feature feature) { - return null; - } + @Override + public Object includeFilterInstance(BeanPropertyDefinition beanPropertyDefinition, + Class aClass) { + return null; + } - @Override public boolean isEnabled(Feature feature) { - return false; + @Override + public boolean includeFilterSuppressNulls(Object o) { + return false; + } } - - @Override public int getFeatureMask() { - return 0; - } - - @Override public JsonGenerator setFeatureMask(int i) { - return null; - } - - @Override public JsonGenerator useDefaultPrettyPrinter() { - return null; - } - - @Override public void writeStartArray() { - fail(); - } - - @Override public void writeEndArray() { - fail(); - } - - @Override public void writeStartObject() { - fail(); - } - - @Override public void writeEndObject() { - fail(); - } - - @Override public void writeFieldName(String s) { - fail(); - } - - @Override public void writeFieldName(SerializableString serializableString) - { - fail(); - } - - @Override public void writeString(String s) { - fail(); - } - - @Override public void writeString(char[] chars, int i, int i1) { - fail(); - } - - @Override public void writeString(SerializableString serializableString) { - fail(); - } - - @Override public void writeRawUTF8String(byte[] bytes, int i, int i1) { - fail(); - } - - @Override public void writeUTF8String(byte[] bytes, int i, int i1) { - fail(); - } - - @Override public void writeRaw(String s) { - fail(); - } - - @Override public void writeRaw(String s, int i, int i1) { - fail(); - } - - @Override public void writeRaw(char[] chars, int i, int i1) { - fail(); - } - - @Override public void writeRaw(char c) { - fail(); - } - - @Override public void writeRawValue(String s) { - fail(); - } - - @Override public void writeRawValue(String s, int i, int i1) { - fail(); - } - - @Override public void writeRawValue(char[] chars, int i, int i1) { - fail(); - } - - @Override public void writeBinary(Base64Variant base64Variant, byte[] bytes, int i, int i1) - { - fail(); - } - - @Override public int writeBinary(Base64Variant base64Variant, InputStream inputStream, int i) - { - fail(); - return 0; - } - - @Override public void writeNumber(int i) { - fail(); - } - - @Override public void writeNumber(long l) { - atomicLong.set(l); - } - - @Override public void writeNumber(BigInteger bigInteger) { - fail(); - } - - @Override public void writeNumber(double v) { - fail(); - } - - @Override public void writeNumber(float v) { - fail(); - } - - @Override public void writeNumber(BigDecimal bigDecimal) { - fail(); - } - - @Override public void writeNumber(String s) { - fail(); - } - - @Override public void writeBoolean(boolean b) { - fail(); - } - - @Override public void writeNull() { - fail(); - } - - @Override public void writeObject(Object o) { - fail(); - } - - @Override public void writeTree(TreeNode treeNode) { - fail(); - } - - @Override public void flush() { - fail(); - } - - @Override public boolean isClosed() { - return false; - } - - @Override public void close() { - fail(); - } - } - - - private static class MySerializerProvider extends SerializerProvider { - @Override - public WritableObjectId findObjectId(Object o, ObjectIdGenerator objectIdGenerator) { - return null; - } - - @Override public JsonSerializer serializerInstance(Annotated annotated, Object o) { - return null; - } - - @Override public Object includeFilterInstance(BeanPropertyDefinition beanPropertyDefinition, - Class aClass) { - return null; - } - - @Override public boolean includeFilterSuppressNulls(Object o) { - return false; - } - } } diff --git a/src/test/java/net/locusworks/common/utils/FileReaderTest.java b/src/test/java/net/locusworks/common/utils/FileReaderTest.java index 330bb03..18b1ee6 100644 --- a/src/test/java/net/locusworks/common/utils/FileReaderTest.java +++ b/src/test/java/net/locusworks/common/utils/FileReaderTest.java @@ -1,110 +1,275 @@ package net.locusworks.common.utils; import net.locusworks.common.interfaces.AutoCloseableIterator; -import net.locusworks.common.utils.FileReader; import net.locusworks.common.utils.FileReader.LineInfo; -import net.locusworks.common.utils.RandomString; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import java.io.File; -import java.io.FileOutputStream; +import java.io.*; +import java.nio.file.Path; import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; +import java.util.NoSuchElementException; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mockStatic; public class FileReaderTest { - private static final String TEST_FILE = "test_file.txt"; - private static final Map numLines = new LinkedHashMap<>(); + private static final String TEST_FILE = "test_file.txt"; + private static final Map numLines = new LinkedHashMap<>(); + private BufferedReader testBufferedReader; - @BeforeAll - public static void setUpBeforeClass() throws Exception { - File testFile = new File(TEST_FILE); - FileOutputStream fos = new FileOutputStream(testFile); + private static final String[] TEST_CONTENT = new String[]{ + "Line 1", + "Line 2", + "Line 3" + }; - int count = ThreadLocalRandom.current().nextInt(100); - for (int i = 1; i <= count; i++) { - String randomString = RandomString.getInstance().getString(ThreadLocalRandom.current().nextInt(5, 100)) + "\n"; - numLines.put(i, randomString.length()); - fos.write(randomString.getBytes()); + @BeforeAll + public static void setUpBeforeClass() throws Exception { + File testFile = new File(TEST_FILE); + FileOutputStream fos = new FileOutputStream(testFile); + + for (int i = 1; i <= TEST_CONTENT.length; i++) { + String randomString = TEST_CONTENT[i - 1] + "\n"; + numLines.put(i, randomString.length()); + fos.write(randomString.getBytes()); + } + + fos.close(); } - fos.close(); - } - - @AfterAll - public static void tearDownAfterClass() throws Exception { - File file = new File(TEST_FILE); - file.delete(); - } - - @Test - public void testForLoop() { - int lineCount = 0; - try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { - for (LineInfo s : fr) { - lineCount++; - Integer lineNumber = s.getLineNumber(); - int lineLength = s.getLine().length(); - Integer mapLineLength = numLines.get(lineNumber); - assertEquals(lineLength, (mapLineLength - 1)); - } + @BeforeEach + void setUp() throws FileNotFoundException { + testBufferedReader = new BufferedReader(new java.io.FileReader(TEST_FILE)); } - assertEquals(lineCount, numLines.size()); - } - @Test - public void testIterator() { - int lineCount = 0; - - try(AutoCloseableIterator iter = new FileReader(Paths.get(TEST_FILE))) { - while(iter.hasNext()) { - lineCount++; - LineInfo s = iter.next(); - Integer lineNumber = s.getLineNumber(); - int lineLength = s.getLine().length(); - Integer mapLineLength = numLines.get(lineNumber); - assertEquals(lineLength, (mapLineLength - 1)); - } + @AfterEach + void tearDown() { + try { + if (testBufferedReader != null) { + testBufferedReader.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } } - assertEquals(lineCount, numLines.size()); - } - @Test - public void testForIterator() { - int lineCount = 0; - try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { - while (((Iterator) fr).hasNext()) { - lineCount++; - LineInfo s = ((Iterator) fr).next(); - Integer lineNumber = s.getLineNumber(); - int lineLength = s.getLine().length(); - Integer mapLineLength = numLines.get(lineNumber); - assertEquals(lineLength, (mapLineLength - 1)); - } - assertEquals(lineCount, numLines.size()); + @AfterAll + public static void tearDownAfterClass() { + File file = new File(TEST_FILE); + file.delete(); } - } - @Test - public void testNoAlgorithmException() { - try(MockedStatic mocked = mockStatic(SecureRandom.class)) { - mocked.when(() -> SecureRandom.getInstance(anyString())).thenThrow(NoSuchAlgorithmException.class); - assertDoesNotThrow(() -> RandomString.newInstance()); + @Test + public void testForLoop() { + int lineCount = 0; + try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { + for (LineInfo s : fr) { + lineCount++; + Integer lineNumber = s.getLineNumber(); + int lineLength = s.getLine().length(); + Integer mapLineLength = numLines.get(lineNumber); + assertEquals(lineLength, (mapLineLength - 1)); + } + } + assertEquals(lineCount, numLines.size()); } - } + @Test + void testDeprecatedConstructor() { + try (FileReader fr = new FileReader(new File(TEST_FILE))) { + assertNotNull(fr); + } + } + + @Test + void testInit() { + + } + + @Test + public void testIterator() { + int lineCount = 0; + + try (AutoCloseableIterator iter = new FileReader(Paths.get(TEST_FILE))) { + while (iter.hasNext()) { + lineCount++; + LineInfo s = iter.next(); + Integer lineNumber = s.getLineNumber(); + int lineLength = s.getLine().length(); + Integer mapLineLength = numLines.get(lineNumber); + assertEquals(lineLength, (mapLineLength - 1)); + } + } + assertEquals(lineCount, numLines.size()); + } + + @Test + public void testForIterator() { + int lineCount = 0; + try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) { + while (((Iterator) fr).hasNext()) { + lineCount++; + LineInfo s = ((Iterator) fr).next(); + Integer lineNumber = s.getLineNumber(); + int lineLength = s.getLine().length(); + Integer mapLineLength = numLines.get(lineNumber); + assertEquals(lineLength, (mapLineLength - 1)); + } + assertEquals(lineCount, numLines.size()); + } + } + + @Test + public void testNoAlgorithmException() { + try (MockedStatic mocked = mockStatic(SecureRandom.class)) { + mocked.when(() -> SecureRandom.getInstance(anyString())).thenThrow(NoSuchAlgorithmException.class); + assertDoesNotThrow(() -> RandomString.newInstance()); + } + } + + @Test + void testFileReaderWithBufferedReader() { + try (FileReader fileReader = new FileReader(testBufferedReader)) { + Iterator iterator = fileReader.iterator(); + + assertTrue(iterator.hasNext()); + FileReader.LineInfo lineInfo = iterator.next(); + assertEquals(1, lineInfo.getLineNumber()); + assertEquals("Line 1", lineInfo.getLine()); + + assertTrue(iterator.hasNext()); + lineInfo = iterator.next(); + assertEquals(2, lineInfo.getLineNumber()); + assertEquals("Line 2", lineInfo.getLine()); + + assertTrue(iterator.hasNext()); + lineInfo = iterator.next(); + assertEquals(3, lineInfo.getLineNumber()); + assertEquals("Line 3", lineInfo.getLine()); + + assertFalse(iterator.hasNext()); + } + } + + @Test + void testFileReaderWithFile() { + try (FileReader fileReader = new FileReader(TEST_FILE)) { + // Add assertions based on the content of the file + Iterator iterator = fileReader.iterator(); + + assertTrue(iterator.hasNext()); + FileReader.LineInfo lineInfo = iterator.next(); + assertNotNull(lineInfo); + } + } + + @Test + void testFileReaderWithNonexistentFile() { + // Provide the path to a nonexistent file for testing + String filePath = "path/to/nonexistent/file.txt"; + + // Ensure that the constructor throws an IllegalArgumentException + assertThrows(NullPointerException.class, () -> new FileReader(filePath)); + } + + @Test + void testFileReaderInitWithNullFile() { + // Ensure that the constructor throws an IllegalArgumentException with a null file + assertThrows(IllegalArgumentException.class, () -> new FileReader((String) null)); + } + + @Test + void testFileReaderInitWithNullBufferedReader() { + // Ensure that the constructor throws an IllegalArgumentException with a null BufferedReader + assertThrows(IllegalArgumentException.class, () -> new FileReader((BufferedReader) null)); + } + + @Test + void testFileReaderInitWithNonexistentResource() { + // Provide the name of a nonexistent resource for testing + String resourceName = "nonexistent_resource.txt"; + + // Ensure that the constructor throws an IllegalArgumentException + assertThrows(NullPointerException.class, () -> new FileReader(resourceName)); + } + + @Test + void testFileReaderInitWithInvalidResource() { + // Provide the name of an invalid resource for testing + String resourceName = "invalid_resource.txt"; + + // Ensure that the constructor throws a RuntimeException + assertThrows(RuntimeException.class, () -> new FileReader(resourceName)); + } + + @Test + void testFileReaderInitWithNullPath() { + // Ensure that the constructor throws an IllegalArgumentException with a null path + assertThrows(IllegalArgumentException.class, () -> new FileReader((Path) null)); + } + + @Test + void testFileReaderInitWithNonexistentPath() { + // Provide a nonexistent path for testing + Path path = Paths.get("nonexistent_path.txt"); + + // Ensure that the constructor throws a RuntimeException + assertThrows(RuntimeException.class, () -> new FileReader(path)); + } + + @Test + void testFileReaderInitWithNonRegularFile() { + // Provide the path to a non-regular file for testing + Path path = Paths.get("path/to/non_regular_file"); + + // Ensure that the constructor throws an IllegalArgumentException + assertThrows(IllegalArgumentException.class, () -> new FileReader(path)); + } + + @Test + void testFileReaderHasNextIOException() { + // Provide a BufferedReader that throws an IOException when reading + BufferedReader bufferedReader = new BufferedReader(new StringReader(TEST_FILE)) { + @Override + public String readLine() throws IOException { + throw new IOException("Simulated IOException"); + } + }; + + try (FileReader fileReader = new FileReader(bufferedReader)) { + Iterator iterator = fileReader.iterator(); + + assertThrows(RuntimeException.class, iterator::hasNext); + } + } + + @Test + void testFileReaderNextNoMoreElements() { + // Provide a BufferedReader with no more elements for testing + BufferedReader bufferedReader = new BufferedReader(new StringReader("")); + + try (FileReader fileReader = new FileReader(bufferedReader)) { + Iterator iterator = fileReader.iterator(); + + assertThrows(NoSuchElementException.class, iterator::next); + } + } } diff --git a/src/test/java/net/locusworks/common/utils/HashUtilsTest.java b/src/test/java/net/locusworks/common/utils/HashUtilsTest.java index 120f943..1729300 100644 --- a/src/test/java/net/locusworks/common/utils/HashUtilsTest.java +++ b/src/test/java/net/locusworks/common/utils/HashUtilsTest.java @@ -1,37 +1,36 @@ package net.locusworks.common.utils; -import net.locusworks.common.utils.HashUtils; import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class HashUtilsTest { - - private static final String TEST_STRING = "Hello World"; - @Test - public void testMD5() throws Exception { - String digestUtilsMD5 = DigestUtils.md5Hex(TEST_STRING.getBytes()); - String hashUtilsMD5 = HashUtils.hash("MD5", TEST_STRING); + private static final String TEST_STRING = "Hello World"; - assertEquals(digestUtilsMD5, hashUtilsMD5); - } - - @Test - public void testSHA1() throws Exception { - String digestUtilsMD5 = DigestUtils.sha1Hex(TEST_STRING.getBytes()); - String hashUtilsMD5 = HashUtils.hash("SHA-1", TEST_STRING); + @Test + public void testMD5() { + String digestUtilsMD5 = DigestUtils.md5Hex(TEST_STRING.getBytes()); + String hashUtilsMD5 = HashUtils.hash("MD5", TEST_STRING); - assertEquals(digestUtilsMD5, hashUtilsMD5); - } - - @Test - public void testSHA512() throws Exception { - String digestUtilsMD5 = DigestUtils.sha512Hex(TEST_STRING.getBytes()); - String hashUtilsMD5 = HashUtils.hash("SHA-512", TEST_STRING); + assertEquals(digestUtilsMD5, hashUtilsMD5); + } - assertEquals(digestUtilsMD5, hashUtilsMD5); - } + @Test + public void testSHA1() { + String digestUtilsMD5 = DigestUtils.sha1Hex(TEST_STRING.getBytes()); + String hashUtilsMD5 = HashUtils.hash("SHA-1", TEST_STRING); + + assertEquals(digestUtilsMD5, hashUtilsMD5); + } + + @Test + public void testSHA512() { + String digestUtilsMD5 = DigestUtils.sha512Hex(TEST_STRING.getBytes()); + String hashUtilsMD5 = HashUtils.hash("SHA-512", TEST_STRING); + + assertEquals(digestUtilsMD5, hashUtilsMD5); + } } diff --git a/src/test/java/net/locusworks/common/utils/ObjectUtilsTest.java b/src/test/java/net/locusworks/common/utils/ObjectUtilsTest.java new file mode 100644 index 0000000..7b7b739 --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/ObjectUtilsTest.java @@ -0,0 +1,81 @@ +package net.locusworks.common.utils; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import static net.locusworks.common.utils.ObjectUtils.performIfNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +class ObjectUtilsTest { + + private static final String TEST_FILE = "object-utils-test.txt"; + private static Path testFile; + + @BeforeAll + public static void setUpBeforeClass() throws Exception { + Path classpathRoot = Paths.get(Objects.requireNonNull(ObjectUtilsTest.class.getResource("/")).toURI()); + testFile = classpathRoot.resolve(TEST_FILE); + Files.writeString(testFile, "test resource"); + } + + @AfterAll + public static void tearDownAfterClass() throws Exception { + Files.deleteIfExists(testFile); + } + + @Test + void testNullObject() { + performIfNotNull(null, str -> fail()); + } + + @Test + void testNotNullObject() { + String str = "hello world"; + performIfNotNull(str, obj -> assertEquals(str, obj)); + } + + @Test + void getResourceStream_whenResourceExists_ReturnsInputStream() throws Exception { + try (InputStream result = ObjectUtils.getResourceStream("/" + TEST_FILE)) { + assertNotNull(result); + } + } + + @Test + void getResourceStream_whenResourceDoesNotExist_ThrowsException() { + // Arrange + String resourceName = "nonexistent.txt"; + + // Act & Assert + assertThrows(NullPointerException.class, + () -> ObjectUtils.getResourceStream(resourceName)); + } + + @Test + void getResourceStream_whenResourceExistsInClassLoader_ReturnsInputStream() throws Exception { + try (InputStream result = ObjectUtils.getResourceStream(TEST_FILE)) { + assertNotNull(result); + } + } + + @Test + void getResourceStream_whenResourceDoesNotExistInClassLoader_ThrowsException() { + // Arrange + String resourceName = "nonexistent.txt"; + + // Act & Assert + assertThrows(NullPointerException.class, + () -> ObjectUtils.getResourceStream(resourceName)); + } + +} diff --git a/src/test/java/net/locusworks/common/utils/RandomStringTest.java b/src/test/java/net/locusworks/common/utils/RandomStringTest.java index 7cb8bf1..e27c52d 100644 --- a/src/test/java/net/locusworks/common/utils/RandomStringTest.java +++ b/src/test/java/net/locusworks/common/utils/RandomStringTest.java @@ -7,24 +7,24 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class RandomStringTest { - @Test - public void testStaticBytes() { - for (int length = 3; length < 50; length++) { - assertEquals(RandomString.getInstance().getBytes(length).length, length); + @Test + public void testStaticBytes() { + for (int length = 3; length < 50; length++) { + assertEquals(RandomString.getInstance().getBytes(length).length, length); + } } - } - - @Test - public void testStaticString() { - for (int length = 3; length < 50; length++) { - String random = RandomString.getInstance().getString(length); - assertEquals(random.length(), length); - } - } - @Test - public void testExceptions() { - assertThrows(IllegalArgumentException.class, () -> RandomString.newInstance().getString(0)); - } + @Test + public void testStaticString() { + for (int length = 3; length < 50; length++) { + String random = RandomString.getInstance().getString(length); + assertEquals(random.length(), length); + } + } + + @Test + public void testExceptions() { + assertThrows(IllegalArgumentException.class, () -> RandomString.newInstance().getString(0)); + } } diff --git a/src/test/java/net/locusworks/common/utils/SplitterTest.java b/src/test/java/net/locusworks/common/utils/SplitterTest.java index 26af52e..1ce0999 100644 --- a/src/test/java/net/locusworks/common/utils/SplitterTest.java +++ b/src/test/java/net/locusworks/common/utils/SplitterTest.java @@ -5,77 +5,79 @@ import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; class SplitterTest { - @Test - void testBasicSplitter() { - List split = Splitter.onSpace().split("Hello World"); - assertNotNull(split); - assertEquals(2, split.size()); - assertEquals("Hello", split.get(0)); - assertEquals("World", split.get(1)); + @Test + void testBasicSplitter() { + List split = Splitter.onSpace().split("Hello World"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); - split = Splitter.fixedLengthSplit(5).split("HelloWorld"); - assertNotNull(split); - assertEquals(2, split.size()); - assertEquals("Hello", split.get(0)); - assertEquals("World", split.get(1)); + split = Splitter.fixedLengthSplit(5).split("HelloWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); - split = Splitter.onNewLine().split("Hello\nWorld"); - assertNotNull(split); - assertEquals(2, split.size()); - assertEquals("Hello", split.get(0)); - assertEquals("World", split.get(1)); + split = Splitter.onNewLine().split("Hello\nWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); - split = Splitter.onNewLine().split("Hello\r\nWorld"); - assertNotNull(split); - assertEquals(2, split.size()); - assertEquals("Hello", split.get(0)); - assertEquals("World", split.get(1)); + split = Splitter.onNewLine().split("Hello\r\nWorld"); + assertNotNull(split); + assertEquals(2, split.size()); + assertEquals("Hello", split.get(0)); + assertEquals("World", split.get(1)); - String[] array = Splitter.onSpace().splitToArray("Hello World"); - assertNotNull(array); - assertEquals(2, array.length); - assertEquals("Hello", array[0]); - assertEquals("World", array[1]); - } + String[] array = Splitter.onSpace().splitToArray("Hello World"); + assertNotNull(array); + assertEquals(2, array.length); + assertEquals("Hello", array[0]); + assertEquals("World", array[1]); + } - @Test - void testOmitEmptyStringWithLimits() { - List split = Splitter.onSpace().split("Hello World"); - assertEquals(3, split.size()); - split = Splitter.onSpace().omitEmptyStrings().withLimit(1).split("Hello World"); - assertNotNull(split); - assertEquals(1, split.size()); - } + @Test + void testOmitEmptyStringWithLimits() { + List split = Splitter.onSpace().split("Hello World"); + assertEquals(3, split.size()); + split = Splitter.onSpace().omitEmptyStrings().withLimit(1).split("Hello World"); + assertNotNull(split); + assertEquals(1, split.size()); + } - @Test - void testExceptions() { - assertThrows(IllegalArgumentException.class, () -> Splitter.onSpace().split("")); - assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLengthSplit(0).split("Hello World")); - assertThrows(NullPointerException.class, () -> Splitter.on(null).split("Hello World")); + @Test + void testExceptions() { + assertThrows(IllegalArgumentException.class, () -> Splitter.onSpace().split("")); + assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLengthSplit(0).split("Hello World")); + assertThrows(NullPointerException.class, () -> Splitter.on(null).split("Hello World")); - } + } - @Test - void testWithKeyValueSeparator() { - Map map = Splitter.on(";").withKeyValueSeparator("=").split("Hello=World;bubba=hotep"); - assertEquals(2, map.size()); - assertEquals("World", map.get("Hello")); - assertEquals("hotep", map.get("bubba")); - } + @Test + void testWithKeyValueSeparator() { + Map map = Splitter.on(";").withKeyValueSeparator("=").split("Hello=World;bubba=hotep"); + assertEquals(2, map.size()); + assertEquals("World", map.get("Hello")); + assertEquals("hotep", map.get("bubba")); + } - @Test - void testWithMapSplitterErrors() { - assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("").split("Hello=;bubba=hotep")); - assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split("Hello=;bubba=hotep")); - assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split(null)); - Map map = Splitter.on(";").withKeyValueSeparator("=").skipInvalidKeyValues().split("Hello=;bubba=hotep"); - assertEquals(1, map.size()); - assertEquals("hotep", map.get("bubba")); - } + @Test + void testWithMapSplitterErrors() { + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("").split("Hello=;bubba=hotep")); + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split("Hello=;bubba=hotep")); + assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split(null)); + Map map = Splitter.on(";").withKeyValueSeparator("=").skipInvalidKeyValues().split("Hello=;bubba=hotep"); + assertEquals(1, map.size()); + assertEquals("hotep", map.get("bubba")); + } } diff --git a/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java b/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java index dd41898..8f2a9c1 100644 --- a/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java +++ b/src/test/java/net/locusworks/common/utils/StreamUtilsTest.java @@ -4,16 +4,16 @@ import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class StreamUtilsTest { - @Test - void testAsStream() { - List list = List.of("Hello", "World"); - assertEquals(2, StreamUtils.asStream(list).count()); - assertEquals(2, StreamUtils.asStream(list.iterator()).count()); - assertEquals(2, StreamUtils.asStream(list.toArray(), false).count()); - } + @Test + void testAsStream() { + List list = List.of("Hello", "World"); + assertEquals(2, StreamUtils.asStream(list).count()); + assertEquals(2, StreamUtils.asStream(list.iterator()).count()); + assertEquals(2, StreamUtils.asStream(list.toArray(), false).count()); + } } diff --git a/src/test/java/net/locusworks/common/utils/UtilityWrappersTest.java b/src/test/java/net/locusworks/common/utils/UtilityWrappersTest.java new file mode 100644 index 0000000..d94b977 --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/UtilityWrappersTest.java @@ -0,0 +1,65 @@ +package net.locusworks.common.utils; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class UtilityWrappersTest { + @Test + void successFactoriesAndConstructorExposeBodyAndStatus() { + assertTrue(Success.success().getSuccess()); + assertEquals(true, Success.success().getBody()); + assertFalse(Success.fail().getSuccess()); + assertEquals(false, Success.fail().getBody()); + Success value = new Success(true, "body"); + assertEquals("body", value.getBody()); + } + + @Test + void dataOutputStreamConvertsAndClosesByteArrayOutput() throws Exception { + DataOutputStreamHelper stream = new DataOutputStreamHelper(); + stream.write("hello".getBytes(StandardCharsets.UTF_8)); + assertArrayEquals("hello".getBytes(StandardCharsets.UTF_8), stream.toByteArray()); + assertEquals("hello", stream.toString()); + assertEquals("aGVsbG8=", stream.base64Encoded()); + stream.close(); + assertArrayEquals(new byte[0], stream.toByteArray()); + assertDoesNotThrow(stream::close); + } + + @Test + void dataOutputStreamSupportsOtherOutputsAndSwallowsCloseFailure() throws Exception { + OutputStream custom = new OutputStream() { + @Override + public void write(int b) { + } + + @Override + public String toString() { + return "custom"; + } + + @Override + public void close() { + throw new IllegalStateException("expected"); + } + }; + DataOutputStreamHelper stream = new DataOutputStreamHelper(custom); + assertEquals("custom", stream.toString()); + assertDoesNotThrow(stream::close); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (DataOutputStreamHelper explicit = new DataOutputStreamHelper(bytes)) { + explicit.writeByte(1); + assertArrayEquals(new byte[]{1}, explicit.toByteArray()); + } + } +} diff --git a/src/test/java/net/locusworks/common/utils/UtilsPackageCoverageTest.java b/src/test/java/net/locusworks/common/utils/UtilsPackageCoverageTest.java new file mode 100644 index 0000000..70f8b5b --- /dev/null +++ b/src/test/java/net/locusworks/common/utils/UtilsPackageCoverageTest.java @@ -0,0 +1,161 @@ +package net.locusworks.common.utils; + +import com.fasterxml.jackson.core.JsonParser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.when; +import org.mockito.MockedStatic; + +class UtilsPackageCoverageTest { + @TempDir + Path tempDir; + + private static java.util.Date deserialize(String value) throws Exception { + JsonParser parser = mock(JsonParser.class); + when(parser.getText()).thenReturn(value); + return new DateTimeStampDeserializer().deserialize(parser, null); + } + + @Test + void dateDeserializerHandlesEpochBuiltInCustomAndInvalidFormats() throws Exception { + assertEquals(1234L, deserialize("1234").getTime()); + + Locale original = Locale.getDefault(); + Locale.setDefault(Locale.US); + try { + assertNotNull(deserialize("January 1, 2020")); + assertNotNull(deserialize("12/31/2020")); + assertNotNull(deserialize("Dec 31, 2020 1:02:03 PM")); + assertNull(deserialize("definitely-not-a-date")); + + Field stylesField = DateTimeStampDeserializer.class.getDeclaredField("styles"); + stylesField.setAccessible(true); + Integer[] styles = (Integer[]) stylesField.get(null); + Integer[] originalStyles = styles.clone(); + java.util.Arrays.fill(styles, -1); + try { + assertNotNull(deserialize("12/31/2020")); + } finally { + System.arraycopy(originalStyles, 0, styles, 0, styles.length); + } + } finally { + Locale.setDefault(original); + } + } + + @SuppressWarnings("deprecation") + @Test + void hashUtilsCoversEveryInputOverloadCaseAndFailure() throws Exception { + String expected = HashUtils.hash("SHA-256", "content"); + byte[] bytes = "content".getBytes(StandardCharsets.UTF_8); + Path path = tempDir.resolve("hash.txt"); + Files.write(path, bytes); + File file = path.toFile(); + + assertEquals(expected, HashUtils.hash("SHA-256", bytes)); + assertEquals(expected, HashUtils.hash("SHA-256", path)); + assertEquals(expected, HashUtils.hash("SHA-256", file)); + assertEquals(expected.toUpperCase(Locale.ROOT), HashUtils.hash("SHA-256", file, false)); + assertEquals(expected.toUpperCase(Locale.ROOT), HashUtils.hash("SHA-256", path, false)); + assertEquals(expected, HashUtils.hash(new ByteArrayInputStream(bytes), "SHA-256")); + assertEquals(expected.toUpperCase(Locale.ROOT), + HashUtils.hash(new ByteArrayInputStream(bytes), "SHA-256", false)); + assertThrows(IllegalArgumentException.class, + () -> HashUtils.hash("SHA-256", tempDir.resolve("missing"))); + assertThrows(IllegalArgumentException.class, + () -> HashUtils.hash(new ByteArrayInputStream(bytes), "not-an-algorithm")); + assertThrows(IllegalArgumentException.class, + () -> HashUtils.hash(new java.io.InputStream() { + @Override public int read() throws IOException { throw new IOException("read failed"); } + }, "SHA-256")); + } + + @Test + void fileReaderCoversResourceValidationIterationMutationAndFailures() throws Exception { + try (FileReader resource = new FileReader("test.properties")) { + assertTrue(resource.hasNext()); + assertSame(resource, resource.iterator()); + assertNotNull(resource.next()); + } + + assertThrows(IllegalArgumentException.class, () -> new FileReader((String) null)); + assertThrows(IllegalArgumentException.class, () -> new FileReader((Path) null)); + assertThrows(IllegalArgumentException.class, () -> new FileReader(tempDir.resolve("missing"))); + assertThrows(IllegalArgumentException.class, () -> new FileReader(tempDir)); + assertThrows(IllegalArgumentException.class, () -> new FileReader((BufferedReader) null)); + + BufferedReader readFailure = new BufferedReader(new StringReader("")) { + @Override public String readLine() throws IOException { throw new IOException("read failed"); } + }; + assertThrows(RuntimeException.class, () -> new FileReader(readFailure).hasNext()); + + BufferedReader closeFailure = new BufferedReader(new StringReader("")) { + @Override public void close() throws IOException { throw new IOException("close failed"); } + }; + assertThrows(RuntimeException.class, () -> new FileReader(closeFailure).close()); + + FileReader nullReader = new FileReader(new BufferedReader(new StringReader(""))); + Field readerField = FileReader.class.getDeclaredField("reader"); + readerField.setAccessible(true); + readerField.set(nullReader, null); + assertDoesNotThrow(nullReader::close); + + Path simulated = tempDir.resolve("simulated.txt"); + try (MockedStatic files = mockStatic(Files.class, CALLS_REAL_METHODS)) { + files.when(() -> Files.notExists(simulated)).thenReturn(false); + files.when(() -> Files.isRegularFile(simulated)).thenReturn(true); + files.when(() -> Files.newBufferedReader(simulated)).thenThrow(new IOException("open failed")); + assertThrows(RuntimeException.class, () -> new FileReader(simulated)); + } + + FileReader.LineInfo info = new FileReader.LineInfo(1, "line"); + info.setLineNumber(2); + info.setLineLength(10); + info.setLine("changed"); + assertEquals(2, info.getLineNumber()); + assertEquals(10, info.getLineLength()); + assertEquals("changed", info.getLine()); + } + + @Test + void singletonAndUtilityClassConstructorsAreCovered() throws Exception { + Field instance = RandomString.class.getDeclaredField("instance"); + instance.setAccessible(true); + instance.set(null, null); + assertNotNull(RandomString.getInstance()); + + assertNotNull(new HashUtils()); + assertNotNull(new Checks()); + assertNotNull(new Utils()); + assertNotNull(new Constants()); + assertNotNull(new ObjectUtils()); + assertNotNull(new StreamUtils()); + assertNotNull(ObjectUtils.getResourceStream("/test.properties")); + + assertEquals(List.of("a", "b"), StreamUtils.asStream(List.of("a", "b"), true).toList()); + assertTrue(StreamUtils.asStream(new String[]{"a"}, true).isParallel()); + } +} diff --git a/src/test/java/net/locusworks/common/utils/UtilsTest.java b/src/test/java/net/locusworks/common/utils/UtilsTest.java index ba9996b..1360bae 100644 --- a/src/test/java/net/locusworks/common/utils/UtilsTest.java +++ b/src/test/java/net/locusworks/common/utils/UtilsTest.java @@ -1,14 +1,22 @@ package net.locusworks.common.utils; import net.locusworks.common.annotations.MapValue; -import net.locusworks.common.utils.Utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.ParameterizedTest; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -202,7 +210,7 @@ public class UtilsTest { } List extractedList = Utils.extractFieldToList("field5", list); - Set extractedSet = Utils.extractFieldToSet("field5", list); + java.util.Set extractedSet = Utils.extractFieldToSet("field5", list); assertEquals(10, extractedList.size()); assertEquals(10, extractedSet.size()); @@ -274,7 +282,7 @@ public class UtilsTest { @Test public void testBuildSet() { - Set myset = Utils.buildSet(HashSet.class, String.class, "Hello", "World"); + java.util.Set myset = Utils.buildSet(HashSet.class, String.class, "Hello", "World"); assertNotNull(myset); assertEquals(2, myset.size()); } diff --git a/src/test/java/net/locusworks/test/AESEncryptionTest.java b/src/test/java/net/locusworks/test/AESEncryptionTest.java index 7854265..0271f48 100644 --- a/src/test/java/net/locusworks/test/AESEncryptionTest.java +++ b/src/test/java/net/locusworks/test/AESEncryptionTest.java @@ -1,41 +1,44 @@ package net.locusworks.test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; + import net.locusworks.common.crypto.AES; import net.locusworks.common.utils.Utils; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; public class AESEncryptionTest { - @Test - public void testEncryption() { - try { - String encrypted = AES.createInstance().encrypt("hello world"); - assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); - } catch (Exception ex) { - ex.printStackTrace(System.err); - fail(); + @Test + public void testEncryption() { + try { + String encrypted = AES.createInstance().encrypt("hello world"); + assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); + } catch (Exception ex) { + ex.printStackTrace(System.err); + fail(); + } } - } - @Test - public void testDecryption() { - String testString ="hello world"; - try { - AES aes = AES.createInstance(); - String encrypted = aes.encrypt(testString); - assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); - - String decrypted = aes.decrypt(encrypted); - assertFalse(Utils.isEmptyString(encrypted), String.format("Decrypted String is not blank? :%s", decrypted)); + @Test + public void testDecryption() { + String testString = "hello world"; + try { + AES aes = AES.createInstance(); + String encrypted = aes.encrypt(testString); + assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted)); - assertEquals(testString, decrypted, "Test String and Original String the same? :%s"); - - } catch (Exception ex) { - ex.printStackTrace(System.err); - fail(); + String decrypted = aes.decrypt(encrypted); + assertFalse(Utils.isEmptyString(encrypted), String.format("Decrypted String is not blank? :%s", decrypted)); + + assertEquals(testString, decrypted, "Test String and Original String the same? :%s"); + + } catch (Exception ex) { + ex.printStackTrace(System.err); + fail(); + } } - } } diff --git a/src/test/java/net/locusworks/test/AllTests.java b/src/test/java/net/locusworks/test/AllTests.java deleted file mode 100644 index 0407b5a..0000000 --- a/src/test/java/net/locusworks/test/AllTests.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.locusworks.test; - -import org.junit.jupiter.api.extension.ExtendWith; - -public class AllTests { - -} diff --git a/src/test/java/net/locusworks/test/HashSaltTest.java b/src/test/java/net/locusworks/test/HashSaltTest.java index b42681f..ea6e8fd 100644 --- a/src/test/java/net/locusworks/test/HashSaltTest.java +++ b/src/test/java/net/locusworks/test/HashSaltTest.java @@ -1,34 +1,36 @@ package net.locusworks.test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import net.locusworks.common.crypto.HashSalt; import net.locusworks.common.utils.Utils; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - public class HashSaltTest { - - private static final String samplePassword="Hello World"; - @Test - public void testEncryption() { - try { - String hashSalt = HashSalt.createHash(samplePassword); - assertFalse(Utils.isEmptyString(hashSalt), String.format("Encrypted String is not blank? :%s", hashSalt)); - } catch(Exception ex) { - fail(); + private static final String samplePassword = "Hello World"; + + @Test + public void testEncryption() { + try { + String hashSalt = HashSalt.createHash(samplePassword); + assertFalse(Utils.isEmptyString(hashSalt), String.format("Encrypted String is not blank? :%s", hashSalt)); + } catch (Exception ex) { + fail(); + } } - } - - @Test - public void testDecryption() { - try { - String hashSalt = HashSalt.createHash(samplePassword); - boolean decrypted = HashSalt.validatePassword(samplePassword, hashSalt); - assertTrue(decrypted, "Test String and Original String the same? :%s"); - } catch(Exception ex) { - fail(); + + @Test + public void testDecryption() { + try { + String hashSalt = HashSalt.createHash(samplePassword); + boolean decrypted = HashSalt.validatePassword(samplePassword, hashSalt); + assertTrue(decrypted, "Test String and Original String the same? :%s"); + } catch (Exception ex) { + fail(); + } } - } } diff --git a/src/test/java/net/locusworks/test/ImmutablesTest.java b/src/test/java/net/locusworks/test/ImmutablesTest.java index 4269faa..782665d 100644 --- a/src/test/java/net/locusworks/test/ImmutablesTest.java +++ b/src/test/java/net/locusworks/test/ImmutablesTest.java @@ -1,45 +1,63 @@ package net.locusworks.test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + import net.locusworks.common.immutables.Pair; import net.locusworks.common.immutables.Triplet; import net.locusworks.common.immutables.Unit; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - public class ImmutablesTest { - @Test - public void testUnit() { - Unit unit = new Unit<>("Hello World"); - assertEquals("Hello World", unit.getValue1()); - Unit unit2 = new Unit<>(2); - assertEquals(2, (int) unit2.getValue1()); - } - - @Test - public void testPair() { - Pair pair1 = new Pair<>("Hello", "World"); - assertEquals("Hello", pair1.getValue1()); - assertEquals("World", pair1.getValue2()); - - Pair pair2 = new Pair<>("Foo", 25); - assertEquals("Foo", pair2.getValue1()); - assertEquals(25, (int) pair2.getValue2()); - - Pair pair3 = new Pair<>(1, 23); - assertEquals(1, (int) pair3.getValue1()); - assertEquals(23, (int) pair3.getValue2()); - } - - @Test - public void testTriplet() { - Triplet triplet1 = new Triplet<>("Hello", 24, "World"); - assertEquals("Hello", triplet1.getValue1()); - assertEquals(24, (int) triplet1.getValue2()); - assertEquals("World", triplet1.getValue3()); - } + @Test + public void testUnit() { + Unit unit1 = new Unit<>("Hello World"); + Unit unit3 = new Unit<>("Hello World"); + assertEquals("Hello World", unit1.getValue1()); + + Unit unit2 = new Unit<>(2); + assertEquals(2, (int) unit2.getValue1()); + + assertEquals(unit1, unit3); + assertNotEquals(unit1, unit2); + assertNotEquals(unit1, new Object()); + assertNotEquals(unit1, null); + + } + + @Test + public void testPair() { + Pair pair1 = new Pair<>("Hello", "World"); + Pair pair2 = new Pair<>("Hello", "World"); + Pair pair3 = new Pair<>("Hello", "World2"); + assertEquals("Hello", pair1.getValue1()); + assertEquals("World", pair1.getValue2()); + assertEquals(pair1, pair2); + + assertNotEquals(pair1, pair3); + assertNotEquals(pair1, new Object()); + assertNotEquals(pair1, null); + + } + + @Test + public void testTriplet() { + Triplet triplet1 = new Triplet<>("Hello", 24, "World"); + Triplet triplet2 = new Triplet<>("Hello", 25, "World"); + Triplet triplet3 = new Triplet<>("Hello", 24, "World2"); + Triplet triplet4 = new Triplet<>("Hello", 24, "World"); + + assertEquals("Hello", triplet1.getValue1()); + assertEquals(24, (int) triplet1.getValue2()); + assertEquals("World", triplet1.getValue3()); + assertEquals(triplet1, triplet4); + + assertNotEquals(triplet1, triplet2); + assertNotEquals(triplet1, triplet3); + assertNotEquals(triplet1, new Object()); + assertNotEquals(triplet1, null); + } } diff --git a/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java b/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java index 5c0af39..93a6146 100644 --- a/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java +++ b/src/test/java/net/locusworks/test/ObjectMapperHelperTest.java @@ -1,5 +1,5 @@ /** - * + * */ package net.locusworks.test; @@ -14,7 +14,9 @@ import org.junit.jupiter.api.Test; import static net.locusworks.common.utils.Constants.JUNIT_TEST_CHECK; import static net.locusworks.common.utils.Constants.LOG4J_CONFIG_PROPERTY; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test cases to test ObjectMapperHelper.class @@ -23,49 +25,49 @@ import static org.junit.jupiter.api.Assertions.*; * */ public class ObjectMapperHelperTest { - - private static Triplet test; - - /** - * @throws java.lang.Exception exception - */ - @BeforeAll - public static void setUpBeforeClass() throws Exception { - System.setProperty(LOG4J_CONFIG_PROPERTY, "log4j2-test.xml"); - System.setProperty(JUNIT_TEST_CHECK, "true"); - test = new Triplet("Hello", 24, "World"); - } - - @AfterAll - public static void tearDownAfterClass() throws Exception { - System.clearProperty(LOG4J_CONFIG_PROPERTY); - System.clearProperty(JUNIT_TEST_CHECK); - } - @Test - public void testWrite() { - String value = ObjectMapperHelper.writeValue(test).getResults(); - assertTrue(value != null && !value.trim().isEmpty()); - } - - @Test - public void testRead() { - String value = ObjectMapperHelper.writeValue(test).getResults(); - assertTrue(value != null && !value.trim().isEmpty()); - - Triplet tmp = ObjectMapperHelper.readValue(value, Triplet.class).getResults(); - assertNotNull(tmp); - assertEquals(tmp, test); - } - - @SuppressWarnings("rawtypes") - @Test - public void testListWriteRead() { - List> htrList = Utils.toList(test); - String value = ObjectMapperHelper.writeValue(htrList).getResults(); - assertTrue(value != null && !value.trim().isEmpty()); - List tmpList = ObjectMapperHelper.readListValue(value, Triplet.class).getResults(); - assertTrue(tmpList != null && !tmpList.isEmpty()); - } + private static Triplet test; + + /** + * @throws java.lang.Exception exception + */ + @BeforeAll + public static void setUpBeforeClass() throws Exception { + System.setProperty(LOG4J_CONFIG_PROPERTY, "log4j2-test.xml"); + System.setProperty(JUNIT_TEST_CHECK, "true"); + test = new Triplet("Hello", 24, "World"); + } + + @AfterAll + public static void tearDownAfterClass() throws Exception { + System.clearProperty(LOG4J_CONFIG_PROPERTY); + System.clearProperty(JUNIT_TEST_CHECK); + } + + @Test + public void testWrite() { + String value = ObjectMapperHelper.writeValue(test).getResults(); + assertTrue(value != null && !value.trim().isEmpty()); + } + + @Test + public void testRead() { + String value = ObjectMapperHelper.writeValue(test).getResults(); + assertTrue(value != null && !value.trim().isEmpty()); + + Triplet tmp = ObjectMapperHelper.readValue(value, Triplet.class).getResults(); + assertNotNull(tmp); + assertEquals(tmp, test); + } + + @SuppressWarnings("rawtypes") + @Test + public void testListWriteRead() { + List> htrList = Utils.toList(test); + String value = ObjectMapperHelper.writeValue(htrList).getResults(); + assertTrue(value != null && !value.trim().isEmpty()); + List tmpList = ObjectMapperHelper.readListValue(value, Triplet.class).getResults(); + assertTrue(tmpList != null && !tmpList.isEmpty()); + } } diff --git a/src/test/java/net/locusworks/test/PropertiesManagerTest.java b/src/test/java/net/locusworks/test/PropertiesManagerTest.java index 698e8ee..8f85101 100644 --- a/src/test/java/net/locusworks/test/PropertiesManagerTest.java +++ b/src/test/java/net/locusworks/test/PropertiesManagerTest.java @@ -1,5 +1,10 @@ package net.locusworks.test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.File; import java.io.IOException; import java.nio.file.Path; @@ -10,115 +15,115 @@ import net.locusworks.common.configuration.PropertiesManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - /** * Test cases for the properties manager class + * * @author Isaac Parenteau * @since 1.0.0-RELEASE * */ public class PropertiesManagerTest { - private static final String PROPERTIES_FILE = "test.properties"; - private static final String TMP_PROPS = "temp.properties"; - private static final int ENTRY_SIZE = 4; - - public static enum Configuration { - DB_HOST("dbHost"), - DB_PORT("dbPort"), - USER_EXPIRATION_DAYS("userExpirationDays"), - LOG_LEVEL("logLevel"); + private static final String PROPERTIES_FILE = "test.properties"; + private static final String TMP_PROPS = "temp.properties"; + private static final int ENTRY_SIZE = 4; - private final String value; + public enum Configuration { + DB_HOST("dbHost"), + DB_PORT("dbPort"), + USER_EXPIRATION_DAYS("userExpirationDays"), + LOG_LEVEL("logLevel"); - private Configuration(String value) { - this.value = value; + private final String value; + + private Configuration(String value) { + this.value = value; + } + + /** + * Get the current value of the enumeration + * + * @return value + */ + public String getValue() { + return this.value; + } + + @Override + public String toString() { + return this.value; + } } - /** - * Get the current value of the enumeration - * @return value - */ - public String getValue() { - return this.value; + @AfterAll + public static void removeSavedProps() { + File tmp = new File(TMP_PROPS); + if (tmp.exists()) { + tmp.delete(); + } } - @Override - public String toString() { - return this.value; + @Test + public void testPropertiesLoad() { + try { + Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); + assertNotNull(props); + assertTrue(props.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); + assertTrue(props.containsKey(Configuration.DB_HOST.toString())); + assertTrue(props.containsKey(Configuration.DB_PORT.toString())); + assertTrue(props.containsKey(Configuration.LOG_LEVEL.toString())); + } catch (IOException e) { + fail(e.getMessage()); + } } - } - @AfterAll - public static void removeSavedProps() { - File tmp = new File(TMP_PROPS); - if (tmp.exists()) { - tmp.delete(); + @Test + public void testAddConfiguration() { + try { + Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); + Properties tmp = new Properties(); + assertEquals(0, tmp.size()); + PropertiesManager.addConfiguration(tmp, props); + assertEquals(ENTRY_SIZE, tmp.size()); + assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); + assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); + assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); + assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString())); + } catch (IOException e) { + fail(e.getMessage()); + } } - } - @Test - public void testPropertiesLoad() { - try { - Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); - assertNotNull(props); - assertTrue(props.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); - assertTrue(props.containsKey(Configuration.DB_HOST.toString())); - assertTrue(props.containsKey(Configuration.DB_PORT.toString())); - assertTrue(props.containsKey(Configuration.LOG_LEVEL.toString())); - } catch (IOException e) { - fail(e.getMessage()); + @Test + public void testRemoveConfiguration() { + try { + Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); + Properties tmp = new Properties(); + assert props != null; + assertEquals(ENTRY_SIZE, props.size()); + assertEquals(0, tmp.size()); + PropertiesManager.removeConfiguration(props, tmp); + assertEquals(0, props.size()); + assertEquals(0, tmp.size()); + } catch (IOException e) { + fail(e.getMessage()); + } } - } - @Test - public void testAddConfiguration() { - try { - Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); - Properties tmp = new Properties(); - assertEquals(0, tmp.keySet().size()); - PropertiesManager.addConfiguration(tmp, props); - assertEquals(ENTRY_SIZE, tmp.keySet().size()); - assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); - assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); - assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); - assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString())); - } catch (IOException e) { - fail(e.getMessage()); + @Test + public void testSaveConfiguration() { + try { + Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); + Path tmpFile = Paths.get(TMP_PROPS); + PropertiesManager.saveConfiguration(props, tmpFile, "test propertis"); + Properties tmp = PropertiesManager.loadConfiguration(tmpFile); + assertEquals(ENTRY_SIZE, tmp.size()); + assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); + assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); + assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); + assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString())); + } catch (IOException e) { + fail(e.getMessage()); + } } - } - - @Test - public void testRemoveConfiguration() { - try { - Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); - Properties tmp = new Properties(); - assert props != null; - assertEquals(ENTRY_SIZE, props.keySet().size()); - assertEquals(0, tmp.keySet().size()); - PropertiesManager.removeConfiguration(props, tmp); - assertEquals(0, props.keySet().size()); - assertEquals(0, tmp.keySet().size()); - } catch (IOException e) { - fail(e.getMessage()); - } - } - - @Test - public void testSaveConfiguration() { - try { - Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); - Path tmpFile = Paths.get(TMP_PROPS); - PropertiesManager.saveConfiguration(props, tmpFile, "test propertis"); - Properties tmp = PropertiesManager.loadConfiguration(tmpFile); - assertEquals(ENTRY_SIZE, tmp.keySet().size()); - assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); - assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); - assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); - assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString())); - } catch (IOException e) { - fail(e.getMessage()); - } - } }