Skip to content

Commit

Permalink
primitive type parser with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JuicyDragon committed Apr 24, 2024
1 parent 759f63d commit 89d5ed3
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 0 deletions.
1 change: 1 addition & 0 deletions Java/SuperUtilities/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.nuix.superutilities.misc;

import com.google.common.collect.ImmutableList;
import lombok.Getter;
import org.joda.time.DateTime;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;

@Getter
public class PrimitiveTypeParser {
public static final Function<String, Object> jodaTimeAutomaticParsing = new Function<>() {
private final List<Pattern> formatValidators = List.of(
// yyyy-MM-dd'T'HH:mm:ss'Z'
// 2017-12-11T15:51:24Z
// 2022-04-20T17:12:37Z
Pattern.compile("^\\d{4}-[01][0-9]-[0-3][0-9][tT][0-2][0-9]:[0-5][0-9]:\\d{2}Z$"),

// yyyy-MM-dd'T'HH:mm:ss.SSSZZ
// 2017-12-11T15:51:24.000-07:00
Pattern.compile("^\\d{4}-[01][0-9]-[0-3][0-9][tT][0-2][0-9]:[0-5][0-9]:\\d{2}\\.\\d{3}[+\\-]\\d{2}:\\d{2}$")
);

@Override
public Object apply(String s) {
if (formatValidators.stream()
.noneMatch(p -> p.matcher(s.trim()).matches())) {
return null;
} else {
return DateTime.parse(s.trim());
}
}
};

public static final Function<String, Object> numericParser = new Function<>() {
private final Pattern formatValidator = Pattern.compile("^[+\\-]?[0-9]+$");

@Override
public Object apply(String s) {
if (!formatValidator.matcher(s.trim()).matches()) {
return null;
} else {
return Long.parseLong(s.trim());
}
}
};

public static final Function<String, Object> decimalParser = new Function<>() {
private final Pattern formatValidator = Pattern.compile("^[+\\-]?[0-9]+\\.[0-9]*$");

@Override
public Object apply(String s) {
if (!formatValidator.matcher(s.trim()).matches()) {
return null;
} else {
return Double.parseDouble(s.trim());
}
}
};

public static final Function<String, Object> booleanParser = new Function<>() {
private final Pattern formatValidator = Pattern.compile("^(true)|(false)$");

@Override
public Object apply(String s) {
if (!formatValidator.matcher(s.trim().toLowerCase()).matches()) {
return null;
} else {
return Boolean.parseBoolean(s.trim());
}
}
};

@Getter(lazy = true)
private static final PrimitiveTypeParser standard = buildImmutableStandard();

private static PrimitiveTypeParser buildImmutableStandard() {
PrimitiveTypeParser result = buildStandardCopy();
result.typeParsers = ImmutableList.copyOf(result.typeParsers);
return result;
}

public static PrimitiveTypeParser buildStandardCopy() {
PrimitiveTypeParser result = new PrimitiveTypeParser();
result.getTypeParsers().add(jodaTimeAutomaticParsing);
result.getTypeParsers().add(numericParser);
result.getTypeParsers().add(decimalParser);
result.getTypeParsers().add(booleanParser);
return result;
}

private List<Function<String, Object>> typeParsers;

public PrimitiveTypeParser() {
typeParsers = new ArrayList<>();
}

public Object parseWithFallback(String input, Object fallback) {
if (input == null || input.trim().isBlank()) {
return fallback;
} else {
Object parsedValue = null;
for (Function<String, Object> parser : typeParsers) {
parsedValue = parser.apply(input);
if (parsedValue != null) {
break;
}
}

if (parsedValue == null) {
return fallback;
} else {
return parsedValue;
}
}
}

public Object parse(String input) {
return parseWithFallback(input, input);
}

public void enrichInPlace(Map<String,Object> input) {
for(Map.Entry<String,Object> entry : input.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if(value instanceof String) {
String stringValue = (String) value;
Object parsedValue = parse(stringValue);
input.put(key,parsedValue);
}
}
}
}
112 changes: 112 additions & 0 deletions Java/SuperUtilities/src/test/java/PrimitiveTypeParserTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import com.nuix.superutilities.misc.PrimitiveTypeParser;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

public class PrimitiveTypeParserTests extends TestFoundation {
@Test
public void testTypeParsing() throws Exception {
PrimitiveTypeParser standardTypeParser = PrimitiveTypeParser.getStandard();

// Since we are testing some without millis precision, we trim millis off so
// round trip still succeeds
DateTime now = DateTime.now(DateTimeZone.UTC).millisOfSecond().setCopy(0);
List<String> formatStrings = List.of(
"yyyy-MM-dd'T'HH:mm:ss.SSSZZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'"
);

for (String formatString : formatStrings) {
log.info(formatString + " => " + now.toString(formatString));
}

for (String formatString : formatStrings) {
String inputString = now.toString(formatString);
Object parsedObject = standardTypeParser.parse(inputString);
assertNotNull(parsedObject);
assertInstanceOf(DateTime.class, parsedObject);
assertTrue(now.isEqual((DateTime) parsedObject),
String.format("Format String: %s, Expected: %s, Got: %s",
formatString, now, ((DateTime) parsedObject)));
}

List<String> additionalDates = List.of(
"2022-04-20T17:12:37Z"
);

for (String dateToParse : additionalDates) {
Object parsedDate = standardTypeParser.parse("2022-04-20T17:12:37Z");
assertTrue(parsedDate instanceof DateTime, String.format("%s did not parse into a DateTime", dateToParse));
}

assertEquals("cat", standardTypeParser.parse("cat"));
assertEquals(" cat ", standardTypeParser.parse(" cat "));
assertEquals("dog", standardTypeParser.parseWithFallback("", "dog"));
assertEquals(0, standardTypeParser.parseWithFallback("bird", 0));

assertEquals(true, standardTypeParser.parse("true"));
assertEquals(false, standardTypeParser.parse("false"));
assertEquals(true, standardTypeParser.parse("True"));
assertEquals(false, standardTypeParser.parse("False"));
assertEquals(true, standardTypeParser.parse(" True "));
assertEquals(false, standardTypeParser.parse(" False "));

assertEquals(0L, standardTypeParser.parse("0"));
assertEquals(0L, standardTypeParser.parse("-0"));
assertEquals(0L, standardTypeParser.parse("+0"));
assertEquals(123456789L, standardTypeParser.parse("123456789"));
assertEquals(123456789L, standardTypeParser.parse("0123456789"));
assertEquals(123456789L, standardTypeParser.parse("+0123456789"));
assertEquals(-123456789L, standardTypeParser.parse("-0123456789"));

assertEquals(0L, standardTypeParser.parse(" 0 "));
assertEquals(0L, standardTypeParser.parse(" -0 "));
assertEquals(0L, standardTypeParser.parse(" +0 "));
assertEquals(123456789L, standardTypeParser.parse(" 123456789 "));
assertEquals(123456789L, standardTypeParser.parse(" 0123456789 "));
assertEquals(123456789L, standardTypeParser.parse(" +0123456789 "));
assertEquals(-123456789L, standardTypeParser.parse(" -0123456789 "));

assertEquals(0.0d, standardTypeParser.parse("0."));
assertEquals(-0.0d, standardTypeParser.parse("-0."));
assertEquals(0.0d, standardTypeParser.parse("+0."));
assertEquals(0.0d, standardTypeParser.parse("0.0"));
assertEquals(0.0d, standardTypeParser.parse("+0.0"));
assertEquals(-0.0d, standardTypeParser.parse("-0.0"));
assertEquals(123456789d, standardTypeParser.parse("123456789."));
assertEquals(123456789d, standardTypeParser.parse("+123456789."));
assertEquals(-123456789d, standardTypeParser.parse("-123456789."));
assertEquals(123456789d, standardTypeParser.parse("123456789.0"));
assertEquals(123456789d, standardTypeParser.parse("+123456789.0"));
assertEquals(-123456789d, standardTypeParser.parse("-123456789.0"));
assertEquals(123456789d, standardTypeParser.parse("123456789.000"));
assertEquals(123456789d, standardTypeParser.parse("+123456789.000"));
assertEquals(-123456789d, standardTypeParser.parse("-123456789.000"));
assertEquals(123456789.314d, standardTypeParser.parse("123456789.314"));
assertEquals(123456789.314d, standardTypeParser.parse("+123456789.314"));
assertEquals(-123456789.314d, standardTypeParser.parse("-123456789.314"));

assertEquals(0.0d, standardTypeParser.parse(" 0. "));
assertEquals(-0.0d, standardTypeParser.parse(" -0. "));
assertEquals(0.0d, standardTypeParser.parse(" +0. "));
assertEquals(0.0d, standardTypeParser.parse(" 0.0 "));
assertEquals(0.0d, standardTypeParser.parse(" +0.0 "));
assertEquals(-0.0d, standardTypeParser.parse(" -0.0 "));
assertEquals(123456789d, standardTypeParser.parse(" 123456789. "));
assertEquals(123456789d, standardTypeParser.parse(" +123456789. "));
assertEquals(-123456789d, standardTypeParser.parse(" -123456789. "));
assertEquals(123456789d, standardTypeParser.parse(" 123456789.0 "));
assertEquals(123456789d, standardTypeParser.parse(" +123456789.0 "));
assertEquals(-123456789d, standardTypeParser.parse(" -123456789.0 "));
assertEquals(123456789d, standardTypeParser.parse(" 123456789.000 "));
assertEquals(123456789d, standardTypeParser.parse(" +123456789.000 "));
assertEquals(-123456789d, standardTypeParser.parse(" -123456789.000 "));
assertEquals(123456789.314d, standardTypeParser.parse(" 123456789.314 "));
assertEquals(123456789.314d, standardTypeParser.parse(" +123456789.314 "));
assertEquals(-123456789.314d, standardTypeParser.parse(" -123456789.314 "));
}
}

0 comments on commit 89d5ed3

Please sign in to comment.