Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.decathlon.tzatziki.steps.ObjectSteps Maven / Gradle / Ivy
package com.decathlon.tzatziki.steps;
import com.decathlon.tzatziki.utils.*;
import com.github.jknack.handlebars.EscapingStrategy;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.helper.ConditionalHelpers;
import com.google.common.base.Splitter;
import edu.utexas.tacc.MathHelper;
import io.cucumber.core.backend.TestCaseState;
import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.runtime.SynchronizedEventBus;
import io.cucumber.datatable.DataTable;
import io.cucumber.docstring.DocString;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.cucumber.messages.types.Examples;
import io.cucumber.messages.types.TableCell;
import io.cucumber.messages.types.TableRow;
import io.cucumber.plugin.event.TestSourceParsed;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Assume;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static com.decathlon.tzatziki.steps.DynamicTransformers.register;
import static com.decathlon.tzatziki.utils.Comparison.IS_COMPARED_TO;
import static com.decathlon.tzatziki.utils.Fields.*;
import static com.decathlon.tzatziki.utils.Guard.GUARD;
import static com.decathlon.tzatziki.utils.Methods.findMethod;
import static com.decathlon.tzatziki.utils.Methods.invoke;
import static com.decathlon.tzatziki.utils.Patterns.*;
import static com.decathlon.tzatziki.utils.Time.TIME;
import static com.decathlon.tzatziki.utils.Types.wrap;
import static com.decathlon.tzatziki.utils.Unchecked.unchecked;
import static java.net.URLDecoder.decode;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("unchecked")
@Slf4j
public class ObjectSteps {
public static final Pattern LIST = Pattern.compile("(.+)\\[(\\d+)]");
public static final Pattern SUBSTRING = Pattern.compile("(.+)\\[(\\d+)-(\\d*)]");
public static final Pattern INLINE_VARIABLE = Pattern.compile(VARIABLE + ": (.*)");
@SuppressWarnings("UnstableApiUsage")
public static final Handlebars handlebars = new Handlebars()
.with((value, next) -> Mapper.toJson(value))
.with(EscapingStrategy.NOOP) // we don't want to escape the templated content, it will be written as it is
.registerHelpers(ConditionalHelpers.class)
.registerHelper("math", new MathHelper())
.registerHelper("split", (Helper) (context, options) -> {
String on = options.params.length > 0 ? options.param(0) : ",";
return Splitter.on(on)
.trimResults()
.omitEmptyStrings()
.splitToStream(decode(context, UTF_8))
.map(value -> unchecked(() -> options.fn(value)))
.collect(joining());
})
.registerHelper("foreach", (context, options) -> {
if (!(context instanceof Collection)) {
context = Mapper.read(context.toString(), List.class);
}
return ((Collection>) context).stream()
.map(value -> unchecked(() -> {
final String placeholder = "_placeholder";
final String strWithPlaceholder = options.fn(placeholder).toString();
if (Mapper.isJson(strWithPlaceholder)) {
return options.fn(value);
}
Pattern indentPattern = Pattern.compile("([\s-]*)" + placeholder);
final Matcher indentMatcher = indentPattern.matcher(strWithPlaceholder);
final String yamlStr = Mapper.toYaml(value);
if (indentMatcher.find()) {
return options.fn(yamlStr.lines().collect(joining("\n" + StringUtils.repeat(" ", indentMatcher.group(1).length()))));
}
return options.fn(value);
}
)).collect(Collectors.joining());
})
.registerHelper("concat", (firstArray, options) -> {
if (options.params.length <= 0) {
return null;
}
List> collectionsToConcat = Stream.concat(Stream.of(firstArray), Arrays.stream(options.params))
.map(arrayToConcat -> {
if (arrayToConcat instanceof Collection> array) {
return array;
} else {
return Mapper.>read(arrayToConcat.toString(), List.class);
}
}).toList();
return options.fn(collectionsToConcat.stream().flatMap(Collection::stream).collect(Collectors.toList()));
})
.registerHelper("noIndent", (str, options) -> options.handlebars.compileInline(str.toString().replaceAll("(?m)(?:^\\s+|\\s+$)", "").replaceAll("\\n", "")).apply(options.context));
static {
register(Type.class, TypeParser::parse);
register(Comparison.class, Comparison::parse);
register(Guard.class, Guard::parse);
// we will return true no matter the value as long as it is not null or "false"
register(boolean.class, value -> value != null && !value.equalsIgnoreCase("false"));
// any null value will default to 0
register(long.class, value -> ofNullable(value).map(Long::parseLong).orElse(0L));
register(int.class, value -> ofNullable(value).map(Integer::parseInt).orElse(0));
register(double.class, value -> ofNullable(value).map(Double::parseDouble).orElse(0d));
register(float.class, value -> ofNullable(value).map(Float::parseFloat).orElse(0f));
register(short.class, value -> ofNullable(value).map(Short::parseShort).orElse((short) 0));
register(byte.class, value -> ofNullable(value).map(Byte::parseByte).orElse((byte) 0));
register(Number.class, value -> ofNullable(value)
.map(s -> s.matches("\\d+") ? (Number) Integer.parseInt(s) : (Number) Double.parseDouble(s))
.orElse(0));
}
private final Map context = new LinkedHashMap<>();
// Handlebars will use the property names to lookup values, we can hijack this proxy to use properties as helpers
private final Map dynamicContext = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, (proxy, method, args) -> {
if ("get".equals(method.getName())) {
String property = (String) args[0];
String variable = null;
Matcher inlineVariable = INLINE_VARIABLE.matcher(property);
if (inlineVariable.matches()) {
variable = inlineVariable.group(1);
property = inlineVariable.group(2);
}
Object value;
if (property.startsWith("@")) {
// this is a time to parse!
value = Time.parse(property.substring(1));
} else if (property.startsWith("&")) {
// this is a file to load
value = load(getOrSelf(property.substring(1)));
} else {
// let's get the value in the context, or fallback on the name of the property
value = getOrSelf(property);
}
if (variable != null) {
add(variable, value);
}
if (value instanceof String) {
value = resolve(value);
}
return value;
}
return Methods.invokeUnchecked(context, method, args);
});
@Before(order = 1)
public void before(Scenario scenario) {
Time.setToNow();
add("_scenario", scenario);
add("_env", Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, (proxy, method, args) -> {
String name = String.valueOf(args[0]);
return switch (method.getName()) {
case "get" -> System.getenv(name);
case "containsKey" -> System.getenv(name) != null;
case "put" -> {
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"));
yield Env.export(name, String.valueOf(args[1]));
}
default -> Methods.invokeUnchecked(new LinkedHashMap<>(), method, args);
};
}));
add("_properties", Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, (proxy, method, args) -> {
String key = String.valueOf(args[0]);
return switch (method.getName()) {
case "get" -> System.getProperty(key);
case "containsKey" -> System.getProperty(key) != null;
case "put" -> System.setProperty(key, String.valueOf(args[1]));
default -> Methods.invokeUnchecked(new LinkedHashMap<>(), method, args);
};
}));
add("_examples", getExamples(scenario));
add("randomUUID", (Supplier) UUID::randomUUID);
context.remove("_method_output");
}
@After(order = Integer.MAX_VALUE)
public void after() {
Guard.awaitAsyncSteps();
}
private Map getExamples(Scenario scenario) {
try {
TestCaseState delegate = getValue(scenario, "delegate");
EventBus bus = getValue(delegate, "bus");
if (bus.getClass().getSimpleName().equals("LocalEventBus")) {
bus = getValue(bus, "parent");
}
assertThat(bus.getClass()).isEqualTo(SynchronizedEventBus.class);
bus = getValue(bus, "delegate");
Map, List> handlers = getValue(bus, "handlers");
return handlers.entrySet().stream()
.filter(e -> e.getKey().equals(TestSourceParsed.class))
.map(Map.Entry::getValue)
.map(l -> getValue(l.get(0), "arg$1"))
.map(plugin -> getValue(plugin, "currentStack"))
.map(currentStack -> currentStack instanceof ThreadLocal threadLocal ? threadLocal.get() : currentStack)
.map(currentStack -> (List>) currentStack)
.filter(stack -> stack.stream().anyMatch(s -> s.getClass().getSimpleName().startsWith("GherkinMessagesExamples")))
.findFirst()
.map(currentStack -> {
List headers = currentStack.stream()
.filter(stack -> stack.getClass().getSimpleName().equals("GherkinMessagesExamples"))
.map(o -> getValue(o, "examples"))
.map(Examples.class::cast)
.flatMap(examples -> examples.getTableHeader().map(TableRow::getCells).stream())
.findFirst().orElseThrow();
List values = currentStack.stream()
.filter(stack -> stack.getClass().getSimpleName().equals("GherkinMessagesExample"))
.map(o -> getValue(o, "tableRow"))
.map(TableRow.class::cast)
.map(TableRow::getCells)
.findFirst().orElseThrow();
assertThat(headers).hasSameSizeAs(values);
Map examples = new LinkedHashMap<>();
for (int i = 0; i < headers.size(); i++) {
examples.put(headers.get(i).getValue(), values.get(i).getValue());
}
return examples;
})
.orElseGet(Map::of);
} catch (Throwable throwable) {
log.warn(throwable.getMessage());
return Map.of();
}
}
@When(THAT + GUARD + "(?:the )?method " + VARIABLE + " of " + VARIABLE + " is called")
public void callMethod(Guard guard, String methodName, String classOrInstance) {
callMethodWithParams(guard, methodName, classOrInstance, null);
}
@When(THAT + GUARD + "(?:the )?method " + VARIABLE + " of " + VARIABLE + " is called with parameters?:$")
public void callMethodWithParams(Guard guard, String methodName, String classOrInstance, Object parametersStr) {
guard.in(this, () -> {
Object host = get(classOrInstance);
if (host == null)
callStaticMethodWithReturn(Types.rawTypeOf(TypeParser.parse(classOrInstance)), methodName, parametersStr);
else callInstanceMethodWithReturn(host, methodName, parametersStr);
});
}
private E callMethodWithReturn(Object host, Class> hostClass, String methodName, Object parametersStr) {
Map parameters = parametersStr == null ? Collections.emptyMap() : Mapper.read(toString(parametersStr), Map.class);
parameters.replaceAll((key, value) -> resolve(value));
Optional methodOpt = Methods.findMethodByParameterNames(hostClass, methodName, parameters.keySet());
Object methodOutput = methodOpt.isPresent() ? invokeMethodByParameterNames(host, methodOpt.get(), parameters)
: invokeMethodByParameterCountAndType(host, hostClass, methodName, parameters);
add("_method_output", methodOutput);
return (E) methodOutput;
}
private E callStaticMethodWithReturn(Class> hostClass, String methodName, Object parametersStr) {
return callMethodWithReturn(null, hostClass, methodName, parametersStr);
}
private E callInstanceMethodWithReturn(Object host, String methodName, Object parametersStr) {
return callMethodWithReturn(host, host.getClass(), methodName, parametersStr);
}
private Object invokeMethodByParameterCountAndType(Object host, Class> targetClass, String methodName, Map parameters) {
int parameterCount = parameters.size();
List eligibleMethodsWithoutParamTypeCheck = Methods.findMethodByNameAndNumberOfArgs(targetClass, methodName, parameterCount);
List rawParameters = parameters.values().stream().toList();
AtomicReference parsedParametersReference = new AtomicReference<>();
Method methodToInvoke = findEligibleMethodWithParamCheck(parameterCount, eligibleMethodsWithoutParamTypeCheck, rawParameters, parsedParametersReference);
return Methods.invokeUnchecked(host, methodToInvoke, parsedParametersReference.get());
}
private static Method findEligibleMethodWithParamCheck(int parameterCount, List eligibleMethods, List rawParametersStr, AtomicReference parsedParametersReference) {
return eligibleMethods.stream()
.sorted(Comparator.comparingLong(method -> Arrays.stream(method.getParameterTypes())
.filter(Class.class::equals)
.count()).reversed())
.filter(method -> {
List methodParameters = Arrays.stream(method.getParameters()).toList();
try {
parsedParametersReference.set(IntStream.range(0, parameterCount).boxed()
.map(idx -> {
Object rawParameter = rawParametersStr.get(idx);
Class> methodParameterType = methodParameters.get(idx).getType();
return wrap(rawParameter.getClass()) == wrap(methodParameterType) ? rawParameter : Mapper.read((String) rawParameter, methodParameterType);
})
.toArray(Object[]::new));
return true;
} catch (Exception e) {
return false;
}
}).findFirst().orElseThrow(() -> new AssertionError("Couldn't find method to call by parameter name or count"));
}
private static Object invokeMethodByParameterNames(Object host, Method method, Map parameters) {
Object[] parsedParameters = Arrays.stream(method.getParameters()).map(parameter -> {
Object paramValue = parameters.get(parameter.getName());
Class> methodParameterType = parameter.getType();
return wrap(paramValue.getClass()) == wrap(methodParameterType) ? paramValue : Mapper.read((String) paramValue, methodParameterType);
}).toArray(Object[]::new);
return Methods.invokeUnchecked(host, method, parsedParameters);
}
@Given(THAT + GUARD + VARIABLE + " is(?: called with)?(?: " + A + TYPE + ")?:$")
public void add_(Guard guard, String name, Type type, Object value) {
add(guard, name, type, value);
}
@Given(THAT + GUARD + VARIABLE + " (?:=|is(?: called with)?)(?: " + A + TYPE + ")? " + QUOTED_CONTENT + "$")
public void add(Guard guard, String name, Type type, Object value) {
guard.in(this, () -> {
Object typedValue = resolvePossiblyTypedObject(type, value);
getSetter(name, Types.rawTypeOf(type)).accept(typedValue);
});
}
@Given(THAT + GUARD + VARIABLE + " (?:=|is) null$")
public void nullify(Guard guard, String name) {
guard.in(this, () -> getSetter(name, Object.class).accept(null));
}
@Given(THAT + GUARD + VARIABLE + " (?:=|is) " + NUMBER + "$")
public void add(Guard guard, String name, Number value) {
guard.in(this, () -> getSetter(name, value.getClass()).accept(value));
}
@Then(THAT + GUARD + VARIABLE + " (?:==|is equal to) " + NUMBER + "$")
public void something_is_equal_to(Guard guard, String name, Number value) {
guard.in(this, () -> assertThat(String.valueOf(this.get(name))).isEqualTo(String.valueOf(value)));
}
@Then(THAT + GUARD + VARIABLE + " (?:==|is equal to) null$")
public void something_is_equal_to_null(Guard guard, String name) {
guard.in(this, () -> assertThat(this.get(name)).isNull());
}
@Then(THAT + GUARD + VARIABLE + " (?:==|is equal to) (true|false)$")
public void something_is_equal_to(Guard guard, String name, Boolean value) {
guard.in(this, () -> assertThat(this.get(name)).isEqualTo(value));
}
@Then(THAT + GUARD + VARIABLE + IS_COMPARED_TO + "(?: " + A + TYPE_PATTERN + ")?:$")
public void something_is_compared_(Guard guard, String name, Comparison comparison, Object value) {
something_is_compared(guard, name, comparison, value);
}
@Then(THAT + GUARD + VARIABLE + IS_COMPARED_TO + "(?: " + A + TYPE_PATTERN + ")? " + QUOTED_CONTENT + "$")
public void something_is_compared(@NotNull Guard guard, String name, Comparison comparison, Object value) {
guard.in(this, () -> {
Object actualObject = get(name);
String expected = resolve(value);
comparison.compare(
actualObject == null ? null : Mapper.toJson(actualObject),
actualObject == null && "null".equals(expected) ? null : Mapper.toJson(expected)
);
});
}
@Given(THAT + GUARD + "the current time is " + TIME + "$")
public void the_current_time_is(Guard guard, String expression, String timezone, String type) {
guard.in(this, () -> {
if (type != null) {
throw new UnsupportedOperationException(
"custom type of time is not supported in this step. The result must be an Instant.");
}
Time.set(Time.parse(expression + ofNullable(timezone).map(v -> " (" + v + ")").orElse("")));
add("now", resolve("{{@now}}"));
});
}
@Given(THAT + GUARD + A_USER + "(?:output|write)s? in " + QUOTED_CONTENT + "(?: " + A + TYPE + ")?:$")
public void output_in(Guard guard, String sourcePath, Type type, Object sourceValue) {
guard.in(this, () -> {
Object value = resolvePossiblyTypedObject(type, sourceValue);
if (!(value instanceof String)) {
value = Mapper.toJson(value);
}
Path resourcePath;
try {
resourcePath = Paths.get(requireNonNull(requireNonNull(this.getClass().getResource("/")).toURI()));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
try {
Path path = Paths.get(resourcePath.toString(), resolve(sourcePath)).normalize();
if (!Paths.get(path.toString()).normalize().startsWith(resourcePath)) {
throw new AssertionError("no escape from the resource folder is allowed!");
}
File file = new File(path.toString());
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Files.writeString(path, (String) value);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
@SneakyThrows
public String resolve(Object content) {
content = toString(content);
return resolve((String) content);
}
private String toString(Object content) {
if (content instanceof DataTable dataTable) {
content = Mapper.toJson(dataTable
.getTableConverter()
.toMaps((DataTable) content, String.class, String.class)
.stream()
.map(map -> map.entrySet().stream().collect(HashMap::new,
(newMap, entry) -> newMap.put(entry.getKey(), resolve(entry.getValue())), HashMap::putAll))
.map(this::dotToMap)
.collect(Collectors.toList()));
} else if (content instanceof DocString docString) {
content = docString.getContent();
}
return String.valueOf(content);
}
@SneakyThrows
public String resolve(String content) {
if (content == null) return null;
if (content.contains("{{")) {
content = handlebars.compileInline(content).apply(dynamicContext);
}
return content;
}
public void add(String name, Object value) {
Map host = context;
if (name.contains(".")) {
int split = name.indexOf(".");
do {
host = (Map) host.computeIfAbsent(name.substring(0, split), k -> new LinkedHashMap<>());
name = name.substring(split + 1);
split = name.indexOf(".");
} while (split > -1);
}
host.put(name, value);
}
public E getHost(Object host, String property, boolean instanciateIfNotFound) {
int split = property.indexOf(".");
while (split > -1) {
host = getProperty(host, property.substring(0, split), instanciateIfNotFound);
property = property.substring(split + 1);
split = property.indexOf(".");
}
return getProperty(host, property, instanciateIfNotFound);
}
public E applyToHost(String hostName, boolean instanciateIfNotFound, BiFunction function) {
return applyToHost(context, hostName, instanciateIfNotFound, function);
}
public E applyToHost(Object host, String hostName, boolean instanciateIfNotFound, BiFunction function) {
int bracket = hostName.lastIndexOf("(");
int split = bracket > -1 ? hostName.substring(0, bracket).lastIndexOf(".") : hostName.lastIndexOf(".");
if (split > -1) {
host = getHost(host, hostName.substring(0, split), instanciateIfNotFound);
hostName = hostName.substring(split + 1);
}
return function.apply(host, hostName);
}
public E get(String name) {
return getOrDefault(name, null);
}
public E getOrSelf(String name) {
return (E) getOrDefault(name, name);
}
public E getOrDefault(String name, E value) {
return (E) ofNullable(applyToHost(name, false, (host, property) -> getProperty(host, property, false))).orElse(value);
}
public Object resolvePossiblyTypedObject(Type type, Object value) {
if (type != null) {
return Mapper.read(resolve(value), type);
}
return resolve(value);
}
@NotNull
public Map dotToMap(Map input) {
Map output = new LinkedHashMap<>();
input.forEach((key, value) -> {
Object object = null;
if (value instanceof String string) {
try {
if (Mapper.isList(string)) {
object = Mapper.read(string, List.class);
} else if (Mapper.firstNonWhitespaceCharacterIs(string, '{')) {
object = Mapper.read(string, Map.class);
} else if (!value.equals("null")) {
object = value;
}
} catch (Exception e) {
// our assumptions were incorrect, ignoring the error and keeping the value as String
object = value;
}
}
Class> parameterType = object == null ? Object.class : object.getClass();
applyToHost(output, key, true, (o, s) -> getSetter(o, s, parameterType)).accept(object);
});
return output;
}
public Consumer getSetter(String name, Class> parameterType) {
return applyToHost(name, true, (host, property) -> getSetter(host, property, parameterType));
}
@NotNull
public Consumer getSetter(Object host, String property, Class> parameterType) {
Matcher isList = LIST.matcher(property);
if (isList.matches()) {
List list = getProperty(host, isList.group(1), true);
if (list == null) {
list = new ArrayList<>();
getSetter(host, isList.group(1), parameterType).accept(list);
}
List target = list;
return value -> target.set(Integer.parseInt(isList.group(2)), value);
} else if (host instanceof Map map) {
return value -> map.put(property, value);
} else if (hasField(host, property)) {
return value -> setValue(host, property, value);
} else if (findMethod(host.getClass(), property, parameterType).isPresent()) {
return value -> invoke(host, property, value);
} else if (findMethod(host.getClass(), "set" + capitalize(property), parameterType).isPresent()) {
return value -> invoke(host, "set" + capitalize(property), value);
}
return value -> {
throw new UnsupportedOperationException("Couldn't assign value %s to host %s".formatted(value, host + "." + property));
};
}
private E getProperty(Object host, String property, boolean instanciateIfNotFound) {
if (host == null) {
return null;
}
Matcher isList = LIST.matcher(property);
Matcher isSubString = SUBSTRING.matcher(property);
if (isList.matches()) {
host = getProperty(host, isList.group(1), instanciateIfNotFound);
if (host != null) {
if (host instanceof String hostStr) {
host = Mapper.read(hostStr, List.class);
}
if (host instanceof List) {
return (E) ((List>) host).get(Integer.parseInt(isList.group(2)));
}
throw new IllegalArgumentException("host is not a list but a " + host.getClass());
}
} else if (isSubString.matches()) {
host = getProperty(host, isSubString.group(1), instanciateIfNotFound);
if (host != null) {
if (!(host instanceof String)) {
host = Mapper.toJson(host);
}
int start = Integer.parseInt(isSubString.group(2));
int end = Optional.ofNullable(isSubString.group(3))
.filter(StringUtils::isNotBlank)
.map(Integer::parseInt)
.orElse(((String) host).length());
return (E) ((String) host).substring(start, Math.max(((String) host).length(), end));
}
} else if (host instanceof Map map && (map.containsKey(property) || instanciateIfNotFound)) {
if (map.containsKey(property)) {
return (E) map.get(property);
} else if (instanciateIfNotFound) {
Map newMap = new LinkedHashMap<>();
map.put(property, newMap);
return (E) newMap;
}
} else if (property.matches(TYPE_PATTERN) && TypeParser.hasClass(property)) {
return (E) TypeParser.parse(property);
} else if (hasField(host, property)) {
return getValue(host, property);
} else if (property.matches("\\w+\\(((?:[^)],?)*+)\\)")) {
String[] splitMethodNameAndArgs = property.split("[()]");
String methodName = splitMethodNameAndArgs[0];
String[] parameters = splitMethodNameAndArgs.length == 1 ? new String[0] : splitMethodNameAndArgs[1].split("[, ]+");
String parametersAsJson = Mapper.toJson(IntStream.range(0, parameters.length).boxed().collect(Collectors.toMap(Function.identity(), idx -> parameters[idx])));
return host instanceof Class> hostClass
? callStaticMethodWithReturn(hostClass, methodName, parametersAsJson)
: callInstanceMethodWithReturn(host, methodName, parametersAsJson);
} else if (findMethod(host.getClass(), property).isPresent()) {
return invoke(host, property);
} else if (findMethod(host.getClass(), "get" + capitalize(property)).isPresent()) {
return invoke(host, "get" + capitalize(property));
} else if (findMethod(host.getClass(), "is" + capitalize(property)).isPresent()) {
return invoke(host, "is" + capitalize(property));
} else if (property.matches("\\d+")) {
if (host instanceof String hostStr) {
host = Mapper.read(hostStr, List.class);
}
if (host instanceof List) {
return (E) ((List>) host).get(Integer.parseInt(property));
}
} else if (host instanceof String hostStr) {
try {
host = Mapper.read(hostStr, Map.class);
return (E) ((Map, ?>) host).get(property);
} catch (Exception e) {
// not a map
}
}
return null;
}
public int getCount(String countAsString) {
if (countAsString.equals("a")) {
return 1;
} else if (countAsString.matches("\\d+")) {
return Integer.parseInt(countAsString);
} else {
return Integer.parseInt(get(countAsString));
}
}
@SneakyThrows
public static String load(String resource) {
if (resource.startsWith("/")) {
resource = resource.substring(1);
}
try (InputStream inputStream = ObjectSteps.class.getClassLoader().getResourceAsStream(resource)) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (inputStream == null) {
Assert.fail("couldn't find resource: " + resource);
}
IOUtils.copy(inputStream, outputStream);
return outputStream.toString(UTF_8).trim();
}
}
}