/* * Copyright 2016 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.rf.ide.core.testdata.text.write; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; 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.model.FilePosition; import org.rf.ide.core.testdata.model.RobotFile; import org.rf.ide.core.testdata.model.table.variables.AVariable; 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.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; public class SectionBuilder { private final ElementPositionResolver posResolver = new ElementPositionResolver(); public List<Section> build(final RobotFile model) { final List<Section> sections = new ArrayList<>(0); final List<RobotLine> fileContent = model.getFileContent(); SectionType currentSectionType = SectionType.TRASH; Section section = new Section(currentSectionType, new FilePosition(1, 0, 0)); sections.add(section); for (final RobotLine line : fileContent) { final List<IRobotLineElement> lineElements = line.getLineElements(); int elemIndex = 0; for (final IRobotLineElement elem : lineElements) { if (elem.getTypes().contains(RobotTokenType.USER_OWN_TABLE_HEADER) && isCorrectTableHeader(line, elemIndex)) { currentSectionType = SectionType.USER_TABLE; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.add(section); } else if (elem.getTypes().contains(RobotTokenType.SETTINGS_TABLE_HEADER) && isCorrectTableHeader(line, elemIndex)) { currentSectionType = SectionType.SETTINGS; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.add(section); } else if (elem.getTypes().contains(RobotTokenType.VARIABLES_TABLE_HEADER) && isCorrectTableHeader(line, elemIndex)) { currentSectionType = SectionType.VARIABLES; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.add(section); } else if (elem.getTypes().contains(RobotTokenType.TEST_CASES_TABLE_HEADER) && isCorrectTableHeader(line, elemIndex)) { currentSectionType = SectionType.TEST_CASES; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.add(section); } else if (elem.getTypes().contains(RobotTokenType.KEYWORDS_TABLE_HEADER) && isCorrectTableHeader(line, elemIndex)) { currentSectionType = SectionType.KEYWORDS; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.add(section); } else if (currentSectionType == SectionType.SETTINGS || currentSectionType == SectionType.SETTING) { if (isSettingDeclaration(line, elemIndex)) { currentSectionType = SectionType.SETTING; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.get(sections.size() - 1).addSubSection(section); } } else if (currentSectionType == SectionType.VARIABLES || currentSectionType == SectionType.VARIABLE) { if (isVariableDeclaration(model, line, elemIndex)) { currentSectionType = SectionType.VARIABLE; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.get(sections.size() - 1).addSubSection(section); } } else if (currentSectionType == SectionType.TEST_CASES || currentSectionType == SectionType.TEST_CASE || currentSectionType == SectionType.TEST_CASE_SETTING || currentSectionType == SectionType.TEST_CASE_ROW) { if (isTestCaseDeclaration(line, elemIndex)) { currentSectionType = SectionType.TEST_CASE; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.get(sections.size() - 1).addSubSection(section); } else if (isTestCaseRowDeclaration(line, elemIndex)) { currentSectionType = SectionType.TEST_CASE_ROW; FilePosition startPos; if (doNotContainsType(line, elem, RobotTokenType.TEST_CASE_NAME)) { startPos = lineElements.get(0).getFilePosition(); } else { startPos = elem.getFilePosition(); } section = new Section(currentSectionType, startPos); final List<Section> subSections = sections.get(sections.size() - 1).getSubSections(); if (!subSections.isEmpty()) { subSections.get(subSections.size() - 1).addSubSection(section); } } else if (isSettingTestCaseDeclaration(line, elemIndex)) { currentSectionType = SectionType.TEST_CASE_SETTING; FilePosition startPos; if (doNotContainsType(line, elem, RobotTokenType.TEST_CASE_NAME)) { startPos = lineElements.get(0).getFilePosition(); } else { startPos = elem.getFilePosition(); } section = new Section(currentSectionType, startPos); final List<Section> subSections = sections.get(sections.size() - 1).getSubSections(); subSections.get(subSections.size() - 1).addSubSection(section); } } else if (currentSectionType == SectionType.KEYWORDS || currentSectionType == SectionType.KEYWORD || currentSectionType == SectionType.KEYWORD_ROW || currentSectionType == SectionType.KEYWORD_SETTING) { if (isKeywordDeclaration(line, elemIndex)) { currentSectionType = SectionType.KEYWORD; section = new Section(currentSectionType, lineElements.get(0).getFilePosition()); sections.get(sections.size() - 1).addSubSection(section); } else if (isKeywordRowDeclaration(line, elemIndex)) { currentSectionType = SectionType.KEYWORD_ROW; FilePosition startPos; if (doNotContainsType(line, elem, RobotTokenType.KEYWORD_NAME)) { startPos = lineElements.get(0).getFilePosition(); } else { startPos = elem.getFilePosition(); } section = new Section(currentSectionType, startPos); final List<Section> subSections = sections.get(sections.size() - 1).getSubSections(); if (!subSections.isEmpty()) { subSections.get(subSections.size() - 1).addSubSection(section); } } else if (isSettingKeywordDeclaration(line, elemIndex)) { currentSectionType = SectionType.KEYWORD_SETTING; FilePosition startPos; if (doNotContainsType(line, elem, RobotTokenType.KEYWORD_NAME)) { startPos = lineElements.get(0).getFilePosition(); } else { startPos = elem.getFilePosition(); } section = new Section(currentSectionType, startPos); final List<Section> subSections = sections.get(sections.size() - 1).getSubSections(); subSections.get(subSections.size() - 1).addSubSection(section); } } elemIndex++; } final FilePosition end = new FilePosition(line.getLineNumber(), line.getEndOfLine().getEndColumn(), line.getEndOfLine().getStartOffset() + line.getEndOfLine().getRaw().length()); if (sections.get(sections.size() - 1) != section) { applyEndForLastSubSections(sections.get(sections.size() - 1), end); } section.setEnd(end); } if (!sections.isEmpty()) { final Section theFirstSection = sections.get(0); if (theFirstSection.getType() == SectionType.TRASH) { final FilePosition start = theFirstSection.getStart(); final FilePosition end = theFirstSection.getEnd(); if (start.isSamePlace(end) || theFirstSection.getEnd() == null) { sections.remove(0); } } } return sections; } private void applyEndForLastSubSections(final Section section, final FilePosition end) { section.setEnd(end); final List<Section> subSections = section.getSubSections(); if (!subSections.isEmpty()) { applyEndForLastSubSections(subSections.get(subSections.size() - 1), end); } } private boolean doNotContainsType(final RobotLine line, final IRobotLineElement current, final RobotTokenType type) { boolean result = true; final List<IRobotLineElement> lineElements = line.getLineElements(); for (final IRobotLineElement rle : lineElements) { if (rle == current) { break; } else if (rle.getTypes().contains(type)) { result = false; break; } } return result; } private boolean isSettingKeywordDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); for (final IRobotTokenType tokenType : types) { if (tokenType instanceof RobotTokenType) { final RobotTokenType type = (RobotTokenType) tokenType; if (type.isSettingDeclaration() && RobotTokenType.getTypesForKeywordsTable().contains(type)) { result = true; break; } } } } return result; } private boolean isKeywordDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); result = types.contains(RobotTokenType.KEYWORD_NAME); } return result && isCorrectKeywordDeclaration(line, elementIndex); } private boolean isKeywordRowDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); result = types.contains(RobotTokenType.KEYWORD_ACTION_NAME); } return result && isCorrectKeywordExecRowDeclaration(line, elementIndex); } private boolean isTestCaseRowDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); result = types.contains(RobotTokenType.TEST_CASE_ACTION_NAME); } return result && isCorrectTestCaseExecRowDeclaration(line, elementIndex); } private boolean isSettingTestCaseDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); for (final IRobotTokenType tokenType : types) { if (tokenType instanceof RobotTokenType) { final RobotTokenType type = (RobotTokenType) tokenType; if (type.isSettingDeclaration() && RobotTokenType.getTypesForTestCasesTable().contains(type)) { result = true; break; } } } } return result; } private boolean isTestCaseDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); result = types.contains(RobotTokenType.TEST_CASE_NAME); } return result && isCorrectTestCaseDeclaration(line, elementIndex); } private boolean isVariableDeclaration(final RobotFile model, final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); for (final IRobotTokenType tokenType : types) { if (tokenType instanceof RobotTokenType) { final RobotTokenType type = (RobotTokenType) tokenType; if ((type.isSettingDeclaration() && RobotTokenType.getTypesForVariablesTable().contains(type)) || (type == RobotTokenType.START_HASH_COMMENT && isUnknownVariableStart(model, line, elem))) { result = true; break; } } } } return result && isCorrectVariableDeclaration(line, elementIndex); } private boolean isUnknownVariableStart(final RobotFile model, final RobotLine line, final IRobotLineElement elem) { boolean result = false; final FilePosition filePosition = elem.getFilePosition(); if (!filePosition.isNotSet()) { final Optional<AVariable> var = model.getVariableTable().findVariable(elem); if (var.isPresent()) { final AVariable v = var.get(); if (v.getDeclaration().getRaw().isEmpty()) { result = (v.getElementTokens().indexOf(elem) == 1); } } } return result; } private boolean isSettingDeclaration(final RobotLine line, final int elementIndex) { boolean result = false; final IRobotLineElement elem = line.getLineElements().get(elementIndex); if (elem instanceof RobotToken) { final List<IRobotTokenType> types = elem.getTypes(); for (final IRobotTokenType tokenType : types) { if (tokenType instanceof RobotTokenType) { final RobotTokenType type = (RobotTokenType) tokenType; if (type.isSettingDeclaration() && RobotTokenType.getTypesForSettingsTable().contains(type)) { result = true; break; } } } } return result && isCorrectSettingDeclaration(line, elementIndex); } private boolean isCorrectKeywordExecRowDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.KEYWORD_EXEC_ROW_ACTION_NAME); } private boolean isCorrectKeywordDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.USER_KEYWORD_NAME); } private boolean isCorrectTestCaseExecRowDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.TEST_CASE_EXEC_ROW_ACTION_NAME); } private boolean isCorrectTestCaseDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.TEST_CASE_NAME); } private boolean isCorrectVariableDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.VARIABLE_DECLARATION_IN_VARIABLE_TABLE); } private boolean isCorrectSettingDeclaration(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.SETTING_TABLE_ELEMENT_DECLARATION); } private boolean isCorrectTableHeader(final RobotLine line, final int elementIndex) { return isCorrectPlace(line, elementIndex, PositionExpected.TABLE_HEADER); } private boolean isCorrectPlace(final RobotLine line, final int elementIndex, final PositionExpected posExpected) { boolean result = false; final RobotLine lineFake = new RobotLine(line.getLineNumber(), line.getParent()); final Optional<SeparatorType> separatorForLine = line.getSeparatorForLine(); if (separatorForLine.isPresent()) { lineFake.setSeparatorType(line.getSeparatorForLine().get()); } lineFake.setLineElements(line.getLineElements().subList(0, elementIndex)); if (posResolver.isCorrectPosition(posExpected, line.getParent(), lineFake, (RobotToken) line.getLineElements().get(elementIndex))) { result = true; } return result; } public static class Section { private final SectionType type; private final FilePosition start; private FilePosition end; private final List<Section> subSections = new ArrayList<>(0); public Section(final SectionType type, final FilePosition start) { this.type = type; this.start = start; } public SectionType getType() { return type; } public FilePosition getStart() { return start; } public FilePosition getEnd() { return end; } public void setEnd(final FilePosition end) { this.end = end; } public void addSubSection(final Section s) { if (type.containsSubSectionType(s.getType())) { subSections.add(s); } else { throw new UnsupportedOperationException("Type " + s.getType() + " is not subtype of " + type); } } public List<Section> getSubSections() { return Collections.unmodifiableList(subSections); } @Override public String toString() { return String.format("Section [type=%s, start=%s, end=%s, subSections=%s]", type, start, end, subSections); } } public static enum SectionType { TRASH, USER_TABLE, SETTING, SETTINGS(SETTING), VARIABLE, VARIABLES(VARIABLE), TEST_CASE_SETTING, TEST_CASE_ROW, TEST_CASE(TEST_CASE_SETTING, TEST_CASE_ROW), TEST_CASES(TEST_CASE), KEYWORD_SETTING, KEYWORD_ROW, KEYWORD(KEYWORD_SETTING, KEYWORD_ROW), KEYWORDS(KEYWORD); private final SectionType[] subSections; private SectionType(final SectionType... subSections) { this.subSections = subSections; } public boolean containsSubSectionType(final SectionType subSectionType) { boolean result = false; for (final SectionType st : subSections) { if (st == subSectionType) { result = true; break; } } return result; } public static List<Section> filterByType(final List<Section> sections, final int sectionWithHeaderPos, final SectionType type) { final List<Section> matched = new ArrayList<>(); if (sectionWithHeaderPos >= 0) { final int sectionsSize = sections.size(); for (int sectionId = sectionWithHeaderPos; sectionId < sectionsSize; sectionId++) { final Section section = sections.get(sectionId); if (section.getType() == type) { matched.add(section); } } } return matched; } } }