package ca.concordia.cssanalyser.parser.less; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.NotImplementedException; import org.slf4j.Logger; import com.github.sommeri.less4j.LessSource; import com.github.sommeri.less4j.LessSource.FileSource; import com.github.sommeri.less4j.LessSource.URLSource; import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.AnonymousExpression; import com.github.sommeri.less4j.core.ast.BinaryExpression; import com.github.sommeri.less4j.core.ast.ColorExpression; import com.github.sommeri.less4j.core.ast.ColorExpression.ColorWithAlphaExpression; import com.github.sommeri.less4j.core.ast.CssClass; 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.FixedMediaExpression; import com.github.sommeri.less4j.core.ast.FunctionExpression; import com.github.sommeri.less4j.core.ast.IdSelector; import com.github.sommeri.less4j.core.ast.IdentifierExpression; import com.github.sommeri.less4j.core.ast.InterpolableName; import com.github.sommeri.less4j.core.ast.InterpolatedMediaExpression; import com.github.sommeri.less4j.core.ast.ListExpression; import com.github.sommeri.less4j.core.ast.ListExpressionOperator.Operator; import com.github.sommeri.less4j.core.ast.MediaExpression; import com.github.sommeri.less4j.core.ast.NamedColorExpression; import com.github.sommeri.less4j.core.ast.NamedColorWithAlphaExpression; import com.github.sommeri.less4j.core.ast.NamedExpression; import com.github.sommeri.less4j.core.ast.NestedSelectorAppender; import com.github.sommeri.less4j.core.ast.Nth; import com.github.sommeri.less4j.core.ast.NumberExpression; import com.github.sommeri.less4j.core.ast.RuleSet; import com.github.sommeri.less4j.core.ast.SelectorAttribute; import com.github.sommeri.less4j.core.ast.SelectorPart; import ca.concordia.cssanalyser.app.FileLogger; import ca.concordia.cssanalyser.cssmodel.LocationInfo; import ca.concordia.cssanalyser.cssmodel.StyleSheet; import ca.concordia.cssanalyser.cssmodel.declaration.Declaration; import ca.concordia.cssanalyser.cssmodel.declaration.DeclarationFactory; import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValue; import ca.concordia.cssanalyser.cssmodel.declaration.value.DeclarationValueFactory; import ca.concordia.cssanalyser.cssmodel.declaration.value.ValueType; import ca.concordia.cssanalyser.cssmodel.media.MediaFeatureExpression; import ca.concordia.cssanalyser.cssmodel.media.MediaQuery; import ca.concordia.cssanalyser.cssmodel.media.MediaQuery.MediaQueryPrefix; import ca.concordia.cssanalyser.cssmodel.media.MediaQueryList; import ca.concordia.cssanalyser.cssmodel.selectors.AdjacentSiblingSelector; import ca.concordia.cssanalyser.cssmodel.selectors.BaseSelector; import ca.concordia.cssanalyser.cssmodel.selectors.ChildSelector; import ca.concordia.cssanalyser.cssmodel.selectors.DescendantSelector; import ca.concordia.cssanalyser.cssmodel.selectors.GroupingSelector; import ca.concordia.cssanalyser.cssmodel.selectors.NegationPseudoClass; import ca.concordia.cssanalyser.cssmodel.selectors.PseudoClass; import ca.concordia.cssanalyser.cssmodel.selectors.PseudoElement; import ca.concordia.cssanalyser.cssmodel.selectors.Selector; import ca.concordia.cssanalyser.cssmodel.selectors.SiblingSelector; import ca.concordia.cssanalyser.cssmodel.selectors.SimpleSelector; import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorCondition; import ca.concordia.cssanalyser.cssmodel.selectors.conditions.SelectorConditionType; import ca.concordia.cssanalyser.migration.topreprocessors.less.LessPreprocessorNodeFinder; import ca.concordia.cssanalyser.parser.ParseException; /** * Adapts a Less StyleSheet object to a CSSAnalyser StyleSheet object * @author Davood Mazinanian * */ public class LessStyleSheetAdapter { private static final String IMPORTANT = "!important"; private static Logger LOGGER = FileLogger.getLogger(LessStyleSheetAdapter.class); private final ASTCssNode lessStyleSheet; public LessStyleSheetAdapter(ASTCssNode lessStyleSheet) { this.lessStyleSheet = lessStyleSheet; } private void adapt(StyleSheet ourStyleSheet) { List<? extends ASTCssNode> nodes = lessStyleSheet.getChilds(); addSelectorsToStyleSheetFromLessASTNodes(ourStyleSheet, nodes); } private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes) { addSelectorsToStyleSheetFromLessASTNodes(styleSheet, nodes, null); } private void addSelectorsToStyleSheetFromLessASTNodes(StyleSheet styleSheet, List<? extends ASTCssNode> nodes, MediaQueryList mediaQueries) { for (ASTCssNode node : nodes) { if (node instanceof RuleSet) { RuleSet ruleSetNode = (RuleSet)node; Selector selector = getSelectorFromLessRuleSet(ruleSetNode); if (mediaQueries != null) selector.addMediaQueryList(mediaQueries); styleSheet.addSelector(selector); } else if (node instanceof com.github.sommeri.less4j.core.ast.Media) { com.github.sommeri.less4j.core.ast.Media lessMedia = (com.github.sommeri.less4j.core.ast.Media)node; MediaQueryList mediaQueryList = getMediaQueryListFromLessMedia(lessMedia); addSelectorsToStyleSheetFromLessASTNodes(styleSheet, lessMedia.getBody().getMembers(), mediaQueryList); } } } private MediaQueryList getMediaQueryListFromLessMedia(com.github.sommeri.less4j.core.ast.Media lessMedia) { // x and y, z and t is a list of media queries containing two media queries MediaQueryList mediaQueryList = new MediaQueryList(); for (com.github.sommeri.less4j.core.ast.MediaQuery lessMediaQuery : lessMedia.getMediums()) { MediaQueryPrefix prefix = null; String mediumType = ""; if (lessMediaQuery.getMedium() != null) { if (lessMediaQuery.getMedium().getModifier() != null) { switch (lessMediaQuery.getMedium().getModifier().getModifier()) { case NOT: prefix = MediaQueryPrefix.NOT; break; case ONLY: prefix = MediaQueryPrefix.ONLY; break; case NONE: default: prefix = null; break; } } if (lessMediaQuery.getMedium().getMediumType() != null) mediumType = lessMediaQuery.getMedium().getMediumType().getName(); } MediaQuery query = new MediaQuery(prefix, mediumType); for (MediaExpression lessMediaExpression : lessMediaQuery.getExpressions()) { try { String feature = ""; String expression = ""; if (lessMediaExpression instanceof FixedMediaExpression) { FixedMediaExpression fixedMediaExpression = (FixedMediaExpression)lessMediaExpression; if (fixedMediaExpression.getExpression() != null) { // Lets re-use a method that we already have, in an ugly manner List<DeclarationValue> values = getListOfDeclarationValuesFromLessExpression("fake", fixedMediaExpression.getExpression()); for (DeclarationValue value : values) expression += value; } feature = fixedMediaExpression.getFeature().getFeature(); } else if (lessMediaExpression instanceof InterpolatedMediaExpression) { throw new RuntimeException("What is " + lessMediaExpression); } MediaFeatureExpression featureExpression = new MediaFeatureExpression(feature, expression); featureExpression.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaExpression)); query.addMediaFeatureExpression(featureExpression); } catch (ParseException ex) { LOGGER.warn(String.format("Ignored media expression %s", lessMediaExpression.toString())); } } query.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMediaQuery)); mediaQueryList.addMediaQuery(query); } mediaQueryList.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(lessMedia)); return mediaQueryList; } public Selector getSelectorFromLessRuleSet(RuleSet ruleSetNode) { Selector selector = null; if (ruleSetNode.getSelectors().size() == 1) { // One selector, this is a base selector selector = getBaseSelectorFromLessSelector(ruleSetNode.getSelectors().get(0)); } else { // More than 1 selector, this is a grouping selector GroupingSelector grouping = new GroupingSelector(); for (com.github.sommeri.less4j.core.ast.Selector lessSelector : ruleSetNode.getSelectors()) { BaseSelector baseSelectorFromLessSelector = getBaseSelectorFromLessSelector(lessSelector); grouping.add(baseSelectorFromLessSelector); baseSelectorFromLessSelector.setParentGroupSelector(grouping); } selector = grouping; } selector.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(ruleSetNode)); // Handle declarations addDeclarationsToSelectorFromLessRuleSetNode(ruleSetNode, selector); return selector; } private void addDeclarationsToSelectorFromLessRuleSetNode(RuleSet ruleSetNode, Selector selector) { for (ASTCssNode declarationNode : ruleSetNode.getBody().getDeclarations()) { Declaration declaration = getDeclarationFromLessDeclaration(declarationNode); if (declaration != null) selector.addDeclaration(declaration); } } public Declaration getDeclarationFromLessDeclaration(ASTCssNode declarationNode) { Declaration declaration = null; if (declarationNode instanceof com.github.sommeri.less4j.core.ast.Declaration) { try { com.github.sommeri.less4j.core.ast.Declaration lessDeclaration = (com.github.sommeri.less4j.core.ast.Declaration)declarationNode; String property = lessDeclaration.getNameAsString(); List<DeclarationValue> values; if (lessDeclaration.getExpression() != null) { values = getListOfDeclarationValuesFromLessExpression(property, lessDeclaration.getExpression()); } else { // If a declaration does not have a value, happened in some cases values = new ArrayList<>(); values.add(new DeclarationValue("", ValueType.OTHER)); } boolean isImportant = false; DeclarationValue declarationValue = values.get(values.size() - 1); if (declarationValue.getValue().endsWith(IMPORTANT)) { isImportant = true; if (declarationValue.getValue().equals(IMPORTANT)) { values.remove(values.size() - 1); } else { String value = declarationValue.getValue().replace("!important", ""); values.set(values.size() - 1, DeclarationValueFactory.getDeclarationValue(property, value, declarationValue.getType())); } } if (values.size() == 0) { LOGGER.warn(String.format("No CSS values could be found for property %s at line %s, column %s", property, lessDeclaration.getSourceLine(), lessDeclaration.getSourceColumn())); } else { declaration = DeclarationFactory.getDeclaration( property, values, null, isImportant, true, LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(declarationNode)); } } catch (Exception ex) { LOGGER.warn("Could not read " + declarationNode + "; " + ex); } } else { throw new RuntimeException("What is that?" + declarationNode); } return declaration; } private List<DeclarationValue> getListOfDeclarationValuesFromLessExpression(String property, Expression expression) throws ParseException { List<DeclarationValue> values = new ArrayList<>(); if (expression instanceof ListExpression) { ListExpression listExpression = (ListExpression)expression; for (Iterator<Expression> iterator = listExpression.getExpressions().iterator(); iterator.hasNext();) { Expression expr = iterator.next(); List<DeclarationValue> vals = getListOfDeclarationValuesFromLessExpression(property, expr); values.addAll(vals); if (listExpression.getOperator() != null) { if (listExpression.getOperator().getOperator() == Operator.COMMA) { if (iterator.hasNext()) { DeclarationValue value = DeclarationValueFactory.getDeclarationValue(property, ",", ValueType.SEPARATOR); value.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(listExpression.getOperator())); values.add(value); } } else if (listExpression.getOperator().getOperator() == Operator.EMPTY_OPERATOR) { // Do nothing } else { throw new RuntimeException("Operator = " + listExpression.getOperator()); } } else { throw new RuntimeException("Operator = " + listExpression.getOperator()); } } } else if (expression instanceof BinaryExpression) { BinaryExpression binary = (BinaryExpression)expression; values.addAll(getListOfDeclarationValuesFromLessExpression(property, binary.getLeft())); // Operator DeclarationValue operator = DeclarationValueFactory.getDeclarationValue(property, binary.getOperator().toString(), ValueType.OPERATOR); operator.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(binary.getOperator())); values.add(operator); values.addAll(getListOfDeclarationValuesFromLessExpression(property, binary.getRight())); } else if (expression instanceof NamedExpression) { NamedExpression namedExpression = (NamedExpression) expression; values.add(DeclarationValueFactory.getDeclarationValue(property, namedExpression.getName(), ValueType.IDENT)); values.add(DeclarationValueFactory.getDeclarationValue(property, "=", ValueType.OPERATOR)); values.addAll(getListOfDeclarationValuesFromLessExpression(property, namedExpression.getExpression())); } else { values.add(getSingleValueFromLessValueExpression(property, expression)); } return values; } private DeclarationValue getSingleValueFromLessValueExpression(String property, Expression expression) throws ParseException { DeclarationValue value = null; if (expression instanceof NumberExpression) { NumberExpression numberExpression = (NumberExpression)expression; value = getDeclarationValueFromLessNumberExpression(property, numberExpression); } else if (expression instanceof NamedColorExpression) { NamedColorExpression namedExpression = (NamedColorExpression)expression; value = DeclarationValueFactory.getDeclarationValue(property, namedExpression.getColorName(), ValueType.COLOR); } else if (expression instanceof ColorWithAlphaExpression) { if (expression instanceof NamedColorWithAlphaExpression) { NamedColorWithAlphaExpression namedColorWithAlphaExpression = (NamedColorWithAlphaExpression) expression; value = DeclarationValueFactory.getDeclarationValue(property, namedColorWithAlphaExpression.getColorName(), ValueType.IDENT); } else { ColorWithAlphaExpression colorWithAlpha = (ColorWithAlphaExpression)expression; value = DeclarationValueFactory.getDeclarationValue(property, colorWithAlpha.toString(), ValueType.COLOR); } } else if (expression instanceof IdentifierExpression) { IdentifierExpression identifier = (IdentifierExpression)expression; String valueString = identifier.getValue(); if (valueString == null) valueString = ""; value = DeclarationValueFactory.getDeclarationValue(property, valueString, ValueType.IDENT); } else if (expression instanceof ColorExpression) { ColorExpression colorExpression = (ColorExpression) expression; value = DeclarationValueFactory.getDeclarationValue(property, colorExpression.getValue(), ValueType.COLOR); } else if (expression instanceof FunctionExpression) { FunctionExpression function = (FunctionExpression)expression; String functionName = function.getName(); if ("rgb".equals(functionName) || "hsl".equals(functionName) || "rgba".equals(functionName) || "hsla".equals(functionName)) { String functionString = getFunctionStringFromLessFunctionExpression(property, function); value = DeclarationValueFactory.getDeclarationValue(property, functionString, ValueType.COLOR); } else if (functionName.equals("url")) { if (function.getParameter().getChilds().get(1) instanceof CssString) { String url = "url('" + ((CssString)function.getParameter().getChilds().get(1)).getValue() + "')"; value = DeclarationValueFactory.getDeclarationValue(property, url, ValueType.URL); } else { throw new RuntimeException("What is that?" + expression); } } else { String functionString = getFunctionStringFromLessFunctionExpression(property, function); value = DeclarationValueFactory.getDeclarationValue(property, functionString, ValueType.FUNCTION); } } else if (expression instanceof CssString) { value = DeclarationValueFactory.getDeclarationValue(property, "'" + ((CssString)expression).getValue() + "'", ValueType.STRING); //} else if (expression instanceof Variable) { // value = DeclarationValueFactory.getDeclarationValue(property, expression.toString(), ValueType.OTHER); } else if (expression instanceof AnonymousExpression) { AnonymousExpression anonymousExpression = (AnonymousExpression) expression; value = DeclarationValueFactory.getDeclarationValue(property, anonymousExpression.getValue(), ValueType.OTHER); } else if (expression instanceof EscapedValue) { EscapedValue escapedValue = (EscapedValue) expression; value = DeclarationValueFactory.getDeclarationValue(property, escapedValue.getValue(), ValueType.OTHER); } else { throw new RuntimeException("What is that?" + expression); } value.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(expression)); return value; } private DeclarationValue getDeclarationValueFromLessNumberExpression(String property, NumberExpression numberExpression) throws ParseException { DeclarationValue value = null; String originalString = numberExpression.getOriginalString(); switch(numberExpression.getDimension()) { case ANGLE: value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.ANGLE); case EMS: case EXS: case LENGTH: value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.LENGTH); break; case FREQ: value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.FREQUENCY); break; case NUMBER: if (originalString.indexOf(".") > -1) value = DeclarationValueFactory.getDeclarationValue(property, DeclarationValueFactory.formatDouble(numberExpression.getValueAsDouble()), ValueType.REAL); else value = DeclarationValueFactory.getDeclarationValue(property, DeclarationValueFactory.formatDouble(numberExpression.getValueAsDouble()), ValueType.INTEGER); break; case PERCENTAGE: value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.PERCENTAGE); break; case REPEATER: throw new RuntimeException("What is " + property + ":" + originalString); case TIME: value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.TIME); break; case UNKNOWN: String suffix = numberExpression.getSuffix().toLowerCase(); if ("turn".equals(suffix)) { value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.ANGLE); } else if ("rem".equals(suffix) || "vw".equals(suffix) || "vh".equals(suffix) || "vmin".equals(suffix)) { value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.PERCENTAGE); } else { value = DeclarationValueFactory.getDeclarationValue(property, originalString, ValueType.OTHER); // throw new ParseException("What is " + property + ":" + originalString); } default: break; } value.setLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(numberExpression)); return value; } private String getFunctionStringFromLessFunctionExpression(String property, FunctionExpression function) throws ParseException { String functionName = function.getName(); StringBuilder functionString = new StringBuilder(functionName); functionString.append("("); Expression parameter = function.getParameter(); List<DeclarationValue> values = getListOfDeclarationValuesFromLessExpression(property, parameter); for (Iterator<DeclarationValue> iterator = values.iterator(); iterator.hasNext(); ) { DeclarationValue value = iterator.next(); if ("".equals(value.getValue())) throw new ParseException(String.format("Could not parse one of the parameters for function %s at <%s:%s>", functionName, function.getSourceLine(), function.getSourceColumn())); if (value.getType() != ValueType.SEPARATOR && !functionString.toString().endsWith("(")) { functionString.append(" "); } functionString.append(value.getValue()); } functionString.append(")"); return functionString.toString(); } /** * Converts a Less4j Selector which only has one child to a BaseSelector. * A Less4j Selector might be a combinator or a simple selector. * @param lessSelector * @return */ private BaseSelector getBaseSelectorFromLessSelector(com.github.sommeri.less4j.core.ast.Selector lessSelector) { BaseSelector toReturn = null; // In the Less selector, every part is a simple selector if (lessSelector.getParts().size() == 1) { // simple selector, not a combinator toReturn = getSimpleSelectorFromLessSelectorPart(lessSelector.getParts().get(0)); } else { // combinator toReturn = getCombinatorFromLessSelectorParts(lessSelector.getParts()); } return toReturn; } private BaseSelector getCombinatorFromLessSelectorParts(List<SelectorPart> parts) { if (parts.size() == 0) return null; // Don't touch the original list List<SelectorPart> partsCopy = new ArrayList<>(parts); // Get the right most selector. It is always a SimpleSelector SelectorPart lastPart = partsCopy.get(partsCopy.size() - 1); SimpleSelector rightHandSelector = getSimpleSelectorFromLessSelectorPart(lastPart); partsCopy.remove(partsCopy.size() - 1); if (partsCopy.size() >= 1) { if (partsCopy.get(partsCopy.size() - 1) instanceof NestedSelectorAppender) { rightHandSelector.setSelectedElementName("&"); partsCopy.remove(partsCopy.size() - 1); } } BaseSelector baseSelectorToReturn = rightHandSelector; if (partsCopy.size() != 0) { BaseSelector leftHandSelector = getCombinatorFromLessSelectorParts(partsCopy); if (lastPart.getLeadingCombinator() != null) { switch (lastPart.getLeadingCombinator().getCombinatorType()) { //ADJACENT_SIBLING("+"), CHILD(">"), DESCENDANT("' '"), GENERAL_SIBLING("~"), HAT("^"), CAT("^^"); case ADJACENT_SIBLING: baseSelectorToReturn = new AdjacentSiblingSelector(leftHandSelector, rightHandSelector); break; case CHILD: baseSelectorToReturn = new ChildSelector(leftHandSelector, rightHandSelector); break; case DESCENDANT: baseSelectorToReturn = new DescendantSelector(leftHandSelector, rightHandSelector); break; case GENERAL_SIBLING: baseSelectorToReturn = new SiblingSelector(leftHandSelector, rightHandSelector); break; case NAMED: case HAT: case CAT: // Not supported return null; default: break; } } else { baseSelectorToReturn = new AdjacentSiblingSelector(leftHandSelector, rightHandSelector); } int lineNumber = leftHandSelector.getSelectorNameLocationInfo().getLineNumber(); int colNumber = leftHandSelector.getSelectorNameLocationInfo().getColumnNumber(); int offset = leftHandSelector.getSelectorNameLocationInfo().getOffset(); int length = rightHandSelector.getSelectorNameLocationInfo().getOffset() + rightHandSelector.getSelectorNameLocationInfo().getLength() - offset; LocationInfo selectorNameLocationInfo = new LocationInfo(lineNumber, colNumber, offset, length); baseSelectorToReturn.setSelectorNameLocationInfo(selectorNameLocationInfo); } return baseSelectorToReturn; } private SimpleSelector getSimpleSelectorFromLessSelectorPart(SelectorPart selectorPart) { SimpleSelector simpleSelector = new SimpleSelector(); if (selectorPart instanceof NestedSelectorAppender) { simpleSelector.setSelectedElementName("&"); } else { for (ASTCssNode cssASTNode : selectorPart.getChilds()) { if (cssASTNode instanceof InterpolableName) { InterpolableName name = (InterpolableName)cssASTNode; simpleSelector.setSelectedElementName(name.getName()); } else if (cssASTNode instanceof IdSelector) { IdSelector id = (IdSelector)cssASTNode; simpleSelector.setElementID(id.getName()); } else if (cssASTNode instanceof CssClass) { CssClass className = (CssClass)cssASTNode; simpleSelector.addClassName(className.getName()); } else if (cssASTNode instanceof SelectorAttribute) { SelectorAttribute attribute = (SelectorAttribute)cssASTNode; SelectorCondition condition = new SelectorCondition(attribute.getName()); SelectorConditionType adaptedConditionType = null; switch (attribute.getOperator().getOperator()) { case NONE: adaptedConditionType = SelectorConditionType.HAS_ATTRIBUTE; break; case EQUALS: adaptedConditionType = SelectorConditionType.VALUE_EQUALS_EXACTLY; break; case INCLUDES: adaptedConditionType = SelectorConditionType.VALUE_CONTAINS_WORD_SPACE_SEPARATED; break; case PREFIXMATCH: adaptedConditionType = SelectorConditionType.VALUE_STARTS_WITH; break; case SPECIAL_PREFIX: adaptedConditionType = SelectorConditionType.VALUE_START_WITH_DASH_SEPARATED; break; case SUBSTRINGMATCH: adaptedConditionType = SelectorConditionType.VALUE_CONTAINS; break; case SUFFIXMATCH: adaptedConditionType = SelectorConditionType.VALUE_ENDS_WITH; } condition.setConditionType(adaptedConditionType); if (adaptedConditionType != SelectorConditionType.HAS_ATTRIBUTE) condition.setValue(attribute.getValue().toString()); simpleSelector.addCondition(condition); } else if (cssASTNode instanceof com.github.sommeri.less4j.core.ast.PseudoClass) { com.github.sommeri.less4j.core.ast.PseudoClass pseudoClass = (com.github.sommeri.less4j.core.ast.PseudoClass) cssASTNode; PseudoClass adaptedPseudoClass = null; if ("not".equals(pseudoClass.getName().toLowerCase())) { com.github.sommeri.less4j.core.ast.Selector parameter = (com.github.sommeri.less4j.core.ast.Selector)pseudoClass.getParameter(); adaptedPseudoClass = new NegationPseudoClass(getBaseSelectorFromLessSelector(parameter)); } else { adaptedPseudoClass = new PseudoClass(pseudoClass.getName()); if (pseudoClass.hasParameters()) { if (pseudoClass.getParameter() instanceof Nth) { adaptedPseudoClass.setValue(NthToString((Nth)pseudoClass.getParameter())); } else if ("lang".equals(pseudoClass.getName().toLowerCase())) { adaptedPseudoClass.setValue(pseudoClass.getParameter().toString()); } else { throw new NotImplementedException("Unhandled parameter for pseudo class :" + pseudoClass.getParameter()); } } } simpleSelector.addPseudoClass(adaptedPseudoClass); } else if (cssASTNode instanceof com.github.sommeri.less4j.core.ast.PseudoElement) { com.github.sommeri.less4j.core.ast.PseudoElement pseudoElement = (com.github.sommeri.less4j.core.ast.PseudoElement) cssASTNode; PseudoElement adaptedPseudoElement = new PseudoElement(pseudoElement.getName()); simpleSelector.addPseudoElement(adaptedPseudoElement); } } } simpleSelector.setSelectorNameLocationInfo(LessPreprocessorNodeFinder.getLocationInfoForLessASTCssNode(selectorPart)); return simpleSelector; } private String NthToString(Nth parameter) { String toReturn = ""; switch (parameter.getForm()) { case EVEN: toReturn = "even"; break; case ODD: toReturn = "odd"; case STANDARD: if (parameter.getRepeater() != null) toReturn = parameter.getRepeater().toString(); if (parameter.getMod() != null) toReturn += parameter.getMod().toString(); break; } return toReturn; } public StyleSheet getAdaptedStyleSheet() { StyleSheet ourStyleSheet = new StyleSheet(); LessSource source = lessStyleSheet.getSource(); if ((source instanceof FileSource || source instanceof URLSource) && source.getURI() != null) { ourStyleSheet.setPath(source.getURI().toString()); } else if (source instanceof ModifiedLessFileSource) { ourStyleSheet.setPath(((ModifiedLessFileSource) source).getInputFile().getAbsolutePath()); } adapt(ourStyleSheet); return ourStyleSheet; } public ASTCssNode getLessStyleSheet() { return lessStyleSheet; } }