package com.github.sommeri.less4j.core.problems;
import java.util.List;
import com.github.sommeri.less4j.LessCompiler.Problem;
import com.github.sommeri.less4j.LessProblems;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.ArgumentDeclaration;
import com.github.sommeri.less4j.core.ast.Body;
import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator;
import com.github.sommeri.less4j.core.ast.DetachedRuleset;
import com.github.sommeri.less4j.core.ast.DetachedRulesetReference;
import com.github.sommeri.less4j.core.ast.EscapedSelector;
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.Import;
import com.github.sommeri.less4j.core.ast.MediaQuery;
import com.github.sommeri.less4j.core.ast.MixinReference;
import com.github.sommeri.less4j.core.ast.NestedSelectorAppender;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.PseudoClass;
import com.github.sommeri.less4j.core.ast.ReusableStructure;
import com.github.sommeri.less4j.core.ast.ReusableStructureName;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.Selector;
import com.github.sommeri.less4j.core.ast.SignedExpression;
import com.github.sommeri.less4j.core.ast.StyleSheet;
import com.github.sommeri.less4j.core.ast.SupportsLogicalOperator;
import com.github.sommeri.less4j.core.ast.UnknownAtRule;
import com.github.sommeri.less4j.core.ast.Variable;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.utils.LessPrinter;
import com.github.sommeri.less4j.utils.PrintUtils;
public class ProblemsHandler implements LessProblems {
private ProblemsCollector collector = new ProblemsCollector();
private LessPrinter printer = new LessPrinter();
@Override
public String toString() {
return collector.toString();
}
public void wrongMemberInCssBody(ASTCssNode member, Body node) {
ASTCssNode parent = node.getParent()==null? node : node.getParent();
ASTCssNodeType parentType = node.getParent()==null? ASTCssNodeType.STYLE_SHEET : node.getParent().getType();
addWarning(member, "Compilation resulted in incorrect CSS. The " + PrintUtils.toTypeName(member) + " ended up inside a body of " + PrintUtils.toTypeName(parentType) +" located at "+PrintUtils.toLocation(parent)+".");
}
public void errWrongSupportsLogicalOperator(SupportsLogicalOperator node, String faultyOperator) {
addError(node, "@supports at rule does not support '" + faultyOperator + "' as a binary logical operator. You can use only 'and' and 'or'.");
}
public void wrongMemberInLessBody(ASTCssNode member, Body node) {
ASTCssNodeType parentType = node.getParent()==null? ASTCssNodeType.STYLE_SHEET : node.getParent().getType();
addError(member, "The element " + PrintUtils.toTypeName(member) + " is not allowed to be a " + PrintUtils.toTypeName(parentType) +" member.");
}
public void notAColor(ASTCssNode node, String text) {
addError(node, "The string \"" + text + "\" is not a valid color.");
}
public void warnLessjsIncompatibleSelectorAttributeValue(Expression value) {
addWarning(value, "This works, but is incompatible with less.js. Only less.js compatible selector attribute values are string, number and identifier.");
}
public void warnMerginMediaQueryWithMedium(MediaQuery mediaQuery) {
addWarning(mediaQuery, "Attempt to merge media query with a medium. Merge removed medium from inner media query, because the result CSS would be invalid otherwise.");
}
public void unknownImportOption(Import node, String text) {
addError(node, "Unknown import option \"" + text);
}
public void warnInconsistentSupportsLogicalConditionOperators(SupportsLogicalOperator faulty, SupportsLogicalOperator masterOperator) {
String faultySymbol = faulty.getOperator().getSymbol();
String masterSymbol = masterOperator.getOperator().getSymbol();
addWarning(faulty, "CSS specification does not allow mixing of 'and', 'or', and 'not' operators without a layer of parentheses. Operators '" + faultySymbol + "' at " + PrintUtils.toLocation(faulty) + "' and '" + masterSymbol + "' at " + PrintUtils.toLocation(masterOperator) + " are in the same layer of parentheses.");
}
public void warnLessImportNoBaseDirectory(Expression urlExpression) {
addWarning(urlExpression, "Attempt to import less file with an unknown compiled file location. Import statement left unchanged.");
}
public void errorFileReferenceNoBaseDirectory(ASTCssNode node, String path) {
addError(node, "Attempt to reference file with an unknown compiled file location. Relative path: " + path);
}
public void errorFileCanNotBeRead(ASTCssNode node, String filename) {
addError(node, "The file " + filename + " can not be read.");
}
public void errorFileNotFound(ASTCssNode node, String filename) {
addError(node, "The file " + filename + " does not exist.");
}
public void errorUnknownImageFileFormat(ASTCssNode node, String filename) {
addError(node, "Unknown image file format for " + filename);
}
public void errorWrongImport(Expression urlExpression) {
addError(urlExpression, "Unsupported @import url kind. File link expression in @import can handle only strings and urls.");
}
public void nestedAppenderOnTopLevel(NestedSelectorAppender appender) {
addError(appender, "Appender symbol is not allowed inside top level rulesets.");
}
public void interpolatedMixinReferenceSelector(MixinReference reference) {
addError(reference.getFinalName(), "Interpolation is not allowed inside mixin references.");
}
public void interpolatedNamespaceReferenceSelector(MixinReference reference) {
addError(reference, "Interpolation is not allowed inside namespace references.");
}
public void wrongMemberBroughtIntoBody(ASTCssNode reference, ASTCssNode member, ASTCssNode body) {
ASTCssNode parent = body.getParent()==null? body : body.getParent();
addError(reference, "The reference brought " + PrintUtils.toTypeName(member) + " from " + PrintUtils.toLocation(member) + " into " + PrintUtils.toTypeName(parent) + " which started at " + PrintUtils.toLocation(body) + ". Compilation produced an incorrect CSS.");
}
public void errFormatWrongFirstParameter(Expression param) {
addError(param, "First argument of format function must be either string or escaped value.");
}
public void wrongNumberOfArgumentsToFunction(Expression param, String function, int expectedArguments) {
addError(param, "Wrong number of arguments to function '" + function + "', should be " + expectedArguments + ".");
}
public void wrongNumberOfArgumentsToFunctionMin(Expression param, String function, int expectedArguments) {
addError(param, "Wrong number of arguments to function '" + function + "', should be at least " + expectedArguments + ".");
}
public void wrongNumberOfArgumentsToFunctionMax(Expression param, String function, int expectedArguments) {
addError(param, "Wrong number of arguments to function '" + function + "', should be at maximum " + expectedArguments + ".");
}
public void wrongArgumentTypeToFunction(Expression param, String function, ASTCssNodeType received, ASTCssNodeType... expected) {
addError(param, "Wrong argument type to function '" + function + "', expected " + PrintUtils.toTypeNames(expected) + " saw " + PrintUtils.toTypeName(received) + ".");
}
public void warnScriptingNotSupported(ASTCssNode call, String errorName) {
addError(call, errorName + "are not supported. The problem can be solved using custom functions. Compilation resulted in incorrect CSS.");
}
public void variablesCycle(List<Variable> cycle) {
addError(cycle.get(0), "Cyclic references among variables: " + printer.toVariablesString(cycle));
}
public void mixinsCycle(List<MixinReference> cycle) {
addError(cycle.get(0), "Cyclic references among mixins: " + printer.toMixinReferencesString(cycle));
}
public void deprecatedImportOnce(Import errorNode) {
addWarning(errorNode, "`@import-once <url>` have been deprecated. Use `@import (once) <url>` instead. Input file is less.js incompatible.");
}
public void deprecatedImportMultiple(Import errorNode) {
addWarning(errorNode, "`@import-multiple <url>` have been deprecated. Use `@import (multiple) <url>` instead. Input file is less.js incompatible.");
}
public void deprecatedSyntaxEscapedSelector(EscapedSelector errorNode) {
addWarning(errorNode, "Selector fragment (~" + errorNode.getQuoteType() + errorNode.getValue() + errorNode.getQuoteType() + ") uses deprecated (~\"escaped-selector\") syntax. Use selector interpolation @{variableName} instead.");
}
public void warnEscapeFunctionArgument(Expression errorNode) {
addWarning(errorNode, "Escape function argument should be a string.");
}
public void warnExtendInsideExtend(Selector errorNode) {
addWarning(errorNode, "Target selector of extend contains nested extend. Nested extend will be ignored. The behaviour of less.js and less4j differ on this type of input.");
}
public void warnEFunctionArgument(Expression errorNode) {
addError(errorNode, "e function argument should be a string.");
}
public void variableAsPseudoclassParameter(PseudoClass errorNode) {
addWarning(errorNode.getParameter(), "Variables as pseudo classes parameters have been deprecated. Use selector interpolation @{variableName} instead.");
}
public void undefinedMixinParameterValue(ReusableStructure mixin, ArgumentDeclaration declaration, MixinReference reference) {
collector.addError(createUndefinedMixinParameterValue(mixin, declaration, reference));
}
private CompilationError createUndefinedMixinParameterValue(ReusableStructure mixin, ArgumentDeclaration declaration, MixinReference reference) {
return new CompilationError(reference, "Undefined parameter " + declaration.getVariable().getName() + " of mixin " + reference.getFinalNameAsString() + " defined on line " + mixin.getSourceLine());
}
public void undefinedVariable(Variable variable) {
collector.addError(createUndefinedVariable(variable.getName(), variable));
}
public void undefinedVariable(String name, ASTCssNode ifErrorNode) {
collector.addError(createUndefinedVariable(name, ifErrorNode));
}
@SuppressWarnings("unused")
private CompilationError createUndefinedVariable(Variable variable) {
return createUndefinedVariable(variable.getName(), variable);
}
private CompilationError createUndefinedVariable(String name, ASTCssNode variable) {
return new CompilationError(variable, "The variable \"" + name + "\" was not declared.");
}
public void undefinedMixin(MixinReference reference) {
collector.addError(createUndefinedMixin(reference));
}
private CompilationError createUndefinedMixin(MixinReference reference) {
return createUndefinedMixin(reference.getFinalName(), reference);
}
private CompilationError createUndefinedMixin(ReusableStructureName name, MixinReference reference) {
return new CompilationError(reference.getFinalName(), "Could not find mixin named \"" + name.asString() + "\".");
}
public void detachedRulesetNotfound(DetachedRulesetReference reference) {
addError(reference, "Could not find detached ruleset for \"" + reference.getVariable().getName() + "\".");
}
public void wrongDetachedRulesetReference(DetachedRulesetReference reference, Expression value) {
addError(reference, "Detached ruleset reference \"" + reference.getVariable().getName() + "\" does not evaluate to detached ruleset. It resolved to expression defined at " + printer.toPosition(value));
}
public void noMixinHasRightParametersCountError(MixinReference reference) {
collector.addError(createNoMixinHasRightParametersCountError(reference));
}
public void patternsInMatchingMixinsDoNotMatch(MixinReference reference) {
collector.addError(createPatternsInMatchingMixinsDoNotMatch(reference.getFinalName(), reference));
}
private CompilationError createPatternsInMatchingMixinsDoNotMatch(ReusableStructureName name, MixinReference reference) {
return new CompilationError(reference.getFinalName(), "No mixin named \"" + name.asString() + "\" has matching patterns.");
}
private CompilationError createNoMixinHasRightParametersCountError(MixinReference reference) {
return createNoMixinHasRightParametersCountError(reference.getFinalName(), reference);
}
private CompilationError createNoMixinHasRightParametersCountError(ReusableStructureName name, MixinReference reference) {
return new CompilationError(reference.getFinalName(), "No mixin named \"" + name.asString() + "\" has the right number of parameters.");
}
public void undefinedNamespace(MixinReference reference) {
collector.addError(createUndefinedNamespace(reference));
}
private CompilationError createUndefinedNamespace(MixinReference reference) {
return createUndefinedNamespace(printer.toString(reference), reference);
}
private CompilationError createUndefinedNamespace(String name, MixinReference reference) {
return new CompilationError(reference, "The namespace \"" + name + "\" was not declared.");
}
public void nonNumberNegation(SignedExpression errorNode) {
addError(errorNode, "Cannot negate non number.");
}
public void subtractOrDiveColorFromNumber(Expression errorNode) {
addError(errorNode, "Can't subtract or divide a color from a number");
}
public void mathFunctionParameterNotANumberWarn(String functionName, Expression errorNode) {
addWarning(errorNode, "function '" + functionName + "' requires number as a parameter.");
}
public void cannotEvaluate(Expression errorNode) {
addError(errorNode, "Unable to evaluate expression");
}
public void incompatibleComparisonOperand(Expression errorNode, ComparisonExpressionOperator operator) {
addError(errorNode, "The operator '" + operator + "' on non-numbers is not defined. The behaviour of less.js and less4j may/does differ. Avoid its use with non-numbers or use one of `istype(@arument)` functions to protect against mismatches: `when (isnumber(@var)) and (@var "+ operator +" ...)`. The operator is located at position " + PrintUtils.toLocation(operator) +".");
}
public void rulesetWithoutSelector(RuleSet errorNode) {
addWarning(errorNode, "Ruleset without selector encountered.");
}
public void divisionByZero(NumberExpression errorNode) {
addWarning(errorNode, "Division by zero.");
}
public void warnIE8UnsafeDataUri(FunctionExpression errorNode, String filename, int fileSizeInKB, int dataUriMaxKb) {
addWarning(errorNode, "Skipped data-uri embedding of " + filename + " because its size when encoded to string ("+fileSizeInKB+"dKB) exceeds IE8-safe "+dataUriMaxKb+"dKB!");
}
public void ambiguousDefaultSet(MixinReference reference, List<ASTCssNode> possibleMixins) {
addError(reference, "Ambiguous use of `default()` found when matching reference " + reference.getFinalName() +". Matched mixins using default are located at " + printer.toNodesPositions(possibleMixins));
}
public boolean hasErrors() {
return collector.hasErrors();
}
public boolean hasWarnings() {
return collector.hasWarnings();
}
public List<Problem> getWarnings() {
return collector.getWarnings();
}
public List<Problem> getErrors() {
return collector.getErrors();
}
public void addErrors(List<Problem> errors) {
collector.addErrors(errors);
}
@Override
public void addError(ASTCssNode errorNode, String description) {
collector.addError(new CompilationError(errorNode, description));
}
public void addError(HiddenTokenAwareTree errorNode, String description) {
collector.addError(new CompilationError(new FaultyExpression(errorNode), description));
}
@Override
public void addWarning(ASTCssNode weirdNode, String description) {
collector.addWarning(new CompilationWarning(weirdNode, description));
}
public void warnUnknowAtRule(UnknownAtRule unknown) {
addWarning(unknown, "Unknown at-rule, resulting css may be incorrect. Unknown at-rules support in less.js is undocumented and may be changed in the future (altrough it is unlikely). If that happen, less4j implementation will change too.");
}
public void warnSourceMapLinkWithoutCssResultLocation(ASTCssNode less) {
addWarning(less, "Cannot link source map. Css result location is not know and could not be deduced from input less source.");
}
public void errUnknownEncodingCharsetSourceMap(ASTCssNode nodeForErrorReport, String encodingCharset) {
addError(nodeForErrorReport, "Source map link or data could not be created. Requested charset '" + encodingCharset+"' is not available.");
}
public void detachedRulesetCallWithoutParentheses(DetachedRulesetReference reference) {
addError(reference, "Detached ruleset call is missing parentheses.");
}
public void wrongDetachedRulesetLocation(DetachedRuleset detachedRuleset) {
addError(detachedRuleset, "Detached ruleset is not allowed outside of variable declaration.");
}
public void stringInterpolationNotSupported(HiddenTokenAwareTree errorNode, Expression value) {
addError(errorNode, "String interpolation does not requeted expression type. Requested expression was defined at " + printer.toPosition(value));
}
public void errorIncompatibleTypesCompared(FunctionExpression errorNode, String suffix1, String suffix2) {
addError(errorNode, "Can not compare '" + suffix1 +"' with '" + suffix2 + "'.");
}
public void wrongEnumeratedArgument(FunctionExpression errorNode, String argumentName, String... allowed) {
addError(errorNode, "Wrong '" + errorNode +"' argument to function '" + errorNode.getName() + "'. Should be one of: " + PrintUtils.toString(allowed));
}
public void errorSvgGradientArgument(FunctionExpression errorNode) {
addError(errorNode, "svg-gradient expects direction and sequence of color stops. Color stops can be supplied either as parameters: `direction, start_color [start_position], [color position,]..., end_color [end_position]` or inside a list `direction, list`");
}
public void regexpFunctionError(FunctionExpression call, String message) {
addError(call, "Regular expression failed: " + message);
}
public void unableToFinish(StyleSheet lessStyleSheet, UnableToFinish ex) {
addError(lessStyleSheet, ex.getMessage());
}
}