package com.sap.furcas.runtime.parser.textblocks.observer;
import java.util.Collection;
import java.util.List;
import com.sap.furcas.metamodel.FURCAS.textblocks.AbstractToken;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.runtime.parser.textblocks.TextBlockFactory;
public class TextBlockComparer {
private final Stack<TextBlockTraversationContext> stack = new Stack<TextBlockTraversationContext>();
private final TextBlockFactory myFactory;
private static final boolean reUseTextblocks = true;
public TextBlockComparer(TextBlock root, TextBlockFactory factory) {
if (root == null || factory == null) {
throw new IllegalArgumentException(root + ", " + factory);
}
myFactory = factory;
TextBlockTraversationContext rootContext = new TextBlockTraversationContext(
root, false);
stack.push(rootContext);
}
/**
* returns existing or newly created textblock
*
* @return
*/
public TextBlock enterNextChild() {
TextBlockTraversationContext currentContext = stack.peek();
TextBlock currentBlock = currentContext.getContextBlock();
int lastIndex = currentContext.getLastVisitedChildIndex();
currentContext.setLastVisitedChildIndex(lastIndex + 1);
// TODO This is actually to optimistic, instead it should be checked if
// the tokens within
// the current current rule can reach the next block , if so it can be
// re-used
// otherwise a new textblock needs to be created
TextBlockTraversationContext newContext = getNextBlockAtIndex(
currentBlock, lastIndex + 1);
stack.push(newContext);
return newContext.getContextBlock();
}
/**
* Returns the index of the last visited child.
*
* @return
*/
public int getLastVisitedChildIndex() {
TextBlockTraversationContext currentContext = stack.peek();
return currentContext.getLastVisitedChildIndex();
}
/**
* get child at position or create new TextBlock
*
* @param currentBlock
* @param i
* @return
*/
private TextBlockTraversationContext getNextBlockAtIndex(
TextBlock currentBlock, int i) {
TextBlock result;
// TODO this is a workaround until incremental
if (!reUseTextblocks) {
result = myFactory.createBlock();
return new TextBlockTraversationContext(result, true);
}
List<TextBlock> subBlocks = currentBlock.getSubBlocks();
boolean created = false;
if (subBlocks != null && subBlocks.size() > i && i >= 0
// TODO this is a heuristic?!?!, only if the subblock was changed it is
// resonable to return it
// && subBlocks.get(i).isChildrenChanged()
//
) {
result = subBlocks.get(i);
} else {
result = myFactory.createBlock();
created = true;
}
return new TextBlockTraversationContext(result, created);
}
public void leaveChild() {
stack.pop();
if (stack.isEmpty()) {
throw new IllegalStateException("Cannot leave root context");
}
}
public TextBlock getCurrent() {
return stack.peek().getContextBlock();
}
public void addTokenToBeRelocated(AbstractToken token) {
stack.peek().addTokenToBeRelocated(token);
}
public List<AbstractToken> getTokensToBeRelocated() {
return stack.peek().getTokensToBeRelocated();
}
/**
* @param offChannelTokens
*/
public void addTokensToBeRelocated(
Collection<? extends AbstractToken> tokens) {
stack.peek().addTokensToBeRelocated(tokens);
}
/**
* @return
*/
public boolean isNewlyCreatedBlock() {
return stack.peek().isNewBlock();
}
/**
* this can be used to reset the counter one child back in order to re-enter
* the last child upon the next call to {@link #enterNextChild()}.
*/
public void resetOneChildBack() {
// //only reset if we are at least at the first child
// if(stack.peek().getLastVisitedChildIndex() > -1) {
stack.peek().setLastVisitedChildIndex(
stack.peek().getLastVisitedChildIndex() - 1);
// }
}
}