/*
* Copyright 2015 Nokia Solutions and Networks
* Licensed under the Apache License, Version 2.0,
* see license.txt file for details.
*/
package org.robotframework.ide.eclipse.main.plugin.project.build.validation;
import static com.google.common.collect.Sets.newHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.rf.ide.core.testdata.model.table.VariableTable;
import org.rf.ide.core.testdata.model.table.variables.AVariable;
import org.rf.ide.core.testdata.model.table.variables.AVariable.VariableType;
import org.rf.ide.core.testdata.model.table.variables.DictionaryVariable;
import org.rf.ide.core.testdata.model.table.variables.DictionaryVariable.DictionaryKeyValuePair;
import org.rf.ide.core.testdata.model.table.variables.IVariableHolder;
import org.rf.ide.core.testdata.model.table.variables.names.VariableNamesSupport;
import org.rf.ide.core.testdata.text.read.recognizer.RobotToken;
import org.rf.ide.core.testdata.text.read.recognizer.RobotTokenType;
import org.robotframework.ide.eclipse.main.plugin.model.RobotVariablesSection;
import org.robotframework.ide.eclipse.main.plugin.project.build.AdditionalMarkerAttributes;
import org.robotframework.ide.eclipse.main.plugin.project.build.ProblemsReportingStrategy;
import org.robotframework.ide.eclipse.main.plugin.project.build.RobotArtifactsValidator.ModelUnitValidator;
import org.robotframework.ide.eclipse.main.plugin.project.build.RobotProblem;
import org.robotframework.ide.eclipse.main.plugin.project.build.causes.VariablesProblem;
import org.robotframework.ide.eclipse.main.plugin.project.build.validation.versiondependent.VersionDependentModelUnitValidator;
import org.robotframework.ide.eclipse.main.plugin.project.build.validation.versiondependent.VersionDependentValidators;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
class VariablesTableValidator implements ModelUnitValidator {
private final FileValidationContext validationContext;
private final Optional<RobotVariablesSection> variablesSection;
private final ProblemsReportingStrategy reporter;
private final VersionDependentValidators versionDependentValidators;
VariablesTableValidator(final FileValidationContext validationContext,
final Optional<RobotVariablesSection> variablesSection, final ProblemsReportingStrategy reportingStrategy) {
this(validationContext, variablesSection, reportingStrategy, new VersionDependentValidators());
}
@VisibleForTesting
VariablesTableValidator(final FileValidationContext validationContext,
final Optional<RobotVariablesSection> variablesSection, final ProblemsReportingStrategy reportingStrategy,
final VersionDependentValidators versionDependentValidators) {
this.validationContext = validationContext;
this.variablesSection = variablesSection;
this.reporter = reportingStrategy;
this.versionDependentValidators = versionDependentValidators;
}
@Override
public void validate(final IProgressMonitor monitor) throws CoreException {
if (!variablesSection.isPresent()) {
return;
}
final VariableTable variableTable = variablesSection.get().getLinkedElement();
reportVersionSpecificProblems(variableTable, monitor);
reportInvalidVariableName(variableTable);
reportUnknownVariableTypes(variableTable);
reportDuplicatedVariables(variableTable);
reportDictionaryValuesWithInvalidSyntax(variableTable);
reportUnknownVariablesInValues(variableTable);
}
private void reportVersionSpecificProblems(final VariableTable variableTable, final IProgressMonitor monitor)
throws CoreException {
for (final IVariableHolder variable : variableTable.getVariables()) {
final Iterable<VersionDependentModelUnitValidator> validators = versionDependentValidators
.getVariableValidators(validationContext, variable, reporter);
for (final ModelUnitValidator validator : validators) {
validator.validate(monitor);
}
}
}
private void reportInvalidVariableName(final VariableTable variableTable) {
for (final IVariableHolder variable : variableTable.getVariables()) {
final VariableType varType = variable.getType();
if (varType != VariableType.INVALID) {
final String variableName = variable.getDeclaration().getRaw();
if (variableName != null && variableName.length() >= 2) {
if (variableName.charAt(1) != '{') {
final RobotProblem problem = RobotProblem.causedBy(VariablesProblem.INVALID_NAME)
.formatMessageWith(variableName);
final Map<String, Object> attributes = ImmutableMap
.<String, Object> of(AdditionalMarkerAttributes.NAME, variableName);
reporter.handleProblem(problem, validationContext.getFile(), variable.getDeclaration(),
attributes);
}
}
}
}
}
private void reportUnknownVariableTypes(final VariableTable variableTable) {
for (final IVariableHolder variable : variableTable.getVariables()) {
if (variable.getType() == VariableType.INVALID) {
final RobotProblem problem = RobotProblem.causedBy(VariablesProblem.INVALID_TYPE)
.formatMessageWith(variable.getName());
final Map<String, Object> attributes = ImmutableMap.<String, Object> of(AdditionalMarkerAttributes.NAME,
variable.getName());
reporter.handleProblem(problem, validationContext.getFile(), variable.getDeclaration(), attributes);
}
}
}
private void reportDuplicatedVariables(final VariableTable variableTable) {
final Set<String> duplicatedNames = newHashSet();
for (final IVariableHolder var1 : variableTable.getVariables()) {
final String var1Name = VariableNamesSupport.extractUnifiedVariableName(var1.getName());
for (final IVariableHolder var2 : variableTable.getVariables()) {
if (var1.getName() != null && var2.getName() != null) {
if (var1 != var2
&& var1Name.equals(VariableNamesSupport.extractUnifiedVariableName(var2.getName()))) {
duplicatedNames.add(var1Name);
}
}
}
}
for (final IVariableHolder variable : variableTable.getVariables()) {
if (variable.getName() != null
&& duplicatedNames.contains(VariableNamesSupport.extractUnifiedVariableName(variable.getName()))) {
final RobotProblem problem = RobotProblem.causedBy(VariablesProblem.DUPLICATED_VARIABLE)
.formatMessageWith(variable.getName());
final Map<String, Object> attributes = ImmutableMap.<String, Object> of(AdditionalMarkerAttributes.NAME,
variable.getName());
reporter.handleProblem(problem, validationContext.getFile(), variable.getDeclaration(), attributes);
}
}
}
private void reportDictionaryValuesWithInvalidSyntax(final VariableTable variableTable) {
for (final AVariable variableDef : variableTable.getVariables()) {
if (variableDef.getType() == VariableType.DICTIONARY) {
final DictionaryVariable dictionaryDef = (DictionaryVariable) variableDef;
for (final DictionaryKeyValuePair pair : dictionaryDef.getItems()) {
final boolean hasAssignment = pair.getRaw().getText().contains("=");
if (!hasAssignment && !isOtherDictionary(pair.getRaw())) {
reportDictionarySyntaxProblem(variableDef, pair, true, "has to contain '=' separator");
} else if (hasAssignment && keyIsListOrDict(pair.getKey())) {
reportDictionarySyntaxProblem(variableDef, pair, false, "cannot use list or dictionary as key");
}
}
}
}
}
private boolean keyIsListOrDict(final RobotToken token) {
return (token.getText().startsWith("@{") || token.getText().startsWith("&{")) && token.getText().endsWith("}");
}
private boolean isOtherDictionary(final RobotToken token) {
return token.getTypes().contains(RobotTokenType.VARIABLES_DICTIONARY_DECLARATION)
&& token.getText().startsWith("&{") && token.getText().endsWith("}");
}
private void reportDictionarySyntaxProblem(final AVariable variableDef, final DictionaryKeyValuePair pair,
final boolean change, final String additionalMsg) {
final String value = pair.getRaw().getText();
final RobotProblem problem = RobotProblem
.causedBy(VariablesProblem.INVALID_DICTIONARY_ELEMENT_SYNTAX)
.formatMessageWith(value, variableDef.getName(), additionalMsg);
final Map<String, Object> attributes = change
? ImmutableMap.<String, Object> of(AdditionalMarkerAttributes.VALUE, value)
: ImmutableMap.<String, Object> of();
reporter.handleProblem(problem, validationContext.getFile(), pair.getRaw(), attributes);
}
private void reportUnknownVariablesInValues(final VariableTable variableTable) {
final Set<String> variables = validationContext.getAccessibleVariables();
final UnknownVariables unknownVarsValidator = new UnknownVariables(validationContext, reporter);
for (final AVariable variableDef : variableTable.getVariables()) {
unknownVarsValidator.reportUnknownVars(variableDef.getValueTokens(), variables);
}
}
}