/*
* 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.model.table.exec.descs.ast.mapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.rf.ide.core.testdata.model.FilePosition;
import org.rf.ide.core.testdata.model.FileRegion;
import org.rf.ide.core.testdata.model.RobotFileOutput.BuildMessage;
import org.rf.ide.core.testdata.model.table.exec.descs.TextPosition;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.Container;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.Container.ContainerType;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.ContainerElementType;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.IContainerElement;
import org.rf.ide.core.testdata.model.table.exec.descs.ast.mapping.SimpleElementsMapper.IElementMapper;
@SuppressWarnings("PMD.GodClass")
public class DeclarationMapper {
private String fileMapped;
private final SimpleElementsMapper mapperFactory;
public DeclarationMapper() {
this.mapperFactory = new SimpleElementsMapper();
this.fileMapped = "<NOT_SET>";
}
public MappingResult map(final FilePosition fp, final Container container, final String filename) {
return map(new MappingResult(fp, filename), fp, container, filename);
}
private MappingResult map(final MappingResult topLevel, final FilePosition fp, final Container container,
final String filename) {
final MappingResult mappingResult = new MappingResult(fp, filename);
FilePosition currentPosition = fp;
if (container.getContainerType() == ContainerType.MIX) {
if (container.getParent() != null) {
throw new IllegalStateException("Mix container is only supported on the top level extraction.");
}
}
final ContainerMappingHelper mappingHelper = ContainerMappingHelper.createDeclaration(container,
currentPosition, mappingResult);
final IElementDeclaration topContainer = mappingHelper.getContainerDeclarationHolder();
if (topContainer != null) {
mappingResult.addMappedElement(topContainer);
}
final List<IContainerElement> elements = container.getElements();
final int contentEnd = mappingHelper.getContentEnd();
for (int index = mappingHelper.getContentStart(); index < contentEnd; index++) {
final IContainerElement containerElement = elements.get(index);
if (containerElement.isComplex()) {
final Container subContainer = (Container) containerElement;
final FilePosition previousForContainer = currentPosition;
final MappingResult subResult = map(mappingResult, currentPosition, subContainer, filename);
mappingResult.addCorrectVariables(subResult.getCorrectVariables());
mappingResult.addBuildMessages(subResult.getMessages());
if (topContainer != null) {
final List<IElementDeclaration> mappedElements = subResult.getMappedElements();
for (final IElementDeclaration dec : mappedElements) {
topContainer.addElementDeclarationInside(dec);
dec.setLevelUpElement(topContainer);
}
} else {
mappingResult.addMappedElements(subResult.getMappedElements());
}
List<IElementDeclaration> mappedElements;
if (topContainer != null) {
mappedElements = topContainer.getElementsDeclarationInside();
} else {
mappedElements = mappingResult.getMappedElements();
}
final IElementDeclaration lastComplex = mappedElements.get(mappedElements.size() - 1);
final IElementDeclaration variableIdentifier = getPossibleVariableIdentifier(mappedElements);
if (lastComplex instanceof VariableDeclaration) {
final VariableDeclaration variableDec = (VariableDeclaration) lastComplex;
final List<IElementDeclaration> escape = getEscape(mappedElements);
if (!escape.isEmpty()) {
if (topContainer != null) {
for (final IElementDeclaration d : escape) {
topContainer.removeExactlyTheSameInstance(d);
}
} else {
if (variableIdentifier != null) {
for (final IElementDeclaration d : escape) {
mappingResult.removeExactlyTheSameInstance(d);
}
}
}
variableDec.setEscape(escape.get(0).getStart());
}
if (variableIdentifier != null) {
if (topContainer != null) {
topContainer.removeExactlyTheSameInstance(variableIdentifier);
} else {
mappingResult.removeExactlyTheSameInstance(variableIdentifier);
}
variableDec.setTypeIdentificator(new TextPosition(
variableIdentifier.getStart().getFullText(), variableIdentifier.getStart().getStart(),
variableIdentifier.getEnd().getEnd()));
}
if (seamsToBeCorrectRobotVariable(previousForContainer, mappingResult, variableDec)) {
mappingResult.addCorrectVariable(variableDec);
} else {
convertIncorrectVariableBackToText(mappingResult, topContainer, variableDec);
}
} else {
final IndexDeclaration indexDec = (IndexDeclaration) lastComplex;
if (subContainer.isOpenForModification()) {
convertIncorrectIndexElementBackToText(mappingResult, topContainer, indexDec);
}
}
} else {
final ContainerElementType type = containerElement.getType();
final IElementMapper mapper = mapperFactory.getMapperFor(type);
if (mapper == null) {
throw new UnsupportedOperationException(
"ContainerElementType \'" + type + "\' is not supported yet!");
}
final MappingResult subResult = mapper.map(mappingResult, containerElement, currentPosition, filename);
if (topContainer != null) {
final List<IElementDeclaration> mappedElements = subResult.getMappedElements();
for (final IElementDeclaration dec : mappedElements) {
topContainer.addElementDeclarationInside(dec);
dec.setLevelUpElement(topContainer);
}
} else {
mappingResult.addMappedElements(subResult.getMappedElements());
}
currentPosition = subResult.getLastFilePosition();
}
}
mappingResult.setLastFilePosition(currentPosition);
return mappingResult;
}
private void convertIncorrectIndexElementBackToText(final MappingResult mappingResult,
final IElementDeclaration topContainer, final IndexDeclaration indexDec) {
final TextDeclaration textDec = new TextDeclaration(indexDec.getStart(),
ContainerElementType.SQUARE_BRACKET_OPEN);
final JoinedTextDeclarations joinedStart = new JoinedTextDeclarations();
joinedStart.addElementDeclarationInside(textDec);
if (topContainer != null) {
topContainer.removeExactlyTheSameInstance(indexDec);
topContainer.addElementDeclarationInside(joinedStart);
} else {
mappingResult.removeExactlyTheSameInstance(indexDec);
mappingResult.addMappedElement(joinedStart);
}
joinedStart.setLevelUpElement(topContainer);
final List<IElementDeclaration> elementsDeclarationInside = indexDec.getElementsDeclarationInside();
for (final IElementDeclaration dec : elementsDeclarationInside) {
if (dec.isComplex()) {
if (dec.getEnd() != null) {
if (topContainer != null) {
topContainer.addElementDeclarationInside(dec);
} else {
mappingResult.addMappedElement(dec);
}
dec.setLevelUpElement(topContainer);
}
} else {
if (topContainer != null) {
topContainer.addElementDeclarationInside(dec);
} else {
mappingResult.addMappedElement(dec);
}
dec.setLevelUpElement(topContainer);
}
}
}
private void convertIncorrectVariableBackToText(final MappingResult mappingResult,
final IElementDeclaration topContainer, final VariableDeclaration variableDec) {
final JoinedTextDeclarations joinedStart = new JoinedTextDeclarations();
if (variableDec.isEscaped()) {
final TextDeclaration escapeDec = new TextDeclaration(variableDec.getEscape(), ContainerElementType.ESCAPE);
joinedStart.addElementDeclarationInside(escapeDec);
}
if (variableDec.getTypeIdentificator() != null) {
final TextDeclaration typeId = new TextDeclaration(variableDec.getTypeIdentificator(),
ContainerElementType.VARIABLE_TYPE_ID);
joinedStart.addElementDeclarationInside(typeId);
}
final TextDeclaration variableCurlyBracket = new TextDeclaration(variableDec.getStart(),
ContainerElementType.CURLY_BRACKET_OPEN);
joinedStart.addElementDeclarationInside(variableCurlyBracket);
if (topContainer != null) {
topContainer.removeExactlyTheSameInstance(variableDec);
topContainer.addElementDeclarationInside(joinedStart);
} else {
mappingResult.removeExactlyTheSameInstance(variableDec);
mappingResult.addMappedElement(joinedStart);
}
joinedStart.setLevelUpElement(topContainer);
final List<IElementDeclaration> elementsDeclarationInside = variableDec.getElementsDeclarationInside();
for (final IElementDeclaration dec : elementsDeclarationInside) {
if (dec.isComplex()) {
if (dec.getEnd() != null) {
if (topContainer != null) {
topContainer.addElementDeclarationInside(dec);
} else {
mappingResult.addMappedElement(dec);
}
dec.setLevelUpElement(topContainer);
}
} else {
if (topContainer != null) {
topContainer.addElementDeclarationInside(dec);
} else {
mappingResult.addMappedElement(dec);
}
dec.setLevelUpElement(topContainer);
}
}
final TextPosition end = variableDec.getEnd();
if (end != null) {
final JoinedTextDeclarations joinedEnd = new JoinedTextDeclarations();
joinedEnd.addElementDeclarationInside(new TextDeclaration(end, ContainerElementType.CURLY_BRACKET_CLOSE));
if (topContainer != null) {
topContainer.addElementDeclarationInside(joinedEnd);
} else {
mappingResult.addMappedElement(joinedEnd);
}
joinedEnd.setLevelUpElement(topContainer);
}
}
private boolean seamsToBeCorrectRobotVariable(final FilePosition currentPosition, final MappingResult mappingResult,
final VariableDeclaration variableDec) {
boolean result = false;
if (!variableDec.isEscaped()) {
final TextPosition typeId = variableDec.getTypeIdentificator();
if (typeId != null) {
final String idText = typeId.getText();
if (idText != null && !idText.isEmpty()) {
if (idText.length() == 1) {
if (variableDec.getEnd() != null) {
result = true;
}
} else {
final BuildMessage warnMessage = BuildMessage.createWarnMessage(
"Incorrect variable id with space between " + idText.charAt(0) + " and '{'.",
getFileMapped());
warnMessage.setFileRegion(new FileRegion(
new FilePosition(currentPosition.getLine(), currentPosition.getColumn(),
currentPosition.getOffset()),
new FilePosition(currentPosition.getLine(),
currentPosition.getColumn() + variableDec.getEnd().getEnd(),
currentPosition.getOffset() + variableDec.getEnd().getEnd())));
mappingResult.addBuildMessage(warnMessage);
}
}
}
}
return result;
}
private List<IElementDeclaration> getEscape(final List<IElementDeclaration> mappedElements) {
final List<IElementDeclaration> varElements = new ArrayList<>();
if (mappedElements != null) {
final int nrOfMapped = mappedElements.size();
if (nrOfMapped >= 3) {
final IElementDeclaration possibleEscape = mappedElements.get(nrOfMapped - 3);
if (possibleEscape instanceof JoinedTextDeclarations) {
final JoinedTextDeclarations joined = (JoinedTextDeclarations) possibleEscape;
if (mapperFactory.containsOnly(joined, Arrays.asList(ContainerElementType.ESCAPE))) {
final List<IElementDeclaration> elementsInside = joined.getElementsDeclarationInside();
if (elementsInside.size() == 1) {
final TextDeclaration dec = (TextDeclaration) elementsInside.get(0);
if (dec.getLength() == 1) {
varElements.add(possibleEscape);
}
}
}
}
}
}
return varElements;
}
private IElementDeclaration getPossibleVariableIdentifier(final List<IElementDeclaration> mappedElements) {
IElementDeclaration elem = null;
if (mappedElements != null) {
final int numberOfMapped = mappedElements.size();
if (numberOfMapped >= 2) {
final IElementDeclaration lastSubContainer = mappedElements.get(numberOfMapped - 1);
if (lastSubContainer instanceof VariableDeclaration) {
final IElementDeclaration previous = mappedElements.get(numberOfMapped - 2);
final JoinedTextDeclarations text = new JoinedTextDeclarations();
text.addElementDeclarationInside(previous);
final String idText = text.getText();
if (idText != null) {
final String trimmed = idText.trim();
if (trimmed.length() >= 1) {
if (ContainerElementType.VARIABLE_TYPE_ID.getRepresentation().contains(trimmed.charAt(0))) {
elem = previous;
}
}
}
}
}
}
return elem;
}
public String getFileMapped() {
return fileMapped;
}
public void setFileMapped(final String fileMapped) {
this.fileMapped = fileMapped;
}
}