Types library can convert any object type into a required one, iterate any object, do structure transformations and wrap generic types.
Convert!
// from
String str = "1234";
String str = "0b11010";
String str = "0x46";
// to
int number = Types.INTEGER.parse(str);
// from
double[] array = new double[] { 10.3, 8.4, 5.0 };
// to
List<Float> list = AnyObject.of(array).asList(Types.FLOAT);
// from
List<String> strList = List.of(
"1234", "true",
"55", "false",
"10", "true"
);
// to
Map<Integer, Boolean> intBooleanMap = new TypeOf<Map<Integer, Boolean>>(){}.parse(from);Iterate!
// single object
int number = 1234;
for (int element : AnyIterable.<Integer>ofAny(number)) {
// do something
}
// array
double[] array = new double[] { 10.3, 8.4, 5.0 };
for (double element : AnyIterable.of(array)) {
// do something
}
// list
List<Float> list = List.of(10.3, 8.4, 5.0);
for (Float element : AnyIterable.of(list)) {
// do something
}
// map
Map<Integer, Boolean> to = Map.of(
1234, true,
55, false,
10, true
);
for (Map.Entry<Integer, Boolean> entry : AnyIterable.of(to)) {
// do something
}Wrap!
// Original String list
List<String> stringList = List.of("1", "2", "3", "4");
// A view of original list as Integer list
List<Integer> integerList = new WrappedList<>(stringList, TypeWrapper.of(Types.STRING, Types.INTEGER));
// So this
if (integerList.contains(3)) {
// ...
}
// Is the equivalent of
if (stringList.contains("3")) {
// ...
}How to implement Types library in your project.
This library contains the following artifacts:
types- The main project, a type parsing and iteration API.types-annotated- Annotated type parsing API.types-mapper- (experimental) Type mapping and transformation API.types-wrapper- Type wrapping API.
build.gradle
plugins {
id 'com.gradleup.shadow' version '9.2.2'
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.saicone.types:types:1.4.1'
}
jar.dependsOn (shadowJar)
shadowJar {
// Relocate types
relocate 'com.saicone.types', project.group + '.libs.types'
// Exclude unused classes (optional)
minimize()
}build.gradle.kts
plugins {
id("com.gradleup.shadow") version "9.2.2"
}
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.saicone.types:types:1.4.1")
}
tasks {
jar {
dependsOn(tasks.shadowJar)
}
shadowJar {
// Relocate types
relocate("com.saicone.types", "${project.group}.libs.types")
// Exclude unused classes (optional)
minimize()
}
}pom.xml
<repositories>
<repository>
<id>Jitpack</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.saicone.types</groupId>
<artifactId>types</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<relocations>
<!-- Relocate types -->
<relocation>
<pattern>com.saicone.types</pattern>
<shadedPattern>${project.groupId}.libs.types</shadedPattern>
</relocation>
</relocations>
<!-- Exclude unused classes (optional) -->
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</build>A "supported" type (for example) doesn't mean you can magically convert a Map<String, Integer> into Boolean,
every object type that you want to parse must have a minimum sense with the conversion or null will be return,
for example a "1.4" can be converted into any Number type.
Note
Any supported (primitive) object type can be returned as array value of its type.
Since primitive types cannot be explicitly null, they will be returned with a fallback value.
Click to show
charor'\0'booleanorBoolean.FALSEbyteorByte.MIN_VALUEshortorShort.MIN_VALUEintorInteger.MIN_VALUEfloatorFloat.MIN_VALUElongorLong.MIN_VALUEdoubleorDouble.MIN_VALUE
Well known Java objects and the accepted types to properly parse them.
Click to show
java.lang.Objectjava.lang.Stringjava.lang.Characterjava.lang.Booleantrue/falseoryes/nostring- Integer
0or1 - Decimal
0or between0and1
java.lang.Numberjava.lang.Bytejava.lang.Shortjava.lang.integerjava.lang.Floatjava.lang.Longjava.lang.Doublejava.lang.Class<?>- Qualified name, for example
some.package.for.MyClass - File path, for example
some/package/for/MyClass.classorC:\some\package\for\MyClass.java - Descriptor, for example
Lsome/package/for/MyClass; - Readable name, for example
int[][]
- Qualified name, for example
java.math.BigIntegerjava.math.BigDecimaljava.util.UUID- 36-length
String(with dashes)00000000-0000-0000-0000-000000000000 - 32-length
String(without dashes)00000000000000000000000000000000 - 4-length array (converted to int)
[000000000, 000000000, 000000000, 000000000] - 2-length array (converted to long)
[mostSigBits, leastSigBits]
- 36-length
java.util.regex.PatternStringStringwith a type annotated withPatternFlags
java.util.BitSetjava.net.URIStringURLFilePath
java.net.URLStringURIFilePath
java.io.FileStringseparated by/String[]Path
java.nio.file.PathStringseparated by/String[]File
java.time.DurationStringformated like15 MINUTES AND 30 SECONDSString[]formated like["15 MINUTES", "30 SECONDS"]
java.time.LocalDate- Epoch day
Long - 2-length array (converted to int)
[year, dayOfYear] - 3-length array (converted to int)
[year, month, day] - ISO-8601
String
- Epoch day
java.time.LocalTime- Seconds of day
Long - 2-length array (converted to int)
[hour, minute] - 3-length array (converted to int)
[hour, minute, second] - 4-length array (converted to int)
[hour, minute, second, nanoOfSecond] Stringformatted ashour:minute:second.nanoOfSecond, examples:"10:30","10:40:05","09:08:21.35"
- Seconds of day
java.time.LocalDateTime- Epoch seconds
Long - 5-length array (converted to int)
[year, month, day, hour, minute] - 6-length array (converted to int)
[year, month, day, hour, minute, second] - 7-length array (converted to int)
[year, month, day, hour, minute, second, nanoOfSecond] - ISO-8601
Stringseparated byTwith time formatted ashour:minute:second.nanoOfSecond
- Epoch seconds
Note
Any Number type can be parsed from:
- Enum, by extracting ordinal value
- Boolean
true = 1 | false = 0 - Decimal, for example
35 - Binary
0[bB][0-1], for example0b11010 - Hex
0[xX][0-9A-Fa-f], for example0x46 - Hex color
#[0-9A-Fa-f], for example#46 - Octal
0[oO]?[0-7], for example075or0o75
And also detect:
- Leading signs
+ - - Unsigned suffix
uorU - Number suffixes
b B s S i I f F l L d D
Typical Java objects with parameters.
Click to show
java.lang.Enum<?>- Name
String(case-insensitive) - Ordinal
Number - Other
Enumby extracting ordinal value.
- Name
java.util.Collection<E>- Can be any Java object that implementsCollectionjava.util.Map<K, V>- Can be any Java object that implementsMapMapIterableobject which its elements will be converted into keys and values.
(The type parameters E, K and V can be any supported type)
Functional Java objects to handle any type parser in a flexible way.
Click to show
java.util.Optional<E>- Contains parsed value, empty optional type is return when parse fails or throw exceptionjava.util.concurrent.CompletableFuture<E>- Maintain parse function into non-null return value (may throw NullPointerException)
(The type parameter E can be any supported type)
How to use Types library.
By default, Types library can convert 6 data types using 4 different methods.
- TypeParser - Make your own implementation of type conversion.
- Types - Same as TypeParser, but every parser is cached.
- AnyObject - Encapsulated object with methods to convert with Types or create one-time use TypeParser.
- TypeOf - Same as Types, but computes automatically the required type object parser (including generic objects).
Single objects:
String str = "1234";
// Using TypeParser
TypeParser<Double> parser = (object) -> Double.parseDouble(String.valueOf(object));
double number = parser.parse(str);
// Using Types (Same as TypeParser, but extract first element of any iterable or array object)
int number = Types.INTEGER.parse(str);
// Using AnyObject
float number = AnyObject.of(str).asFloat();
// Using TypeOf
TypeOf<Long> type = new TypeOf<Long>(){};
long number = type.parse(str);Collections:
int number = 1234;
// Using TypeParser
TypeParser<List<Integer>> parser = ListParser.of(Types.INTEGER);
List<Integer> list = parser.parse(number);
// Using Types
List<Double> list = Types.DOUBLE.list().parse(number);
// Using AnyObject
List<Float> list = AnyObject.of(number).asList(Types.FLOAT);
// Using TypeOf
TypeOf<List<Long>> type = new TypeOf<List<Long>>(){};
List<Long> list = type.parse(number);Object Arrays:
String str = "1234";
// Using TypeParser
TypeParser<String[]> parser = ArrayParser.of(String.class);
String[] array = parser.parse(str);
// Using Types
Integer[] array = Types.INTEGER.array().parse(str);
// Using AnyObject
String[] array = AnyObject.of(str).asArray(Types.STRING);
// Using TypeOf
TypeOf<Integer[]> type = new TypeOf<Integer[]>(){};
Integer[] array = type.parse(str);Primitive Arrays:
String str = "1234";
// Using TypeParser
TypeParser<int[]> parser = ArrayParser.of(int.class);
int[] array = parser.parse(str);
// Using Types
float[] array = Types.of(float.class).array().parse(str);
// Using AnyObject
double[] array = AnyObject.of(str).asArray(Types.of(double.class));
// Using TypeOf
TypeOf<long[]> type = new TypeOf<long[]>(){};
long[] array = type.parse(str);Enums:
String str = "VALUE_NAME";
// Using TypeParser
TypeParser<MyEnum> parser = EnumParser.of(MyEnum.class);
MyEnum value = parser.parse(str);
// Using AnyObject
MyEnum value = AnyObject.of(str).asEnum(MyEnum.class);
// Using TypeOf
TypeOf<MyEnum> type = new TypeOf<MyEnum>(){};
MyEnum value = type.parse(str);Maps:
Map<String, String> map = new HashMap<>();
map.put("1234", "true");
map.put("55", "false");
map.put("12", "true")
// Using TypeParser
TypeParser<Map<Integer, Boolean>> parser = MapParser.of(Types.INTEGER, Types.BOOLEAN);
Map<Integer, Boolean> value = parser.parse(map);
// Using AnyObject
Map<Integer, Boolean> value = AnyObject.of(map).asMap(Types.INTEGER, Types.BOOLEAN, new HashMap<>());
// Using TypeOf
TypeOf<Map<Integer, Boolean>> type = new TypeOf<Map<Integer, Boolean>>(){};
Map<Integer, Boolean> value = type.parse(map);Tip
If you create a TypeParser or use TypeOf is suggested to save as static final field.
How to iterate any object value.
// Single object
String value = "text";
for (String str : AnyIterable.<String>ofAny(value)) {
// do something
}
// Array / Collection
Integer[] value = new Integer[] { 1, 2, 3, 4 };
int[] value = new int[] { 1, 2, 3, 4 };
List<Integer> value = new ArrayList<>();
for (int i : AnyIterable.of(value)) {
// do something
}
// Map
Map<String, Integer> value = new HashMap<>();
for (Map.Entry<String, Integer> entry : AnyIterable.of(value)) {
// do something
}Type wrapping is a lazy object conversion that work in different dimensions using two types.
Type A: Is the base type of object, you can get it by unwrap the type B.
Type B: Is the type of object that wrap type A, in other words, to show A as B.
One dimension wrapper:
A one dimension TypeWrapper can be created using one type parser to wrap or unwrap any representation of a type.
TypeParser<Integer> parser = Types.INTEGER;
// Wrapper that only wrap objects
TypeWrapper<Integer, Integer> onlyWrap = TypeWrapper.wrap(parser);
// Wrapper that only unwrap objects
TypeWrapper<Integer, Integer> onlyUnwrap = TypeWrapper.unwrap(parser);
// Example
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4));
// By only wrapping values you got a list that pass values to type wrapper for outgoing operations
// Which is useless in this case since the wrapper doesn't make a value transformation
List<Integer> integerList = new WrappedList<>(list, onlyWrap);
// This value was processed on wrapper, int this case any change was applied
int i = integerList.get(0);
// By only unwrapping values you got a list that accept any kind of object that can be converted to Integer on incoming operations
// Which is useful to take advantage of type parsing
List<Integer> integerList = new WrappedList<>(list, onlyUnwrap);
// In this case, the following operation is valid since any String is converted to Integer
if (integerList.contains("2")) {
integerList.remove("1");
}Two dimension wrapper with cast:
A two dimension TypeWrapper can be created using one type parser to wrap type A or unwrap type B and expect that the other type must be obtained using a regular cast.
TypeParser<Long> parserA = Types.LONG;
TypeParser<Integer> parserB = Types.INTEGER;
// Wrapper that only wrap type A to show it as B
// Meaning that you only need to provide a type parser to convert A to B
TypeWrapper<Long, Integer> onlyWrap = TypeWrapper.wrap(parserB);
// Wrapper that only unwrap type B to return it as A
// Meaning that you only need to provide a type parser to convert B to A
TypeWrapper<Long, Integer> onlyUnwrap = TypeWrapper.unwrap(parserA);
// Example
List<Long> longList = new ArrayList<>(List.of(1L, 2L, 3L, 4L));
// By only wrapping values you got a list that show values as Integer for outgoing operations
// And try to cast incoming values as Long for outgoing operations
List<Integer> integerList = new WrappedList<>(longList, onlyWrap);
// This value was converted from Long
int i = integerList.get(0);
// By only unwrapping values you got a list that try to cast outgoing values as Integer for incoming operations
// And accepts Long-representation values for incoming operations (for example, an Integer)
List<Integer> integerList = new WrappedList<>(longList, onlyUnwrap);
// In this case the list accept any Long representation as well
if (integerList.contains(2L)) {
integerList.remove("1L");
}Two dimension wrapper:
A two dimension TypeWrapper can be created using one type parser to wrap type A and other type parser to unwrap type B.
TypeParser<Integer> parserA = Types.INTEGER;
TypeParser<String> parserB = Types.STRING;
// A wrapper that make Integers look like Strings by wrapping any String representation
// And make String look like Integers by unwrapping any Integer representation
TypeWrapper<Integer, String> wrapper = TypeWrapper.of(parserA, parserB);
// Example
List<Integer> integerList = new ArrayList<>(List.of(1, 2, 3, 4));
// By providing the wrapper, the Integer list will act as a String list
List<String> stringList = new WrappedList<>(integerList, wrapper);
if (integerList.contains("2")) {
integerList.remove("1");
}How to register your own types.
// Register
TypeParser<MyObject> parser = (object) -> {
// Convert into MyObject...
};
Types.put(MyObject.class, parser);
// Unregister
Types.remove(MyObject.class);
// Get
TypeParser<MyObject> parser = Types.of(MyObject.class);