io.ultreia.java4all.validation.spi.field.generator.ExpressionResolver Maven / Gradle / Ivy
package io.ultreia.java4all.validation.spi.field.generator;
/*-
* #%L
* Validation :: SPI
* %%
* Copyright (C) 2021 - 2024 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static io.ultreia.java4all.validation.spi.field.generator.FieldValidationGenerator.doCast;
import static io.ultreia.java4all.validation.spi.field.generator.FieldValidationGenerator.escapeString;
import static io.ultreia.java4all.validation.spi.field.generator.FieldValidationGenerator.guessGetter;
import static io.ultreia.java4all.validation.spi.field.generator.FieldValidationGenerator.guessGetterName;
/**
* Object that can transform an expression to a java code.
*
* Created at 02/02/2024.
*
* @author Tony Chemit - [email protected]
* @since 2.0.0
*/
public class ExpressionResolver {
private static final Logger log = LogManager.getLogger(ExpressionResolver.class);
private final List config;
public ExpressionResolver(List config) {
this.config = config;
}
public static ExpressionResolver of(Config... config) {
return new ExpressionResolver(List.of(config));
}
public String resolve(String expression) throws NoSuchMethodException {
String result = expression;
for (Config config : this.config) {
result = resolve(result, config);
}
return result;
}
private String resolve(String result, Config config) throws NoSuchMethodException {
String variablePrefix = config.getVariablePrefix();
String variableName = config.getVariableName();
Class> variableType = config.getVariableType();
boolean needCast = config.isNeedCast();
return resolve(result, variablePrefix, variableName, variableType, needCast);
}
public String resolve(String expression, String variablePrefix, String variableName, Class> variableType, boolean needCast) throws NoSuchMethodException {
log.info("Resolving '{}' (using '{}' → '{}' : {})", expression, variablePrefix, variableName, variableType.getName());
int lastIndex = 0;
int index = expression.indexOf(variablePrefix);
List parts = new ArrayList<>();
while (index != -1) {
String variableExpression = expression.substring(lastIndex, index);
log.info(">> next token '{}'", variableExpression);
String resolvedExpression = resolveExpression(variableExpression, variablePrefix, variableName, variableType, needCast);
log.info("<< next token '{}' → '{}'", variableExpression, resolvedExpression);
parts.add(resolvedExpression);
lastIndex = index;
index = expression.indexOf(variablePrefix, lastIndex + 1);
}
String variableExpression = expression.substring(lastIndex);
log.info(">> last token '{}'", variableExpression);
String resolvedExpression = resolveExpression(variableExpression, variablePrefix, variableName, variableType, needCast);
log.info("<< last token '{}' → '{}'", variableExpression, resolvedExpression);
parts.add(resolvedExpression);
String result = String.join("", parts);
log.info("Resolved '{}' to '{}' (using '{}' → '{}' : {})", expression, result, variablePrefix, variableName, variableType.getName());
return result;
}
protected String resolveExpression(String variableExpression, String variablePrefix, String variableName, Class> variableType, boolean needCast) throws NoSuchMethodException {
if (!variableExpression.startsWith(variablePrefix)) {
// No variable here
return variableExpression;
}
variableExpression = variableExpression.substring(variablePrefix.length() + 1);
StringBuilder result = new StringBuilder(needCast ? doCast(variableType, variableName) : variableName);
String resultEnd = "";
int index;
if ((index = variableExpression.indexOf(' ')) > -1) {
resultEnd = variableExpression.substring(index);
variableExpression = variableExpression.substring(0, index);
}
if ((index = variableExpression.indexOf(',')) > -1) {
resultEnd = variableExpression.substring(index) + resultEnd;
variableExpression = variableExpression.substring(0, index);
}
if ((index = variableExpression.indexOf(')')) > -1) {
resultEnd = variableExpression.substring(index) + resultEnd;
variableExpression = variableExpression.substring(0, index);
}
String[] nestedParts = variableExpression.split("\\.");
if (nestedParts.length == 1) {
// no nested
if (nestedParts[0].contains("(")) {
// not a getter
result.append('.').append(nestedParts[0]);
return result + resultEnd;
}
if (Map.class.isAssignableFrom(variableType)) {
return String.format("%s.get(%s)%s", result, escapeString(nestedParts[0]), resultEnd);
} else {
String getterName = guessGetterName(variableType, nestedParts[0]);
return String.format("%s.%s()%s", result, getterName, resultEnd);
}
}
Class> type = variableType;
for (String nestedPart : nestedParts) {
if (nestedPart.contains("(")) {
// not a getter
result.append('.').append(nestedPart);
continue;
}
if (Map.class.isAssignableFrom(type)) {
type = (Class>) type.getTypeParameters()[1].getBounds()[0];
result.append(String.format(".get(%s)", escapeString(nestedPart)));
} else {
Method method = guessGetter(type, nestedPart);
type = method.getReturnType();
result.append(String.format(".%s()", method.getName()));
}
}
result.append(resultEnd);
return result.toString();
}
public static class Config {
private final String variablePrefix;
private final String variableName;
private final Class> variableType;
private final boolean needCast;
public Config(String variablePrefix, String variableName, Class> variableType, boolean needCast) {
this.variablePrefix = variablePrefix;
this.variableName = variableName;
this.variableType = variableType;
this.needCast = needCast;
}
public String getVariablePrefix() {
return variablePrefix;
}
public String getVariableName() {
return variableName;
}
public Class> getVariableType() {
return variableType;
}
public boolean isNeedCast() {
return needCast;
}
}
}