/*
* 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.importer;
import java.io.File;
import java.net.URI;
import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.rf.ide.core.executor.RobotRuntimeEnvironment;
import org.rf.ide.core.project.ImportSearchPaths.PathsProvider;
import org.rf.ide.core.testdata.model.FileRegion;
import org.rf.ide.core.testdata.model.RobotExpressions;
import org.rf.ide.core.testdata.model.RobotFileOutput;
import org.rf.ide.core.testdata.model.RobotFileOutput.BuildMessage;
import org.rf.ide.core.testdata.model.RobotProjectHolder;
import org.rf.ide.core.testdata.model.table.SettingTable;
import org.rf.ide.core.testdata.model.table.setting.AImported;
import org.rf.ide.core.testdata.model.table.setting.AImported.Type;
import org.rf.ide.core.testdata.model.table.setting.VariablesImport;
import org.rf.ide.core.testdata.text.read.recognizer.RobotToken;
import com.google.common.annotations.VisibleForTesting;
public class VariablesImporter {
private static final Pattern ILLEGAL_PATH_TEXT = Pattern.compile("\\s+([\\\\]|/)");
private final AbsoluteUriFinder uriFinder = new AbsoluteUriFinder();
public List<VariablesFileImportReference> importVariables(final PathsProvider pathsProvider,
final RobotProjectHolder robotProject, final RobotFileOutput robotFile) {
return importVariables(pathsProvider, robotProject.getRobotRuntime(), robotProject, robotFile);
}
public List<VariablesFileImportReference> importVariables(final PathsProvider pathsProvider,
final RobotRuntimeEnvironment robotRunEnv, final RobotProjectHolder robotProject,
final RobotFileOutput robotFile) {
final List<VariablesFileImportReference> varsImported = new ArrayList<>();
final SettingTable settingTable = robotFile.getFileModel().getSettingTable();
if (settingTable.isPresent()) {
final List<AImported> imports = settingTable.getImports();
for (final AImported imported : imports) {
final Type type = imported.getType();
if (type == Type.VARIABLES) {
// skip iteration if declared path is incorrect
final VariablesImport varImport = (VariablesImport) imported;
if (varImport.getPathOrName() == null) {
continue;
}
final String path = varImport.getPathOrName().getRaw().toString();
if (!isCorrectPath(path)) {
continue;
}
final Map<String, String> variableMappings = robotProject.getVariableMappings();
final List<String> varFileArguments = convertTokensToArguments(varImport, variableMappings);
// skip iteration if file does not exist or could not be obtained
URI importUri = null;
final File currentRobotFile = robotFile.getProcessedFile().getAbsoluteFile();
try {
final Optional<URI> foundUri = uriFinder.find(pathsProvider, variableMappings, currentRobotFile,
path);
if (foundUri.isPresent()) {
importUri = foundUri.get();
} else {
continue;
}
} catch (final Exception e) {
reportError("Problem with importing variable file " + path + " with error stack: " + e,
"" + currentRobotFile, varImport, robotFile);
continue;
}
// Get cached variables file import reference
final File varFile = new File(importUri);
VariablesFileImportReference varImportRef;
try {
varImportRef = findInProjectVariablesImport(pathsProvider, robotProject, varImport,
varFile.toPath().normalize().toFile());
} catch (final InvalidPathException ipe) {
reportError("Problem with importing variable file " + path + " with error stack: " + ipe,
"" + currentRobotFile, varImport, robotFile);
continue;
}
if (varImportRef == null) {
// could not find import reference in project, so will ask interpreter
Map<?, ?> variablesFromFile = new HashMap<>();
try {
variablesFromFile = robotRunEnv.getVariablesFromFile(varFile.getAbsolutePath(),
varFileArguments);
} catch (final Exception e) {
reportError("Problem with importing variable file " + path + " with error stack: " + e,
"" + currentRobotFile, varImport, robotFile);
continue;
}
varImportRef = new VariablesFileImportReference(varImport);
varImportRef.setVariablesFile(varFile.getAbsoluteFile());
varImportRef.map(variablesFromFile);
} else {
varImportRef = varImportRef.copy(varImport);
}
// Report that this variable file import contains no obtainable information
if (varImportRef.getVariables().isEmpty()) {
reportError("Could not find any variable in variable file: " + path, "" + currentRobotFile,
varImport, robotFile);
}
varsImported.add(varImportRef);
}
}
}
return varsImported;
}
@VisibleForTesting
protected boolean isCorrectPath(final String path) {
if (path != null && !path.trim().isEmpty()) {
final String convertedPath = RobotExpressions.unescapeSpaces(path);
return !ILLEGAL_PATH_TEXT.matcher(convertedPath).find();
}
return false;
}
private VariablesFileImportReference findInProjectVariablesImport(final PathsProvider pathsProvider,
final RobotProjectHolder robotProject, final VariablesImport varImport, final File varFile) {
VariablesFileImportReference varImportRef = null;
final RobotFileOutput fileWhichImportsVariables = robotProject
.findFileWithImportedVariableFile(pathsProvider, varFile);
if (fileWhichImportsVariables != null) {
final VariablesFileImportReference variableFile = findVariableFileByPath(pathsProvider, robotProject,
fileWhichImportsVariables, varFile);
if (isThisVariableFileTheSearchedOne(variableFile, varImport, varFile)) {
varImportRef = variableFile;
}
}
return varImportRef;
}
@VisibleForTesting
protected VariablesFileImportReference findVariableFileByPath(final PathsProvider pathsProvider,
final RobotProjectHolder robotProject, final RobotFileOutput rfo, final File varFile) {
final List<VariablesFileImportReference> variablesImportReferences = rfo
.getVariablesImportReferences(robotProject, pathsProvider);
for (final VariablesFileImportReference varFileImport : variablesImportReferences) {
if (varFileImport.getVariablesFile().getAbsolutePath().equals(varFile.getAbsolutePath())) {
return varFileImport;
}
}
return null;
}
private static boolean isThisVariableFileTheSearchedOne(final VariablesFileImportReference current,
final VariablesImport varImport, final File varFile) {
return (current != null) && checkIfImportDeclarationAreTheSame(varImport, current.getImportDeclaration())
&& (varFile.lastModified() == current.getLastModificationEpochTime());
}
@VisibleForTesting
protected static boolean checkIfImportDeclarationAreTheSame(final VariablesImport varImportCurrent,
final VariablesImport alreadyExecuted) {
boolean result = false;
if (varImportCurrent != null && alreadyExecuted != null) {
final List<RobotToken> argsCurrent = varImportCurrent.getArguments();
final List<RobotToken> argsPrevious = alreadyExecuted.getArguments();
if (argsCurrent.size() == argsPrevious.size()) {
result = true;
final int size = argsCurrent.size();
for (int i = 0; i < size; i++) {
final RobotToken argCurrent = argsCurrent.get(i);
final String argumentTextCurrent = argCurrent.getText();
final RobotToken argPrevious = argsPrevious.get(i);
final String argumentTextPrevious = argPrevious.getText();
// TODO: add resolve in case parameter is variable
if (argumentTextCurrent != null && argumentTextPrevious != null) {
if (!argumentTextCurrent.equals(argumentTextPrevious)) {
result = false;
}
} else if (argumentTextCurrent == null && argumentTextPrevious == null) {
result = true;
} else {
result = false;
}
if (!result) {
break;
}
}
}
}
return result;
}
@VisibleForTesting
protected List<String> convertTokensToArguments(final VariablesImport varImport,
final Map<String, String> variableMappings) {
final List<String> arguments = new ArrayList<>();
for (final RobotToken rtArgument : varImport.getArguments()) {
String arg = rtArgument.getRaw().toString();
if (RobotExpressions.isParameterized(arg)) {
arg = RobotExpressions.resolve(variableMappings, arg);
}
arguments.add(arg);
}
return arguments;
}
private static void reportError(final String message, final String filename, final VariablesImport varImport,
final RobotFileOutput robotFile) {
final BuildMessage errorMsg = BuildMessage.createErrorMessage(message, filename);
errorMsg.setFileRegion(new FileRegion(varImport.getPathOrName().getFilePosition(), varImport.getEndPosition()));
robotFile.addBuildMessage(errorMsg);
}
}