package com.github.sommeri.less4j.core.compiler.selectors; import com.github.sommeri.less4j.core.ast.ASTCssNode; import com.github.sommeri.less4j.core.ast.CssString; import com.github.sommeri.less4j.core.ast.ElementSubsequent; import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.IdentifierExpression; import com.github.sommeri.less4j.core.ast.PseudoClass; import com.github.sommeri.less4j.core.ast.SelectorAttribute; import com.github.sommeri.less4j.core.problems.BugHappened; import com.github.sommeri.less4j.utils.ListsComparator.ListMemberComparator; public class ElementSubsequentComparator implements ListMemberComparator<ElementSubsequent> { private final SelectorsComparatorUtils utils; private final GeneralComparatorForExtend generalComparator; public ElementSubsequentComparator(GeneralComparatorForExtend generalComparator, SelectorsComparatorUtils utils) { this.utils = utils; this.generalComparator = generalComparator; } @Override public boolean equals(ElementSubsequent first, ElementSubsequent second) { if (first.getType() != second.getType()) { return false; } switch (first.getType()) { case CSS_CLASS: case ID_SELECTOR: case PSEUDO_ELEMENT: { return utils.nullSafeEquals(first.getFullName(), second.getFullName()); } case PSEUDO_CLASS: { PseudoClass pClass1 = (PseudoClass) first; PseudoClass pClass2 = (PseudoClass) second; return pseudoclassesEqual(pClass1, pClass2); } case SELECTOR_ATTRIBUTE: { SelectorAttribute attribute1 = (SelectorAttribute) first; SelectorAttribute attribute2 = (SelectorAttribute) second; return attributesEqual(attribute1, attribute2); } default: throw new BugHappened("Unexpected subsequent type: " + first.getType(), first); } } @Override public boolean prefix(ElementSubsequent lookFor, ElementSubsequent inside) { return equals(lookFor, inside); } @Override public boolean suffix(ElementSubsequent lookFor, ElementSubsequent inside) { return equals(lookFor, inside); } private boolean attributesEqual(SelectorAttribute att1, SelectorAttribute att2) { if (!utils.nullSafeEquals(att1.getFullName(), att2.getFullName())) return false; if (!utils.selectorOperatorsEquals(att1.getOperator(), att2.getOperator())) return false; Expression v1 = att1.getValue(); Expression v2 = att2.getValue(); if (v1 == null || v2 == null) return v1 == null && v2 == null; //extend keyword is "smart". It knows that [attribute=something] is the same as [attribute='something'] and the same [attribute="something"] String smart1 = toSmartAttributeValue(v1); String smart2 = toSmartAttributeValue(v2); if (smart1 != null && smart2 != null) return utils.nullSafeEquals(smart1, smart2); return generalComparator.equals(v1, v2); } private String toSmartAttributeValue(Expression value) { switch (value.getType()) { case IDENTIFIER_EXPRESSION: return ((IdentifierExpression) value).getValue(); case STRING_EXPRESSION: return ((CssString) value).getValue(); default: return null; } } private boolean pseudoclassesEqual(PseudoClass pClass1, PseudoClass pClass2) { if (!utils.nullSafeEquals(pClass1.getFullName(), pClass2.getFullName())) return false; ASTCssNode parameter1 = pClass1.getParameter(); ASTCssNode parameter2 = pClass2.getParameter(); if (parameter1 == null && parameter2 == null) return true; return generalComparator.equals(parameter1, parameter2); } @Override public boolean contains(ElementSubsequent lookFor, ElementSubsequent inside) { return equals(lookFor, inside); } }