package com.github.sommeri.less4j.core.compiler.expressions;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.ColorExpression;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FaultyExpression;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.compiler.expressions.strings.StringFormatter;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;
import com.github.sommeri.less4j.js.JsRegExp;
import com.github.sommeri.less4j.utils.InStringCssPrinter;
import com.github.sommeri.less4j.utils.PrintUtils;
public class StringFunctions extends BuiltInFunctionsPack {
protected static final String ESCAPE = "escape";
protected static final String E = "e";
protected static final String FORMAT = "%";
protected static final String REPLACE = "replace";
private static Map<String, Function> FUNCTIONS = new HashMap<String, Function>();
static {
FUNCTIONS.put(ESCAPE, new Escape());
FUNCTIONS.put(E, new E());
FUNCTIONS.put(FORMAT, new Format());
FUNCTIONS.put(REPLACE, new Replace());
}
public StringFunctions(ProblemsHandler problemsHandler) {
super(problemsHandler);
}
@Override
protected Map<String, Function> getFunctions() {
return FUNCTIONS;
}
}
class E extends AbstractFunction {
@Override
public Expression evaluate(List<Expression> parameters, ProblemsHandler problemsHandler, FunctionExpression call, Expression evaluatedParameter) {
if (parameters.size() > 1)
problemsHandler.wrongNumberOfArgumentsToFunctionMin(call.getParameter(), call.getName(), 1);
Expression parameter = parameters.get(0);
if (parameter.getType() == ASTCssNodeType.STRING_EXPRESSION)
return evaluate((CssString) parameter);
if (parameter.getType() == ASTCssNodeType.ESCAPED_VALUE)
return evaluate((EscapedValue) parameter);
if (parameter.getType() == ASTCssNodeType.NUMBER)
return evaluate((NumberExpression) parameter);
problemsHandler.warnEFunctionArgument(parameter);
return new FaultyExpression(call.getParameter());
}
private Expression evaluate(NumberExpression parameters) {
InStringCssPrinter printer = new InStringCssPrinter();
printer.append(parameters);
return new CssString(parameters.getUnderlyingStructure(), printer.toString(), "");
}
private Expression evaluate(EscapedValue parameters) {
return parameters;
}
private CssString evaluate(CssString parameters) {
return new CssString(parameters.getUnderlyingStructure(), parameters.getValue(), "");
}
}
class Escape extends AbstractFunction {
@Override
public Expression evaluate(List<Expression> parameters, ProblemsHandler problemsHandler, FunctionExpression call, Expression evaluatedParameter) {
if (parameters.size() > 1)
problemsHandler.wrongNumberOfArgumentsToFunctionMax(call.getParameter(), call.getName(), 1);
Expression parameter = parameters.get(0);
if (parameter.getType() == ASTCssNodeType.STRING_EXPRESSION)
return evaluate((CssString) parameter);
if (parameter.getType() == ASTCssNodeType.ESCAPED_VALUE)
return evaluate((EscapedValue) parameter);
problemsHandler.warnEscapeFunctionArgument(call.getParameter());
if (parameter.getType() == ASTCssNodeType.COLOR_EXPRESSION)
return evaluate((ColorExpression) parameter);
return call.getParameter();
}
private Expression evaluate(ColorExpression parameters) {
return new CssString(parameters.getUnderlyingStructure(), "undefined", "");
}
private CssString evaluate(EscapedValue parameters) {
String newValue = PrintUtils.toUtf8ExceptURL(parameters.getValue());
return new CssString(parameters.getUnderlyingStructure(), newValue, "");
}
private CssString evaluate(CssString parameters) {
String newValue = PrintUtils.toUtf8ExceptURL(parameters.getValue());
return new CssString(parameters.getUnderlyingStructure(), newValue, "");
}
}
class Format extends AbstractFunction {
@Override
public Expression evaluate(List<Expression> parameters, ProblemsHandler problemsHandler, FunctionExpression call, Expression evaluatedParameter) {
if (parameters.isEmpty())
problemsHandler.errFormatWrongFirstParameter(call.getParameter());
Expression format = parameters.get(0);
if (format.getType() == ASTCssNodeType.STRING_EXPRESSION)
return evaluate((CssString) format, parameters.subList(1, parameters.size()), problemsHandler, call.getUnderlyingStructure());
if (format.getType() == ASTCssNodeType.ESCAPED_VALUE)
return evaluate((EscapedValue) format, parameters.subList(1, parameters.size()), problemsHandler, call.getUnderlyingStructure());
if (!format.isFaulty())
problemsHandler.errFormatWrongFirstParameter(call.getParameter());
return new FaultyExpression(call);
}
private Expression evaluate(EscapedValue format, List<Expression> parameters, ProblemsHandler problemsHandler, HiddenTokenAwareTree technicalUnderlying) {
String newValue = format(format.getValue(), parameters, problemsHandler, technicalUnderlying);
return new CssString(format.getUnderlyingStructure(), newValue, "\"");
}
private Expression evaluate(CssString format, List<Expression> parameters, ProblemsHandler problemsHandler, HiddenTokenAwareTree technicalUnderlying) {
String newValue = format(format.getValue(), parameters, problemsHandler, technicalUnderlying);
return new CssString(format.getUnderlyingStructure(), newValue, "\"");
}
private String format(String value, List<Expression> parameters, ProblemsHandler problemsHandler, HiddenTokenAwareTree technicalUnderlying) {
StringFormatter formatter = new StringFormatter(problemsHandler);
return formatter.replaceIn(value, parameters.iterator(), technicalUnderlying);
}
}
class Replace extends AbstractMultiParameterFunction {
private TypesConversionUtils conversions = new TypesConversionUtils();
@Override
public Expression evaluate(List<Expression> parameters, ProblemsHandler problemsHandler, FunctionExpression call, Expression evaluatedParameter) {
Expression targetExpression = parameters.get(0);
String string = conversions.contentToString(targetExpression);
String pattern = conversions.contentToString(parameters.get(1));
String replacement = conversions.contentToString(parameters.get(2));
String flags = parameters.size() > 3 ? conversions.contentToString(parameters.get(3)) : "";
Expression replaced = regexp(targetExpression, string, pattern, replacement, flags, problemsHandler, call);
return replaced;
}
private Expression regexp(Expression targetExpression, String string, String pattern, String replacement, String flags, ProblemsHandler problemsHandler, FunctionExpression call) {
try {
JsRegExp exp = JsRegExp.compile(pattern, flags);
String replaced = exp.replace(string, replacement);
return buildResult(targetExpression, replaced);
} catch (IllegalArgumentException ex) {
problemsHandler.regexpFunctionError(call, ex.getMessage());
return new FaultyExpression(call.getUnderlyingStructure());
}
}
private Expression buildResult(Expression targetExpression, String replaced) {
HiddenTokenAwareTree token = targetExpression.getUnderlyingStructure();
switch (targetExpression.getType()) {
case IDENTIFIER_EXPRESSION:
return new IdentifierExpression(token, replaced);
case STRING_EXPRESSION:
CssString string = (CssString) targetExpression;
return new CssString(token, replaced, string.getQuoteType());
case ESCAPED_VALUE:
return new EscapedValue(token, replaced);
default:
return new CssString(token, replaced, "'");
}
}
@Override
protected int getMinParameters() {
return 3;
}
@Override
protected int getMaxParameters() {
return 4;
}
@Override
protected String getName() {
return StringFunctions.REPLACE;
}
@Override
protected boolean validateParameter(Expression parameter, int position, ProblemsHandler problemsHandler) {
return conversions.canConvertToString(parameter);
}
// @Override
// public Expression evaluate(List<Expression> parameters, ProblemsHandler problemsHandler, FunctionExpression call, Expression evaluatedParameter) {
// if (parameters.size()>4)
// problemsHandler.wrongNumberOfArgumentsToFunctionMax(call.getParameter(), call.getName(), 4);
//
// if (parameters.size()<3)
// problemsHandler.wrongNumberOfArgumentsToFunctionMin(call.getParameter(), call.getName(), 3);
// }
}