package com.github.sommeri.less4j.core.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.antlr.runtime.CommonToken;
import org.antlr.runtime.tree.CommonTree;
import com.github.sommeri.less4j.core.parser.LessLexer;
/**
* This class is NOT thread-safe.
*
* The combined merges a list of tokens with abstract syntax tree. The merging algorithm
* assigns each token to exactly one node in abstract syntax tree.
*
*/
public class ListToTreeCombiner {
private LinkedList<CommonToken> hiddenTokens;
public void associate(HiddenTokenAwareTree ast, LinkedList<CommonToken> hiddenTokens) {
initialize(hiddenTokens);
LinkedList<HiddenTokenAwareTree> children = getChildren(ast);
associateAllchilds(children);
if (children.isEmpty()) {
addAllContainedTokens(ast);
} else {
HiddenTokenAwareTree lastChild = children.getLast();
lastChild.addFollowing(hiddenTokens);
}
}
private void initialize(LinkedList<CommonToken> hiddenTokens) {
this.hiddenTokens = hiddenTokens;
}
private void associateAsChild(HiddenTokenAwareTree ast) {
addAllPrecedingTokens(ast);
LinkedList<HiddenTokenAwareTree> children = getChildren(ast);
if (children.isEmpty()) {
addAllContainedTokens(ast);
return;
}
HiddenTokenAwareTree lastChild = associateAllchilds(children);
addFollowingTokens(lastChild, ast.getTokenStopIndex());
}
private HiddenTokenAwareTree associateAllchilds(LinkedList<HiddenTokenAwareTree> children) {
HiddenTokenAwareTree previousChild = null;
for (HiddenTokenAwareTree child : children) {
assignFirstCommentsSegment(previousChild, child);
associateAsChild(child);
previousChild = child;
}
return previousChild;
}
private void addFollowingTokens(HiddenTokenAwareTree target, int stop) {
List<CommonToken> result = readPrefix(stop);
target.addFollowing(result);
}
// private int stopIndexForLastChild(HiddenTokenAwareTree parent) {
// int result = parent.getTokenStopIndex();
// HiddenTokenAwareTree parentsSibling = parent.getNextSibling();
// if (parentsSibling!=null)
// return result;
// }
private void assignFirstCommentsSegment(HiddenTokenAwareTree firstChild, HiddenTokenAwareTree secondChild) {
if (firstChild == null)
return;
LinkedList<CommonToken> tail = readTillNewLine(secondChild.getTokenStartIndex());
if (tail.isEmpty())
return;
CommonToken lastInTail = tail.peekLast();
if (lastInTail.getType() == LessLexer.NEW_LINE)
firstChild.addFollowing(tail);
else
secondChild.addPreceding(tail);
}
//this method assumes that ast is empty
private void addAllContainedTokens(HiddenTokenAwareTree ast) {
int actualTokenIndex = ast.getTokenIndex();
List<CommonToken> preceeding = readPrefix(actualTokenIndex);
ast.addPreceding(preceeding);
int lastHiddenBehind = ast.getTokenStopIndex();
List<CommonToken> following = readPrefix(lastHiddenBehind);
ast.addFollowing(following);
}
private LinkedList<HiddenTokenAwareTree> getChildren(HiddenTokenAwareTree ast) {
List<HiddenTokenAwareTree> children = ast.getChildren();
if (children == null)
return new LinkedList<HiddenTokenAwareTree>();
LinkedList<HiddenTokenAwareTree> copy = new LinkedList<HiddenTokenAwareTree>(children);
Collections.sort(copy, new PositionComparator());
return copy;
}
private void addAllPrecedingTokens(HiddenTokenAwareTree target) {
int upTo = target.getTokenStartIndex();
List<CommonToken> tokens = readPrefix(upTo);
target.addPreceding(tokens);
}
private LinkedList<CommonToken> readTillNewLine(int end) {
LinkedList<CommonToken> result = new LinkedList<CommonToken>();
if (hiddenTokens.isEmpty())
return result;
CommonToken first = hiddenTokens.peekFirst();
while (first != null && first.getTokenIndex() < end && first.getType() == LessLexer.COMMENT) {
result.add(first);
hiddenTokens.removeFirst();
first = hiddenTokens.peekFirst();
}
if (first == null || first.getTokenIndex() >= end)
return result;
result.add(first);
return result;
}
private List<CommonToken> readPrefix(int end) {
List<CommonToken> result = new ArrayList<CommonToken>();
if (hiddenTokens.isEmpty())
return result;
CommonToken first = hiddenTokens.peekFirst();
while (first != null && first.getTokenIndex() < end) {
result.add(first);
hiddenTokens.removeFirst();
first = hiddenTokens.peekFirst();
}
return result;
}
class PositionComparator implements Comparator<CommonTree> {
@Override
public int compare(CommonTree arg0, CommonTree arg1) {
return arg0.getTokenStartIndex() - arg1.getTokenStartIndex();
}
}
}