Skip to content

Commit

Permalink
Add support for Optional<...> (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
drapostolos committed Dec 29, 2022
1 parent b0c6b25 commit 54c3f1c
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 6 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ repositories {

dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:1.7.1'
testImplementation 'org.assertj:assertj-core:3.23.1'
}

test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.SortedMap;
Expand Down Expand Up @@ -204,7 +205,20 @@ public Object parse(String input, ParserHelper helper) {
}
return result;
}
};
},
OPTIONAL {

@Override
public Object parse(String input, ParserHelper helper) {
if (!helper.isTargetTypeAssignableTo(Optional.class)) {
return TRY_NEXT;
}
Object value = helper.parseType(input, helper.getParameterizedTypeArguments().get(0));
return Optional.ofNullable(value);
}

},
;

static private Collection<Object> populateCollection(Collection<Object> collection,
Class<?> elementType, String input, ParserHelper helper) {
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/github/drapostolos/typeparser/Helper.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public final Type getTargetType() {
public final List<Class<?>> getParameterizedClassArguments() {
return tt.getParameterizedClassArguments();
}

/**
* When the {@code targetType} is a parameterized type this method
* returns a list with the type arguments.
* <p>
*
* @return List of {@link Type} types.
* @throws UnsupportedOperationException if the {@code targetType} is not a parameterized type.
*/
public List<Type> getParameterizedTypeArguments() {
return tt.getParameterizedTypeArguments();
}


/**
* When the {@code targetType} is an array, this method
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/github/drapostolos/typeparser/TargetType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

final class TargetType {
Expand Down Expand Up @@ -56,7 +57,7 @@ private Class<?> extractRawTargetType() {
return targetType.getClass();
}

final public List<Class<?>> getParameterizedClassArguments() {
List<Class<?>> getParameterizedClassArguments() {
if (parameterizedClassArguments == null) {
parameterizedClassArguments = extractParameterizedClassArguments();
}
Expand Down Expand Up @@ -95,8 +96,17 @@ public String toString() {
return targetType.toString();
}

public boolean isPrimitive() {
boolean isPrimitive() {
return targetType instanceof Class && ((Class<?>) targetType).isPrimitive();
}

List<Type> getParameterizedTypeArguments() {
if (!(isTargetTypeParameterized())) {
String message = String.format("type must be parameterized: %s", Util.objectToString(targetType));
throw new UnsupportedOperationException(message);
}
ParameterizedType pt = (ParameterizedType) targetType;
return Arrays.asList(pt.getActualTypeArguments());
}

}
9 changes: 7 additions & 2 deletions src/test/java/com/github/drapostolos/typeparser/MapTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,20 @@ public void canParseToConcreteMapTypes() throws Exception {
new GenericType<Hashtable<String, String>>() {},
new GenericType<TreeMap<String, String>>() {}
);

}

private void parseToConcreteMapTypes(@SuppressWarnings("unchecked") GenericType<? extends Map<String, String>>... types) {
for (GenericType<? extends Map<String, String>> type : types) {
Class<?> rawType = toRawType(type);
Map<String, String> map = parser.parse("a=A", type);
/*
* Workaround for testing IdentityHashMap with assertJ (version: 3.23.1)
*/
map.forEach((k, v) -> assertThat(k).describedAs("key: %s", k).isEqualTo("a"));
map.forEach((k, v) -> assertThat(v).describedAs("value: %s", v).isEqualTo("A"));

assertThat(map)
.containsOnly(MapEntry.entry("a", "A"))
.describedAs("type: %s", type)
.isInstanceOf(rawType);
assertThat(map.getClass())
.isEqualTo(rawType);
Expand Down
60 changes: 60 additions & 0 deletions src/test/java/com/github/drapostolos/typeparser/OptionalTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.github.drapostolos.typeparser;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import java.util.Optional;

import org.junit.Test;

public class OptionalTest extends TestBase {

@Test
public void canParseSpaceToOptionalString() throws Exception {
GenericType<Optional<String>> type = new GenericType<Optional<String>>() {};
assertThat(parser.parse(" ", type)).contains(" ");
}

@Test
public void canParseStringToEmptyOptional() throws Exception {
GenericType<Optional<String>> type = new GenericType<Optional<String>>() {};
assertThat(parser.parse("null", type)).isEmpty();
}

@Test
public void canParseStringToEmptyOptionalWithWildcard() throws Exception {
GenericType<Optional<?>> type = new GenericType<Optional<?>>() {};
assertThat(parser.parse("null", type)).isEmpty();
}

@Test
public void canParseStringToOptionalListOfIntegers() throws Exception {
Optional<List<Integer>> integers = parser.parse("1,2,3", new GenericType<Optional<List<Integer>>>() {});
assertThat(integers.get()).containsExactly(1, 2, 3);
}

@Test
public void canParseStringToOptionalLong() throws Exception {
GenericType<Optional<Long>> type = new GenericType<Optional<Long>>() {};
assertThat(parser.parse("1", type)).contains(1l);
}

@Test
public void shouldThrowExceptionWhenParsingWildcardType() throws Exception {
shouldThrow(NoSuchRegisteredParserException.class)
.containingErrorMessage("Can not parse \"dummy-string\"")
.containingErrorMessage("to type \"?\"")
.containingErrorMessage("due to: There is no registered 'Parser' for that type.")
.whenParsing(DUMMY_STRING)
.to(new GenericType<Optional<?>>() {});
}

@Test
public void canChangeNullStrategy() throws Exception {
parser = TypeParser.newBuilder()
.setNullStringStrategy((input, helper) -> input.isEmpty())
.build();

assertThat(parser.parse("", new GenericType<Optional<String>>() {})).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.drapostolos.typeparser;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.junit.Test;

Expand All @@ -12,4 +13,12 @@ public void testToString() throws Exception {
assertThat(type.toString()).isEqualTo(String.class.toString());
}

@Test
public void shouldThrowWhenGettingParameterizedTypeArgumentsAndNotParameterized() throws Exception {
TargetType type = new TargetType(String.class);

assertThatThrownBy(() -> type.getParameterizedTypeArguments())
.hasMessageContaining("type must be parameterized")
.hasNoCause();
}
}

0 comments on commit 54c3f1c

Please sign in to comment.