/*
* 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.table;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.rf.ide.core.testdata.mapping.PreviousLineHandler;
import org.rf.ide.core.testdata.model.AKeywordBaseSetting;
import org.rf.ide.core.testdata.model.FilePosition;
import org.rf.ide.core.testdata.model.RobotFile;
import org.rf.ide.core.testdata.model.RobotFileOutput;
import org.rf.ide.core.testdata.model.table.ARobotSectionTable;
import org.rf.ide.core.testdata.model.table.ECompareResult;
import org.rf.ide.core.testdata.model.table.TableHeader;
import org.rf.ide.core.testdata.model.table.exec.descs.ForDescriptorInfo;
import org.rf.ide.core.testdata.model.table.exec.descs.VariableExtractor;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.mapping.VariableDeclaration;
import org.rf.ide.core.testdata.model.table.setting.AImported;
import org.rf.ide.core.testdata.model.table.setting.LibraryImport;
import org.rf.ide.core.testdata.model.table.variables.AVariable.VariableType;
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.ParsingState.TableType;
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.ALineSeparator;
import org.rf.ide.core.testdata.text.read.separators.Separator;
import org.rf.ide.core.testdata.text.read.separators.Separator.SeparatorType;
import org.rf.ide.core.testdata.text.read.separators.StrictTsvTabulatorSeparator;
@SuppressWarnings("PMD.GodClass")
public class ElementsUtility {
private final ParsingStateHelper parsingStateHelper;
private final VariableExtractor varExtractor;
public ElementsUtility() {
this.parsingStateHelper = new ParsingStateHelper();
this.varExtractor = new VariableExtractor();
}
public List<RobotToken> filter(final List<RobotToken> toks, final IRobotTokenType type) {
final List<RobotToken> filtered = new ArrayList<>(0);
for (final RobotToken token : toks) {
if (token.getTypes().contains(type)) {
filtered.add(token);
}
}
return filtered;
}
public boolean isNewExecutableSection(final ALineSeparator separator, final RobotLine line) {
boolean result = false;
if (separator.getProducedType() == SeparatorType.PIPE) {
final List<IRobotLineElement> lineElements = line.getLineElements();
if (lineElements.size() == 1) {
result = lineElements.get(0).getTypes().contains(SeparatorType.PIPE);
}
} else {
result = line.getLineElements().isEmpty();
}
return result;
}
public LibraryImport findNearestLibraryImport(final RobotFileOutput robotFileOutput) {
final AImported imported = getNearestImport(robotFileOutput);
LibraryImport lib;
if (imported instanceof LibraryImport) {
lib = (LibraryImport) imported;
} else {
lib = null;
// FIXME: sth wrong - declaration of library not inside setting
// and
// was not catch by previous library declaration logic
}
return lib;
}
public RobotToken computeCorrectRobotToken(final RobotLine currentLine, final Stack<ParsingState> processingState,
final RobotFileOutput robotFileOutput, final FilePosition fp, final String text, final boolean isNewLine,
final List<RobotToken> robotTokens, final String fileName) {
final ParsingState state = parsingStateHelper.getCurrentStatus(processingState);
RobotToken correct = null;
final List<VariableDeclaration> correctVariables = varExtractor.extract(FilePosition.createNotSet(), text, "fake")
.getCorrectVariables();
if (robotTokens.size() > 1) {
final List<RobotToken> tokensExactlyOnPosition = getTokensExactlyOnPosition(robotTokens, fp);
final TableType currentTable = state.getTable();
if (tokensExactlyOnPosition.size() != 1 || currentTable == TableType.KEYWORD
|| currentTable == TableType.TEST_CASE) {
final List<RobotToken> headersPossible = findHeadersPossible(robotTokens);
if (!headersPossible.isEmpty()) {
if (headersPossible.size() == 1) {
correct = headersPossible.get(0);
} else {
// FIXME: error
}
} else {
final RobotToken comment = findCommentToken(robotTokens, text);
if (comment != null) {
correct = comment;
} else {
for (final RobotToken rt : robotTokens) {
if (parsingStateHelper.isTypeForState(state, rt)) {
correct = rt;
break;
}
}
}
final TableType tableType = state.getTable();
if (correct == null && (tableType == TableType.KEYWORD || tableType == TableType.TEST_CASE)) {
final ParsingState expected;
if (tableType == TableType.KEYWORD) {
expected = ParsingState.KEYWORD_DECLARATION;
} else {
expected = ParsingState.TEST_CASE_DECLARATION;
}
if (meetsState(state, expected) && tokensExactlyOnPosition.size() == 1) {
final RobotToken exactlyOne = tokensExactlyOnPosition.get(0);
final List<RobotTokenType> typesForVariablesTable = RobotTokenType
.getTypesForVariablesTable();
boolean isVarDec = false;
for (final IRobotTokenType type : exactlyOne.getTypes()) {
if (type instanceof RobotTokenType) {
final RobotTokenType tokenType = (RobotTokenType) type;
if (typesForVariablesTable.contains(tokenType)
&& tokenType.isSettingDeclaration()) {
if (!correctVariables.isEmpty()) {
final VariableType typeByTokenType = VariableType.getTypeByTokenType(type);
if (typeByTokenType.getIdentificator()
.equals(correctVariables.get(0).getTypeIdentificator().getText())) {
isVarDec = true;
break;
}
}
}
}
}
if (isVarDec) {
correct = exactlyOne;
}
}
}
if (correct == null) {
if (ParsingState.getSettingsStates().contains(state) || currentTable == TableType.VARIABLES
|| currentTable == TableType.KEYWORD || currentTable == TableType.TEST_CASE
|| state == ParsingState.COMMENT) {
final RobotToken newRobotToken = new RobotToken();
newRobotToken.setLineNumber(fp.getLine());
newRobotToken.setStartColumn(fp.getColumn());
newRobotToken.setText(text);
newRobotToken.setRaw(text);
newRobotToken.setType(RobotTokenType.UNKNOWN);
correct = newRobotToken;
} else {
// FIXME: info that nothing was found so token will
// be
// treat as UNKNOWN
final RobotToken newRobotToken = new RobotToken();
newRobotToken.setLineNumber(fp.getLine());
newRobotToken.setStartColumn(fp.getColumn());
newRobotToken.setText(text);
newRobotToken.setRaw(text);
newRobotToken.setType(RobotTokenType.UNKNOWN);
final List<IRobotTokenType> types = newRobotToken.getTypes();
for (final RobotToken currentProposal : robotTokens) {
types.addAll(currentProposal.getTypes());
}
correct = newRobotToken;
}
}
}
} else {
final RobotToken exactlyOnPosition = tokensExactlyOnPosition.get(0);
if (state.getPreviousState() != ParsingState.VARIABLE_TABLE_HEADER
&& state.getPreviousState() != ParsingState.VARIABLE_TABLE_INSIDE
&& state.getPreviousState() != ParsingState.SETTING_TABLE_HEADER
&& state.getPreviousState() != ParsingState.SETTING_TABLE_INSIDE) {
final List<IRobotTokenType> types = exactlyOnPosition.getTypes();
for (final RobotToken currentProposal : robotTokens) {
if (exactlyOnPosition != currentProposal) {
types.addAll(currentProposal.getTypes());
}
}
}
if (exactlyOnPosition.getRaw().equals(text)) {
correct = exactlyOnPosition;
} else {
final RobotToken newRobotToken = new RobotToken();
newRobotToken.setLineNumber(fp.getLine());
newRobotToken.setStartColumn(fp.getColumn());
newRobotToken.setText(text);
newRobotToken.setRaw(text);
newRobotToken.setType(RobotTokenType.UNKNOWN);
final List<IRobotTokenType> types = newRobotToken.getTypes();
for (final RobotToken rt : robotTokens) {
types.addAll(rt.getTypes());
}
correct = newRobotToken;
}
}
} else {
final RobotToken token = robotTokens.get(0);
if (!token.getTypes().contains(RobotTokenType.UNKNOWN)) {
final RobotToken newRobotToken = new RobotToken();
newRobotToken.setLineNumber(fp.getLine());
newRobotToken.setStartColumn(fp.getColumn());
newRobotToken.setText(text);
newRobotToken.setRaw(text);
if (text != null
&& !(text.equals(token.getRaw()) || text.trim().equals(token.getRaw().trim()))) {
newRobotToken.setType(RobotTokenType.UNKNOWN);
} else {
newRobotToken.getTypes().clear();
}
// FIXME: decide what to do
newRobotToken.getTypes().addAll(token.getTypes());
if (state.getTable() != TableType.VARIABLES) {
final List<RobotTokenType> typesForVariablesTable = RobotTokenType.getTypesForVariablesTable();
for (final IRobotTokenType type : token.getTypes()) {
if (type instanceof RobotTokenType) {
final RobotTokenType tokenType = (RobotTokenType) type;
if (typesForVariablesTable.contains(tokenType) && tokenType.isSettingDeclaration()) {
boolean notValidVar = true;
if (!correctVariables.isEmpty()) {
final VariableType typeByTokenType = VariableType.getTypeByTokenType(type);
for (final VariableDeclaration vd : correctVariables) {
if (typeByTokenType.getIdentificator()
.equals(vd.getTypeIdentificator().getText())) {
notValidVar = false;
break;
}
}
}
if (notValidVar) {
newRobotToken.getTypes().remove(type);
if (!newRobotToken.getTypes().contains(RobotTokenType.VARIABLES_WRONG_DEFINED)) {
newRobotToken.getTypes().add(RobotTokenType.VARIABLES_WRONG_DEFINED);
}
}
}
}
}
}
// or add warning about possible type
if (newRobotToken.getTypes().isEmpty()) {
newRobotToken.setType(RobotTokenType.UNKNOWN);
}
correct = newRobotToken;
} else {
correct = token;
}
}
boolean hasAnyProposalVariableInside = false;
for (final RobotToken rt : robotTokens) {
final List<IRobotTokenType> types = rt.getTypes();
for (final IRobotTokenType type : types) {
if (type == RobotTokenType.VARIABLES_DICTIONARY_DECLARATION
|| type == RobotTokenType.VARIABLES_SCALAR_AS_LIST_DECLARATION
|| type == RobotTokenType.VARIABLES_SCALAR_DECLARATION
|| type == RobotTokenType.VARIABLES_LIST_DECLARATION) {
if (!correctVariables.isEmpty()) {
final VariableType typeByTokenType = VariableType.getTypeByTokenType(type);
if (typeByTokenType.getIdentificator()
.equals(correctVariables.get(0).getTypeIdentificator().getText())) {
hasAnyProposalVariableInside = true;
break;
}
}
}
}
}
if (hasAnyProposalVariableInside && state != ParsingState.VARIABLE_TABLE_INSIDE) {
correct.getTypes().add(RobotTokenType.VARIABLE_USAGE);
}
return correct;
}
private boolean meetsState(final ParsingState state, final ParsingState expected) {
boolean result = false;
ParsingState currentState = state;
while (currentState.getPreviousState() != null) {
if (currentState.getPreviousState() == expected) {
result = true;
break;
} else {
currentState = currentState.getPreviousState();
}
}
return result;
}
private List<RobotToken> getTokensExactlyOnPosition(final List<RobotToken> robotTokens,
final FilePosition currentPosition) {
final List<RobotToken> tokens = new ArrayList<>();
for (final RobotToken rt : robotTokens) {
if (currentPosition.compare(rt.getFilePosition(), false) == ECompareResult.EQUAL_TO.getValue()) {
tokens.add(rt);
}
}
return tokens;
}
public RobotToken findCommentToken(final List<RobotToken> robotTokens, final String text) {
RobotToken comment = null;
for (final RobotToken rt : robotTokens) {
final List<IRobotTokenType> types = rt.getTypes();
if (types.contains(RobotTokenType.START_HASH_COMMENT) || types.contains(RobotTokenType.COMMENT_CONTINUE)) {
if (text.equals(rt.getRaw())) {
comment = rt;
break;
}
}
}
return comment;
}
public List<RobotToken> findHeadersPossible(final List<RobotToken> tokens) {
final List<RobotToken> found = new ArrayList<>();
for (final RobotToken t : tokens) {
if (isTableHeader(t)) {
found.add(t);
}
}
return found;
}
public boolean isComment(final RobotLine line) {
boolean result = false;
for (final IRobotLineElement elem : line.getLineElements()) {
final List<IRobotTokenType> types = elem.getTypes();
if (types.isEmpty()) {
result = false;
break;
} else {
final IRobotTokenType tokenType = types.get(0);
if (tokenType == SeparatorType.PIPE || tokenType == SeparatorType.TABULATOR_OR_DOUBLE_SPACE) {
continue;
} else if (tokenType == RobotTokenType.START_HASH_COMMENT) {
result = true;
break;
} else {
result = false;
break;
}
}
}
return result;
}
public boolean isTableSection(final RobotLine line) {
boolean result = false;
for (final IRobotLineElement elem : line.getLineElements()) {
if (isTableHeader(elem)) {
result = true;
break;
}
}
return result;
}
public AImported getNearestImport(final RobotFileOutput robotFileOutput) {
AImported result;
final List<AImported> imports = robotFileOutput.getFileModel().getSettingTable().getImports();
if (!imports.isEmpty()) {
result = imports.get(imports.size() - 1);
} else {
result = null;
}
return result;
}
public List<TableHeader<? extends ARobotSectionTable>> getKnownHeadersForTable(
final RobotFileOutput robotFileOutput, final ParsingState tableHeaderState) {
List<TableHeader<? extends ARobotSectionTable>> tableKnownHeaders = new ArrayList<>();
final RobotFile fileModel = robotFileOutput.getFileModel();
if (tableHeaderState == ParsingState.SETTING_TABLE_HEADER) {
tableKnownHeaders = fileModel.getSettingTable().getHeaders();
} else if (tableHeaderState == ParsingState.VARIABLE_TABLE_HEADER) {
tableKnownHeaders = fileModel.getVariableTable().getHeaders();
} else if (tableHeaderState == ParsingState.TEST_CASE_TABLE_HEADER) {
tableKnownHeaders = fileModel.getTestCaseTable().getHeaders();
} else if (tableHeaderState == ParsingState.KEYWORD_TABLE_HEADER) {
tableKnownHeaders = fileModel.getKeywordTable().getHeaders();
} else {
// FIXME: error state not coherent
}
return tableKnownHeaders;
}
public boolean isTableHeader(final IRobotTokenType type) {
return (type == RobotTokenType.SETTINGS_TABLE_HEADER || type == RobotTokenType.VARIABLES_TABLE_HEADER
|| type == RobotTokenType.TEST_CASES_TABLE_HEADER || type == RobotTokenType.KEYWORDS_TABLE_HEADER);
}
public boolean isTableHeader(final RobotToken t) {
boolean result = false;
final List<IRobotTokenType> declaredTypes = t.getTypes();
for (final IRobotTokenType type : declaredTypes) {
if (isTableHeader(type)) {
result = true;
break;
}
}
if (!t.getRaw().trim().startsWith("*")) {
result = false;
}
return result;
}
public boolean isTableHeader(final IRobotLineElement elem) {
boolean result = false;
if (elem instanceof RobotToken) {
result = isTableHeader((RobotToken) elem);
}
return result;
}
public boolean isUserTableHeader(final RobotToken t) {
boolean result = false;
final String raw = t.getRaw();
if (raw != null && raw.length() > 1) {
result = raw.trim().startsWith("*");
}
return result;
}
public boolean checkIfHasAlreadyKeywordName(final List<? extends AKeywordBaseSetting<?>> keywordBases) {
boolean result = false;
for (final AKeywordBaseSetting<?> setting : keywordBases) {
result = (setting.getKeywordName() != null);
result = result || !setting.getArguments().isEmpty();
if (result) {
break;
}
}
return result;
}
public boolean isNotOnlySeparatorOrEmptyLine(final RobotLine currentLine) {
boolean anyValuableToken = false;
final List<IRobotLineElement> lineElements = currentLine.getLineElements();
for (final IRobotLineElement lineElem : lineElements) {
if (lineElem instanceof RobotToken) {
anyValuableToken = true;
break;
}
}
return anyValuableToken;
}
public boolean shouldGiveEmptyToProcess(final RobotFileOutput parsingOutput, final ALineSeparator separator,
final Separator currentSeparator, final RobotLine line, final Stack<ParsingState> processingState) {
boolean result = false;
final ParsingState state = parsingStateHelper.getCurrentStatus(processingState);
final TableType tableType = state.getTable();
final List<IRobotLineElement> splittedLine = separator.getSplittedLine();
if (separator.getProducedType() == SeparatorType.PIPE && currentSeparator.getStartColumn() == 0) {
result = false;
} else if (separator.getProducedType() == SeparatorType.PIPE
|| separator instanceof StrictTsvTabulatorSeparator) {
final LineTokenInfo lineTokenInfo = LineTokenInfo.build(splittedLine);
if (!lineTokenInfo.getPositionsOfLineContinoue().isEmpty()
|| !lineTokenInfo.getPositionsOfNotEmptyElements().isEmpty()) {
// Logic: Grant valid to process empty elements in case:
// SETTINGS or VARIABLES: always read empty the exclusion is
// only that we have line continue and element is not the first
// after header declaration
// TEST CASES and KEYWORDS: read empty the exclusion is only
// line continue in any case only first element is omitted
// GRANT LOGIC main: always process start from beginning until
// last not empty element
final boolean isContinoue = lineTokenInfo.isLineContinoueTheFirst();
if (tableType == TableType.SETTINGS || tableType == TableType.VARIABLES) {
if (isContinoue) {
final RobotFile model = parsingOutput.getFileModel();
final PreviousLineHandler prevLineHandler = new PreviousLineHandler();
if (prevLineHandler.isSomethingToContinue(model)) {
result = lineTokenInfo.getDataStartIndex() <= separator.getCurrentElementIndex();
} else {
result = true;
}
} else {
result = true;
}
result = result && lineTokenInfo.getDataEndIndex() >= separator.getCurrentElementIndex();
} else if (tableType == TableType.TEST_CASE || tableType == TableType.KEYWORD) {
if (line.getLineElements().size() >= 2 || (line.getLineElements().size() == 1
&& separator instanceof StrictTsvTabulatorSeparator)) {
if (isContinoue) {
result = lineTokenInfo.getDataStartIndex() <= separator.getCurrentElementIndex();
} else {
if (state == ParsingState.TEST_CASE_DECLARATION
|| state == ParsingState.KEYWORD_DECLARATION) {
/**
* <pre>
* *** Test Cases ***
* | x | | ... | Log | ... |
*
* is not inline:
*
* ** Test Cases ***
* | | x | | ... | Log | ... |
* </pre>
*/
if (shouldTreatAsInlineContinue(lineTokenInfo)) {
result = separator.getCurrentElementIndex() > lineTokenInfo
.getPositionsOfLineContinoue().get(0)
|| separator.getCurrentElementIndex() < lineTokenInfo.getDataStartIndex();
} else {
result = true;
}
} else if (state == ParsingState.TEST_CASE_INSIDE_ACTION
|| state == ParsingState.KEYWORD_INSIDE_ACTION) {
final ForDescriptorInfo forInfo = ForDescriptorInfo.build(splittedLine);
if (forInfo.getForStartIndex() > -1) {
if (forInfo.getForLineContinueInlineIndex() > -1) {
result = (separator.getCurrentElementIndex() > forInfo
.getForLineContinueInlineIndex());
} else {
result = true;
}
} else {
result = true;
}
} else {
result = true;
}
}
result = result && lineTokenInfo.getDataEndIndex() >= separator.getCurrentElementIndex();
}
}
}
} else {
final LineTokenInfo lineTokenInfo = LineTokenInfo.build(splittedLine);
final boolean isContinoue = lineTokenInfo.isLineContinoueTheFirst();
if (tableType == TableType.SETTINGS || tableType == TableType.VARIABLES) {
if (isContinoue) {
final RobotFile model = parsingOutput.getFileModel();
final PreviousLineHandler prevLineHandler = new PreviousLineHandler();
if (prevLineHandler.isSomethingToContinue(model)) {
result = lineTokenInfo.getDataStartIndex() <= separator.getCurrentElementIndex();
} else {
result = true;
}
} else {
result = true;
}
result = result && lineTokenInfo.getDataEndIndex() >= separator.getCurrentElementIndex();
}
}
return result;
}
private boolean shouldTreatAsInlineContinue(final LineTokenInfo lineTokenInfo) {
boolean result = false;
if (!lineTokenInfo.getPositionsOfLineContinoue().isEmpty()
&& !lineTokenInfo.getPositionsOfNotEmptyElements().isEmpty()) {
final int theFirstToken = lineTokenInfo.getPositionsOfNotEmptyElements().get(0);
final int theFirstContinoue = lineTokenInfo.getPositionsOfLineContinoue().get(0);
if (lineTokenInfo.getPositionsOfNotEmptyElements().size() > 1) {
final int theSecondToken = lineTokenInfo.getPositionsOfNotEmptyElements().get(1);
result = theFirstToken < theFirstContinoue && theFirstContinoue < theSecondToken;
} else {
result = theFirstToken < theFirstContinoue;
}
}
return result;
}
public ARobotSectionTable getTable(final RobotFile robotModel, final TableType type) {
ARobotSectionTable table = null;
if (type == TableType.SETTINGS) {
table = robotModel.getSettingTable();
} else if (type == TableType.VARIABLES) {
table = robotModel.getVariableTable();
} else if (type == TableType.KEYWORD) {
table = robotModel.getKeywordTable();
} else if (type == TableType.TEST_CASE) {
table = robotModel.getTestCaseTable();
}
return table;
}
private static class LineTokenInfo {
private final List<Integer> positionsOfNotEmptyElements = new ArrayList<>();
private final List<Integer> positionsOfLineContinoue = new ArrayList<>();
private boolean isLineContinoue;
private int dataStartIndex = -1;
private int dataEndIndex = -1;
public static LineTokenInfo build(final List<IRobotLineElement> elements) {
final LineTokenInfo lti = new LineTokenInfo();
final int numberOfElements = elements.size();
for (int elemIndex = 0; elemIndex < numberOfElements; elemIndex++) {
final IRobotLineElement elem = elements.get(elemIndex);
if (elem instanceof RobotToken) {
final RobotToken token = (RobotToken) elem;
final String tokenText = token.getRaw();
if (RobotTokenType.PREVIOUS_LINE_CONTINUE.getRepresentation().get(0).equals(tokenText)) {
lti.positionsOfLineContinoue.add(elemIndex);
if (lti.positionsOfNotEmptyElements.isEmpty()) {
lti.isLineContinoue = true;
}
if (lti.dataStartIndex == -1) {
lti.dataStartIndex = elemIndex;
}
lti.dataEndIndex = elemIndex;
} else if (tokenText != null && !"".equals(tokenText.trim())) {
lti.positionsOfNotEmptyElements.add(elemIndex);
if (lti.dataStartIndex == -1) {
lti.dataStartIndex = elemIndex;
}
lti.dataEndIndex = elemIndex;
}
}
}
return lti;
}
public List<Integer> getPositionsOfNotEmptyElements() {
return positionsOfNotEmptyElements;
}
public List<Integer> getPositionsOfLineContinoue() {
return positionsOfLineContinoue;
}
public boolean isLineContinoueTheFirst() {
return isLineContinoue;
}
public int getDataStartIndex() {
return dataStartIndex;
}
public int getDataEndIndex() {
return dataEndIndex;
}
}
public void fixNotSetPositions(final RobotToken token, final FilePosition fp) {
if (token.getStartOffset() == IRobotLineElement.NOT_SET) {
token.setStartOffset(fp.getOffset());
}
if (token.getLineNumber() == IRobotLineElement.NOT_SET) {
token.setLineNumber(fp.getLine());
}
if (token.getStartColumn() == IRobotLineElement.NOT_SET) {
token.setStartColumn(fp.getColumn());
}
}
}