/*
* 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;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.rf.ide.core.project.ImportSearchPaths.PathsProvider;
import org.rf.ide.core.testdata.importer.ResourceImporter;
import org.rf.ide.core.testdata.importer.VariablesFileImportReference;
import org.rf.ide.core.testdata.importer.VariablesImporter;
import org.rf.ide.core.testdata.model.RobotFile;
import org.rf.ide.core.testdata.model.RobotFileOutput;
import org.rf.ide.core.testdata.model.RobotFileOutput.BuildMessage;
import org.rf.ide.core.testdata.model.RobotFileOutput.Status;
import org.rf.ide.core.testdata.model.RobotProjectHolder;
import org.rf.ide.core.testdata.model.RobotVersion;
import org.rf.ide.core.testdata.text.read.TsvRobotFileParser;
import org.rf.ide.core.testdata.text.read.TxtRobotFileParser;
public class RobotParser {
private static final int MAX_NUMBER_OF_TRASH_LINES = 5000;
private static final List<IRobotFileParser> AVAIL_FORMAT_PARSERS = new ArrayList<>();
static {
AVAIL_FORMAT_PARSERS.add(new TxtRobotFileParser());
AVAIL_FORMAT_PARSERS.add(new TsvRobotFileParser());
}
private final RobotParserConfig parserCfg;
private final String robotVersionFromCommand;
private final RobotVersion robotVersion;
private final RobotProjectHolder robotProject;
private final PathsProvider pathsProvider;
/**
* Creates parser which eagerly parses given file with all dependent
* resources (resource files, variables)
*
* @param projectHolder
* @return eager parser
*/
public static RobotParser createEager(final RobotProjectHolder projectHolder, final PathsProvider pathsProvider) {
final RobotParserConfig cfg = new RobotParserConfig();
cfg.setEagerImport(true);
return create(projectHolder, cfg, pathsProvider);
}
/**
* Creates parser which parses only given file without dependencies.
*
* @param projectHolder
* @return normal parser
*/
public static RobotParser create(final RobotProjectHolder projectHolder, final PathsProvider pathsProvider) {
return new RobotParser(projectHolder, new RobotParserConfig(), pathsProvider);
}
public static RobotParser create(final RobotProjectHolder projectHolder, final RobotParserConfig cfg,
final PathsProvider pathsProvider) {
return new RobotParser(projectHolder, cfg, pathsProvider);
}
public static RobotParser create(final RobotProjectHolder projectHolder, final RobotParserConfig cfg) {
return new RobotParser(projectHolder, cfg, null);
}
private RobotParser(final RobotProjectHolder robotProject, final RobotParserConfig cfg,
final PathsProvider pathsProvider) {
this.robotProject = robotProject;
this.pathsProvider = pathsProvider;
this.robotVersionFromCommand = robotProject.getRobotRuntime() != null
? robotProject.getRobotRuntime().getVersion() : null;
this.robotVersion = robotVersionFromCommand != null ? RobotVersion.from(robotVersionFromCommand) : null;
this.parserCfg = cfg;
}
public final RobotVersion getRobotVersion() {
return robotVersion;
}
public boolean isImportingEagerly() {
return this.parserCfg.isEagerImportOn();
}
/**
* Should be used for unsaved editor content. Parsed output is not replacing
* saved robot model in {@link RobotProjectHolder} object.
*
* @param fileContent
* @param fileOrDir
* @return
*/
public RobotFileOutput parseEditorContent(final String fileContent, final File fileOrDir) {
final RobotFileOutput robotFile = new RobotFileOutput(robotVersion);
final IRobotFileParser parserToUse = getParser(fileOrDir, true);
if (parserToUse != null) {
ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
if (fileContent != null && fileContent.length() > 0) {
bais = new ByteArrayInputStream(fileContent.getBytes(Charset.forName("UTF-8")));
}
parserToUse.parse(robotFile, bais, fileOrDir);
final RobotFile fileModel = robotFile.getFileModel();
if (fileModel.containsAnyRobotSection()) {
final List<File> alreadyImported = new ArrayList<>();
alreadyImported.add(fileOrDir);
importExternal(robotFile);
} else {
if (fileModel.getFileContent().size() > MAX_NUMBER_OF_TRASH_LINES) {
fileModel.removeLines();
}
}
} else {
robotFile.addBuildMessage(
BuildMessage.createErrorMessage("No parser found for file.", fileOrDir.getAbsolutePath()));
robotFile.setStatus(Status.FAILED);
}
return robotFile;
}
public List<RobotFileOutput> parse(final File fileOrDir) {
final List<RobotFileOutput> output = new ArrayList<>();
parse(fileOrDir, output);
return output;
}
private void parse(final File fileOrDir, final List<RobotFileOutput> output) {
if (fileOrDir != null) {
final boolean isDir = fileOrDir.isDirectory();
if (isDir) {
final int currentOutputSize = output.size();
final File[] files = fileOrDir.listFiles();
for (final File f : files) {
parse(f, output);
}
if (currentOutputSize < output.size()) {
// is high level test suite
// TODO: place where in case of TH type we should put
// information
}
} else if (robotProject.shouldBeLoaded(fileOrDir)) {
final IRobotFileParser parserToUse = getParser(fileOrDir, false);
if (parserToUse != null) {
final RobotFileOutput robotFile = new RobotFileOutput(robotVersion);
output.add(robotFile);
// do not change order !!! for performance reason is better
// to execute importing of variables before add to model,
// which replace previous object
parserToUse.parse(robotFile, fileOrDir);
robotProject.addModelFile(robotFile);
final RobotFile fileModel = robotFile.getFileModel();
if (fileModel.containsAnyRobotSection()) {
importExternal(robotFile);
} else {
if (fileModel.getFileContent().size() > MAX_NUMBER_OF_TRASH_LINES) {
fileModel.removeLines();
}
}
}
} else {
final RobotFileOutput fileByName = robotProject.findFileByName(fileOrDir);
if (fileByName != null) {
output.add(fileByName);
}
}
}
}
private void importExternal(final RobotFileOutput robotFile) {
if (robotFile.getStatus() == Status.PASSED) {
if (parserCfg.isEagerImportOn()) {
// eager get resources example
final ResourceImporter resImporter = new ResourceImporter(this);
resImporter.importResources(pathsProvider, robotProject, robotFile);
}
if (parserCfg.shouldImportVariables()) {
final VariablesImporter varImporter = new VariablesImporter();
final List<VariablesFileImportReference> varsImported = varImporter
.importVariables(pathsProvider, robotProject, robotFile);
robotFile.setVariablesImportReferences(varsImported);
}
}
}
private IRobotFileParser getParser(final File fileOrDir, final boolean isFromStringContent) {
IRobotFileParser parserToUse = null;
for (final IRobotFileParser parser : AVAIL_FORMAT_PARSERS) {
synchronized (parser) {
if (parser.canParseFile(fileOrDir, isFromStringContent)) {
parserToUse = parser.newInstance();
break;
}
}
}
return parserToUse;
}
public static class RobotParserConfig {
public static RobotParserConfig allImportsEager() {
final RobotParserConfig config = new RobotParserConfig();
config.shouldEagerImport = true;
config.shouldImportVariables = true;
return config;
}
public static RobotParserConfig allImportsLazy() {
final RobotParserConfig config = new RobotParserConfig();
config.shouldEagerImport = false;
config.shouldImportVariables = false;
return config;
}
private boolean shouldEagerImport = false;
private boolean shouldImportVariables = true;
public void setEagerImport(final boolean shouldEagerImport) {
this.shouldEagerImport = shouldEagerImport;
}
public void setIncludeImportVariables(final boolean shouldImportVariables) {
this.shouldImportVariables = shouldImportVariables;
}
public boolean isEagerImportOn() {
return this.shouldEagerImport;
}
public boolean shouldImportVariables() {
return this.shouldImportVariables;
}
}
}