get() {
return data;
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Object}
*/
public Object get(String key) {
if (key == null || key.isEmpty()) {
FailureReporter.fail("Key can't be null or empty");
}
Object value;
var keys = splitKey.apply(key);
if (keys.size() > 1)
value = getValueFromKeys(data, keys);
else if (keyHasList.test(key))
value = getValueFromKeyList(data, key);
else {
if (data.containsKey(key))
value = data.get(key);
else {
FailureReporter.fail("This key [" + key + "] is not exist");
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
}
return value;
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link String} even if it not a string it will parse it to be string
*/
public String getTestData(String key) {
var obj = get(key);
return obj == null ? null : String.valueOf(obj);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link String} if the value is not a string it will throw an error
*/
public String getString(String key) {
return parseObjectTo(get(key), String.class);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Integer}
*/
public Integer getInteger(String key) {
return parseObjectTo(get(key), Integer.class);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Double}
*/
public Double getDouble(String key) {
return parseObjectTo(get(key), Double.class);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* to support {@link Long} values the char "L" should be added at the end of the number
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Long}
*/
public Long getLong(String key) {
String value;
try {
value = getString(key);
} catch (ClassCastException ignore) {
FailureReporter.fail("To support Long values please add 'L' at the end of the number");
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
if (value.endsWith("L"))
try {
return Long.parseLong(value.replace("L", ""));
} catch (NumberFormatException ignore) {
}
FailureReporter.fail("Can't parse the value of the key [" + key + "] to be long");
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Boolean}
*/
public Boolean getBoolean(String key) {
return parseObjectTo(get(key), Boolean.class);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* Always return date on the local time zone
*
* @param key the path to the wanted data can be a single key or a series of keys
* @return the wanted value as {@link Date}
*/
public Date getDate(String key) {
return parseObjectTo(get(key), Date.class);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @param clazz the class that data wanted to be parsed for
* @return the wanted value as {@link T}
*/
public T getAs(String key, Class clazz) {
return parseObjectTo(get(key), clazz);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @param clazz the class that data wanted to be parsed for
* @return the wanted value as {@link List}
*/
public List getListAs(String key, Class clazz) {
return parseObjectToList(get(key), clazz);
}
/**
* Fetch a single piece of data from the YAML file using a single key or a series of keys
*
* to support key series use a dot to separate keys "key1.key2"
* to support list in the key series "key[index]"
*
* @param key the path to the wanted data can be a single key or a series of keys
* @param clazz the class that data wanted to be parsed for
* @return the wanted value as {@link Map} of {@link String} and {@link T}
*/
public Map getMapAs(String key, Class clazz) {
return parseObjectToMap(get(key), clazz);
}
/**
* Used internally to fetch a value form a map using a list of keys
*
* @param map the map wanted to fetch data form
* @param keys the list of keys that point on the value
* @return the wanted value as {@link Object}
*/
private Object getValueFromKeys(Map map, List keys) {
Object value = map;
for (int i = 0; i < keys.size(); i++) {
var isLastItem = i == keys.size() - 1;
if (keyHasList.test(keys.get(i))) {
value = getValueFromKeyList(
parseObjectToMap(value, Object.class),
keys.get(i));
if (isLastItem) break;
} else
value = parseObjectToMap(value, Object.class)
.get(keys.get(i));
if (!isLastItem)
value = parseObjectToMap(value, Object.class);
}
return value;
}
/**
* Used internally to fetch data from keys that have an index for a list in it
* @param map the map wanted to fetch data form
* @param key the key that has the index on it
* @return the wanted value as {@link Object}
*/
private Object getValueFromKeyList(Map map, String key) {
var indexes = parseKeyList(key);
key = key.replaceAll(NUMBER_IN_SQUARE_BRACKETS_REGEX, "");
var value = map.get(key);
for (int i = 0; i < indexes.size(); i++) {
value = parseObjectToList(value, Object.class)
.get(indexes.stream().findFirst().orElseThrow());
}
return value;
}
/**
* Used internally to fetch all data existed in the YAML file
* @return all data in the YAML file as {@link Map} of {@link String} and {@link Object}
*/
private Map getData() {
var file = this.getFile();
Map loadedData = new Yaml().load(file);
this.closeFile(file);
return loadedData;
}
/**
* Used internally to load the wanted YAML file
* @return {@link FileInputStream} instance of the desired file
*/
private FileInputStream getFile() {
FileInputStream in;
try {
in = new FileInputStream(filePath);
} catch (FileNotFoundException rootCauseException) {
FailureReporter.fail(this.getClass(), "Couldn't find the desired file. [" + filePath + "].", rootCauseException);
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
return in;
}
/**
* Used internally to close the opened file
* @param file the file wanted to be closed
*/
private void closeFile(FileInputStream file) {
try {
file.close();
} catch (IOException rootCauseException) {
FailureReporter.fail(this.getClass(), "Couldn't close the following file. [" + filePath + "]",
rootCauseException);
}
}
/**
* Used internally to fetch the index or indexes from the key that support list
* @param key the key that contains one or more index
* @return a {@link List} that contains one or more index
*/
private List parseKeyList(String key) {
var indexes = new ArrayList();
var matcher = Pattern
.compile(NUMBER_IN_SQUARE_BRACKETS_REGEX).matcher(key);
while (matcher.find()) {
indexes.add(Integer.parseInt(
matcher
.group()
.replaceAll(SQUARE_BRACKETS_REGEX, "")
));
}
return indexes;
}
/**
* Used internally to parse {@link Object} to be any type
*
* @param obj the value wanted to be parsed
* @param clazz the class that data wanted to be parsed for
* @return the value as {@link T}
* @param the wanted type
*/
private T parseObjectTo(Object obj, Class clazz) {
T v;
try {
v = clazz.cast(obj);
} catch (ClassCastException rootCauseException) {
FailureReporter.fail(this.getClass(), "Can't parse the value of [" + obj + "] to be of type [" + clazz.getSimpleName() + "]",
rootCauseException);
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
return v;
}
/**
* Used internally to parse {@link Object} to be a {@link List}
*
* @param obj the value wanted to be parsed
* @param clazz the class that data wanted to be parsed for
* @return the value as {@link List}
* @param the wanted type
*/
private List parseObjectToList(Object obj, Class clazz) {
if (obj instanceof List> list) {
return list.stream()
.map(item -> parseObjectTo(item, clazz)).toList();
}
FailureReporter.fail("Can't parse the value of [" + obj + "] to be list");
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
/**
* Used internally to parse {@link Object} to be a {@link Map} of {@link String} and {@link T}
*
* @param obj the value wanted to be parsed
* @param clazz the class that data wanted to be parsed for
* @return the value as {@link Map} of {@link String} and {@link T}
* @param the wanted type
*/
private Map parseObjectToMap(Object obj, Class clazz) {
if (obj instanceof Map, ?> map) {
var nMap = new HashMap();
map.forEach(
(k, v) -> nMap.put(k.toString(), parseObjectTo(v, clazz))
);
return nMap;
}
FailureReporter.fail("Can't parse the value of [" + obj + "] to be map");
// unreachable because previous method throws AssertionError
throw new RuntimeException();
}
}