/*
* Copyright 2015 Nokia Solutions and Networks
* Licensed under the Apache License, Version 2.0,
* see license.txt file for details.
*/
package org.rf.ide.core.testdata.mapping;
import java.util.List;
import java.util.Stack;
import org.rf.ide.core.testdata.mapping.table.ElementPositionResolver;
import org.rf.ide.core.testdata.mapping.table.ElementPositionResolver.PositionExpected;
import org.rf.ide.core.testdata.mapping.table.ParsingStateHelper;
import org.rf.ide.core.testdata.model.RobotFile;
import org.rf.ide.core.testdata.text.read.IRobotLineElement;
import org.rf.ide.core.testdata.text.read.IRobotTokenType;
import org.rf.ide.core.testdata.text.read.ParsingState;
import org.rf.ide.core.testdata.text.read.RobotLine;
import org.rf.ide.core.testdata.text.read.recognizer.RobotToken;
import org.rf.ide.core.testdata.text.read.recognizer.RobotTokenType;
import org.rf.ide.core.testdata.text.read.separators.Separator.SeparatorType;
import com.google.common.annotations.VisibleForTesting;
@SuppressWarnings("PMD.GodClass")
public class PreviousLineHandler {
private final ElementPositionResolver posResolver;
private final ParsingStateHelper stateHelper;
public PreviousLineHandler() {
this.posResolver = new ElementPositionResolver();
this.stateHelper = new ParsingStateHelper();
}
private final Stack<ParsingState> storedStack = new Stack<>();
public LineContinueType computeLineContinue(final Stack<ParsingState> parsingStates, boolean isNewLine,
final RobotFile model, final RobotLine currentLine, final RobotToken currentToken) {
LineContinueType continueType = LineContinueType.NONE;
if (isPreviousLineContinueToken(currentLine, currentToken) || isCommentContinue(currentToken, storedStack)) {
ParsingState currentState = stateHelper.getCurrentStatus(parsingStates);
if (currentState == ParsingState.SETTING_TABLE_INSIDE) {
if (isNewLine && containsAnySetting(model) && isSomethingToContinue(model)) {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_NEWLINE_FOR_SETTING_TABLE, model,
currentLine, currentToken)) {
continueType = LineContinueType.SETTING_TABLE_ELEMENT;
}
}
} else if (currentState == ParsingState.VARIABLE_TABLE_INSIDE) {
if (isNewLine && containsAnyVariable(model) && isSomethingToContinue(model)) {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_NEWLINE_FOR_VARIABLE_TABLE, model,
currentLine, currentToken)) {
continueType = LineContinueType.VARIABLE_TABLE_ELEMENT;
}
}
} else if (currentState == ParsingState.TEST_CASE_TABLE_INSIDE
|| currentState == ParsingState.TEST_CASE_DECLARATION) {
if (isNewLine) {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_NEWLINE_FOR_TESTCASE_TABLE, model,
currentLine, currentToken)) {
ParsingState state = stateHelper.getCurrentStatus(storedStack);
if (state == ParsingState.TEST_CASE_TABLE_HEADER) {
storedStack.remove(ParsingState.TEST_CASE_TABLE_HEADER);
storedStack.push(ParsingState.TEST_CASE_TABLE_INSIDE);
}
continueType = LineContinueType.TEST_CASE_TABLE_ELEMENT;
}
} else {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_INLINED_FOR_TESTCASE_TABLE, model,
currentLine, currentToken)) {
continueType = LineContinueType.LINE_CONTINUE_INLINED;
}
}
} else if (currentState == ParsingState.KEYWORD_TABLE_INSIDE
|| currentState == ParsingState.KEYWORD_DECLARATION) {
if (isNewLine) {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_NEWLINE_FOR_KEYWORD_TABLE, model,
currentLine, currentToken)) {
ParsingState state = stateHelper.getCurrentStatus(storedStack);
if (state == ParsingState.KEYWORD_TABLE_HEADER) {
storedStack.remove(ParsingState.KEYWORD_TABLE_HEADER);
storedStack.push(ParsingState.KEYWORD_TABLE_INSIDE);
}
continueType = LineContinueType.KEYWORD_TABLE_ELEMENT;
}
} else {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_INLINED_FOR_KEYWORD_TABLE, model,
currentLine, currentToken)) {
continueType = LineContinueType.LINE_CONTINUE_INLINED;
}
}
} else if (currentState == ParsingState.TEST_CASE_INSIDE_ACTION
|| currentState == ParsingState.KEYWORD_INSIDE_ACTION) {
if (posResolver.isCorrectPosition(PositionExpected.LINE_CONTINUE_INLINED_IN_FOR_LOOP, model,
currentLine, currentToken)) {
continueType = LineContinueType.LINE_CONTINUE_INLINED;
}
}
}
return continueType;
}
@VisibleForTesting
protected boolean isPreviousLineContinueToken(final RobotLine currentLine, final RobotToken currentToken) {
boolean result = false;
if (currentToken.getTypes().size() == 1
&& currentToken.getTypes().contains(RobotTokenType.PREVIOUS_LINE_CONTINUE)) {
result = true;
} else {
result = currentToken.getText().matches("^( )?[.]{3}$");
if (result && !currentToken.getTypes().contains(RobotTokenType.PREVIOUS_LINE_CONTINUE)) {
currentToken.getTypes().add(RobotTokenType.PREVIOUS_LINE_CONTINUE);
}
}
return result;
}
@VisibleForTesting
protected boolean isCommentContinue(RobotToken currentToken, Stack<ParsingState> storedStack) {
boolean result = false;
if (currentToken.getTypes().contains(RobotTokenType.START_HASH_COMMENT)) {
if (!storedStack.isEmpty()) {
result = storedStack.get(storedStack.size() - 1) == ParsingState.COMMENT;
}
}
return result;
}
public boolean isSomethingToContinue(final RobotFile model) {
boolean result = false;
List<RobotLine> fileContent = model.getFileContent();
boolean notFoundYet = true;
for (int i = fileContent.size() - 1; i > 0 && notFoundYet; i--) {
RobotLine robotLine = fileContent.get(i);
List<IRobotLineElement> lineElements = robotLine.getLineElements();
if (!lineElements.isEmpty()) {
for (int k = 0; k < lineElements.size() && k < 2; k++) {
IRobotLineElement elem = lineElements.get(k);
List<IRobotTokenType> types = elem.getTypes();
if (types.contains(RobotTokenType.KEYWORDS_TABLE_HEADER)
|| types.contains(RobotTokenType.SETTINGS_TABLE_HEADER)
|| types.contains(RobotTokenType.VARIABLES_TABLE_HEADER)
|| types.contains(RobotTokenType.TEST_CASES_TABLE_HEADER)) {
result = false;
notFoundYet = false;
break;
} else if (types.contains(SeparatorType.PIPE)
|| types.contains(SeparatorType.TABULATOR_OR_DOUBLE_SPACE)
|| types.contains(RobotTokenType.PREVIOUS_LINE_CONTINUE)
|| types.contains(RobotTokenType.PRETTY_ALIGN_SPACE)) {
continue;
} else {
result = true;
notFoundYet = false;
break;
}
}
}
}
return result;
}
@VisibleForTesting
protected boolean containsAnySetting(final RobotFile file) {
return !file.getSettingTable().isEmpty();
}
@VisibleForTesting
protected boolean containsAnyVariable(final RobotFile file) {
return !file.getVariableTable().getVariables().isEmpty();
}
@VisibleForTesting
protected boolean containsAnyTestCases(final RobotFile file) {
return !file.getTestCaseTable().getTestCases().isEmpty();
}
@VisibleForTesting
protected boolean containsAnyKeywords(final RobotFile file) {
return !file.getKeywordTable().getKeywords().isEmpty();
}
public boolean isSomethingToDo(final LineContinueType type) {
return (type != LineContinueType.NONE);
}
public void restorePreviousStack(final LineContinueType continueType, final Stack<ParsingState> parsingStates,
final RobotLine currentLine, final RobotToken currentToken) {
if (isSomethingToDo(continueType)) {
parsingStates.clear();
removeLastNotWantedStates(storedStack);
parsingStates.addAll(storedStack);
}
}
public void flushNew(final Stack<ParsingState> parsingStates) {
clear();
storedStack.addAll(parsingStates);
}
public void clear() {
storedStack.clear();
}
public static enum LineContinueType {
NONE,
SETTING_TABLE_ELEMENT,
VARIABLE_TABLE_ELEMENT,
TEST_CASE_TABLE_ELEMENT,
KEYWORD_TABLE_ELEMENT,
LINE_CONTINUE_INLINED;
}
@VisibleForTesting
protected void removeLastNotWantedStates(final Stack<ParsingState> parsingStates) {
for (int i = parsingStates.size() - 1; i >= 0; i--) {
ParsingState state = parsingStates.get(i);
if (state == ParsingState.COMMENT) {
parsingStates.remove(i);
} else {
break;
}
}
}
}