package com.github.sommeri.less4j.core.parser;
import java.util.ArrayList;
import java.util.Iterator;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.ElementSubsequent;
import com.github.sommeri.less4j.core.ast.Extend;
import com.github.sommeri.less4j.core.ast.MultiTargetExtend;
import com.github.sommeri.less4j.core.ast.PseudoClass;
import com.github.sommeri.less4j.core.ast.Selector;
import com.github.sommeri.less4j.core.ast.SelectorCombinator;
import com.github.sommeri.less4j.core.ast.SelectorPart;
import com.github.sommeri.less4j.core.ast.SimpleSelector;
import com.github.sommeri.less4j.core.problems.BugHappened;
public class SelectorBuilder {
private static String EXTEND_PSEUDO = "extend";
private final HiddenTokenAwareTree token;
private final ASTBuilderSwitch parent;
private SelectorCombinator leadingCombinator = null;
private SimpleSelector currentSimpleSelector = null;
private SelectorPart nextPart = null;
public SelectorBuilder(HiddenTokenAwareTree token, ASTBuilderSwitch astBuilderSwitch) {
this.token = token;
this.parent = astBuilderSwitch;
}
public Selector buildSelector() {
Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();
Selector result = new Selector(token, new ArrayList<SelectorPart>());
currentSimpleSelector = null;
while (iterator.hasNext()) {
HiddenTokenAwareTree kid = iterator.next();
if (ConversionUtils.isSelectorCombinator(kid)) {
finishPart(result);
leadingCombinator = ConversionUtils.createSelectorCombinator(kid);
} else {
ASTCssNode node = parent.switchOn(kid);
if (node instanceof SelectorPart) {
openPart(result, (SelectorPart) node);
} else {
if (currentSimpleSelector == null) {
SimpleSelector empty = new SimpleSelector(kid, null, null, true);
empty.setEmptyForm(true);
openPart(result, empty);
}
currentSimpleSelector.addSubsequent((ElementSubsequent) node);
}
}
}
finishPart(result);
return result;
}
private void openPart(Selector result, SelectorPart node) {
finishPart(result);
nextPart = node;
if (nextPart instanceof SimpleSelector) {
currentSimpleSelector = (SimpleSelector) nextPart;
}
nextPart.setLeadingCombinator(leadingCombinator);
if (leadingCombinator != null)
nextPart.getUnderlyingStructure().moveHidden(leadingCombinator.getUnderlyingStructure(), null);
leadingCombinator = null;
}
private void finishPart(Selector result) {
if (nextPart != null)
addPart(result, nextPart);
nextPart = null;
currentSimpleSelector = null;
}
private void addPart(Selector selector, SelectorPart part) {
ElementSubsequent lastSubsequent = part.getLastSubsequent();
while (lastSubsequent != null && isExtends(lastSubsequent)) {
convertAndAddExtends(selector, (PseudoClass) lastSubsequent);
part.removeSubsequent(lastSubsequent);
lastSubsequent = part.getLastSubsequent();
}
// if the part had only extend as members
if (!part.isEmpty())
selector.addPart(part);
}
private void convertAndAddExtends(Selector selector, PseudoClass extendPC) {
ASTCssNode parameter = extendPC.getParameter();
if (parameter.getType() == ASTCssNodeType.EXTEND) {
selector.addExtend((Extend) parameter);
} else if (parameter.getType() == ASTCssNodeType.MULTI_TARGET_EXTEND) {
MultiTargetExtend extend = (MultiTargetExtend) parameter;
for (Extend node : extend.getAllExtends()) {
selector.addExtend((Extend) node);
}
} else {
throw new BugHappened(ASTBuilderSwitch.GRAMMAR_MISMATCH, parameter.getUnderlyingStructure());
}
}
private boolean isExtends(ElementSubsequent subsequent) {
return (subsequent instanceof PseudoClass) && EXTEND_PSEUDO.equals(subsequent.getName());
}
}