package com.github.sommeri.less4j.core.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.github.sommeri.less4j.core.ast.annotations.NotAstProperty;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.utils.ArraysUtils;
public class Selector extends ASTCssNode implements Cloneable {
private List<SelectorPart> combinedParts = new ArrayList<SelectorPart>();
private List<Extend> extend = new ArrayList<Extend>();
public Selector(HiddenTokenAwareTree token) {
super(token);
}
public Selector(HiddenTokenAwareTree token, SelectorPart head) {
this(token, ArraysUtils.asModifiableList(head));
}
public Selector(HiddenTokenAwareTree token, List<SelectorPart> combinedParts) {
super(token);
this.combinedParts = combinedParts;
}
public List<SelectorPart> getParts() {
return combinedParts;
}
public void addPart(SelectorPart part) {
combinedParts.add(part);
}
public void addParts(List<SelectorPart> parts) {
combinedParts.addAll(parts);
}
public void removeHead() {
combinedParts.remove(0);
}
public boolean isExtending() {
return !extend.isEmpty();
}
public List<Extend> getExtend() {
return extend;
}
public void setExtend(List<Extend> extend) {
this.extend = extend;
}
@NotAstProperty
public SelectorPart getHead() {
List<SelectorPart> parts = getParts();
if (parts.isEmpty())
return null;
return parts.get(0);
}
@Override
@NotAstProperty
public List<? extends ASTCssNode> getChilds() {
ArrayList<ASTCssNode> result = new ArrayList<ASTCssNode>(combinedParts.size() + extend.size());
result.addAll(combinedParts);
result.addAll(extend);
return result;
}
public ASTCssNodeType getType() {
return ASTCssNodeType.SELECTOR;
}
@Override
public Selector clone() {
Selector clone = (Selector) super.clone();
clone.combinedParts = ArraysUtils.deeplyClonedList(combinedParts);
clone.extend = ArraysUtils.deeplyClonedList(extend);
clone.configureParentToAllChilds();
return clone;
}
@NotAstProperty
public SelectorPart getLastPart() {
return ArraysUtils.last(getParts());
}
public boolean isCombined() {
return combinedParts.size() > 1;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Selector [");
builder.append(combinedParts);
builder.append("]");
return builder.toString();
}
public NestedSelectorAppender findFirstAppender() {
for (SelectorPart part : getParts()) {
if (part.isAppender())
return (NestedSelectorAppender) part;
}
return null;
}
public boolean containsAppender() {
return findFirstAppender() != null;
}
public boolean isReusableSelector() {
Iterator<SelectorPart> parts = getParts().iterator();
if (!parts.hasNext())
return false;
// skip initial appenders
SelectorPart current = parts.next();
while (current.isAppender() && parts.hasNext()) {
current = parts.next();
}
// find out whether there is something not reusable
while (current.isClassesAndIdsOnlySelector() && parts.hasNext()) {
current = parts.next();
}
return current.isClassesAndIdsOnlySelector();
}
/**
* Assumes that hasReusableHead returns true
*
* @return
*/
public ReusableStructureName toReusableStructureName() {
List<ElementSubsequent> nameParts = extractReusableNameParts();
ReusableStructureName result = new ReusableStructureName(nameParts.get(0).getUnderlyingStructure(), nameParts);
return result;
}
// We are loosing a lot of information during the extraction. It is ok,
// because less.js is not using combinators and does not distinguish
// between .aaa.bbb and .aaa .bbb
private List<ElementSubsequent> extractReusableNameParts() {
List<ElementSubsequent> result = new ArrayList<ElementSubsequent>();
for (SelectorPart part : getParts()) {
if (!part.isAppender()) {
result.addAll(((SimpleSelector) part).getSubsequent());
}
}
return result;
}
public boolean hasLeadingCombinator() {
if (getHead()==null)
return false;
return getHead().hasLeadingCombinator();
}
public boolean isEmpty() {
return getParts().isEmpty();
}
public void addExtend(Extend extend) {
this.extend.add(extend);
}
public void addExtends(Collection<Extend> newExtends) {
this.extend.addAll(newExtends);
}
public void setParts(List<SelectorPart> newParts) {
this.combinedParts = newParts;
}
}