/*
* 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;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.rf.ide.core.dryrun.RobotDryRunLibraryImport;
import org.rf.ide.core.dryrun.RobotDryRunLibraryImport.DryRunLibraryImportStatus;
import org.rf.ide.core.dryrun.RobotDryRunLibraryImport.DryRunLibraryType;
import org.rf.ide.core.executor.EnvironmentSearchPaths;
import org.rf.ide.core.executor.RobotRuntimeEnvironment.RobotEnvironmentException;
import org.rf.ide.core.project.RobotProjectConfig.LibraryType;
import org.rf.ide.core.project.RobotProjectConfig.ReferencedLibrary;
import org.robotframework.ide.eclipse.main.plugin.RedPlugin;
import org.robotframework.ide.eclipse.main.plugin.RedWorkspace;
import org.robotframework.ide.eclipse.main.plugin.launch.RobotSuitesNaming;
import org.robotframework.ide.eclipse.main.plugin.model.RobotProject;
import org.robotframework.ide.eclipse.main.plugin.model.RobotSuiteFile;
import org.robotframework.ide.eclipse.main.plugin.project.editor.libraries.ILibraryClass;
import org.robotframework.ide.eclipse.main.plugin.project.editor.libraries.JarStructureBuilder;
import org.robotframework.ide.eclipse.main.plugin.project.editor.libraries.PythonLibStructureBuilder;
import org.robotframework.red.swt.SwtThread;
import com.google.common.base.Strings;
import com.google.common.io.Files;
/**
* @author mmarzec
*/
public class LibrariesAutoDiscoverer extends AbstractAutoDiscoverer {
private final IEventBroker eventBroker;
private final boolean showSummary;
private final Optional<String> libraryNameToDiscover;
public LibrariesAutoDiscoverer(final RobotProject robotProject, final Collection<? extends IResource> suiteFiles,
final IEventBroker eventBroker) {
this(robotProject, suiteFiles, eventBroker, true, null);
}
public LibrariesAutoDiscoverer(final RobotProject robotProject, final Collection<? extends IResource> suiteFiles,
final IEventBroker eventBroker, final String libraryNameToDiscover) {
this(robotProject, suiteFiles, eventBroker, true, libraryNameToDiscover);
}
public LibrariesAutoDiscoverer(final RobotProject robotProject, final Collection<? extends IResource> suiteFiles,
final boolean showSummary) {
this(robotProject, suiteFiles, null, showSummary, null);
}
private LibrariesAutoDiscoverer(final RobotProject robotProject, final Collection<? extends IResource> suiteFiles,
final IEventBroker eventBroker, final boolean showSummary, final String libraryNameToDiscover) {
super(robotProject, suiteFiles);
this.eventBroker = eventBroker == null ? PlatformUI.getWorkbench().getService(IEventBroker.class) : eventBroker;
this.showSummary = showSummary;
this.libraryNameToDiscover = Optional.ofNullable(Strings.emptyToNull(libraryNameToDiscover));
}
@Override
public void start(final Shell parent) {
if (lockDryRun()) {
final WorkspaceJob wsJob = new WorkspaceJob("Discovering libraries") {
@Override
public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException {
try {
startDiscovering(monitor, new DryRunTargetsCollector());
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
final List<RobotDryRunLibraryImport> libraryImports = getLibraryImportsToProcess();
startAddingLibrariesToProjectConfiguration(monitor, libraryImports);
if (showSummary) {
showSummary(parent, libraryImports);
}
} catch (final InvocationTargetException e) {
throw new AutoDiscovererException("Problems occurred during discovering libraries.", e);
} catch (final InterruptedException e) {
// fine, will simply stop dry run
} finally {
monitor.done();
unlockDryRun();
}
return Status.OK_STATUS;
}
@Override
protected void canceling() {
dryRunHandler.destroyDryRunProcess();
}
};
wsJob.setUser(true);
wsJob.schedule();
}
}
private List<RobotDryRunLibraryImport> getLibraryImportsToProcess() {
final List<RobotDryRunLibraryImport> libraryImports = dryRunLibraryImportCollector.getImportedLibraries();
if (libraryNameToDiscover.isPresent()) {
for (final RobotDryRunLibraryImport libraryImport : libraryImports) {
if (libraryImport.getName().equalsIgnoreCase(libraryNameToDiscover.get())) {
return Collections.singletonList(libraryImport);
}
}
return Collections.emptyList();
}
return libraryImports;
}
private void startAddingLibrariesToProjectConfiguration(final IProgressMonitor monitor,
final List<RobotDryRunLibraryImport> libraryImports) {
if (!libraryImports.isEmpty()) {
final ImportedLibrariesConfigUpdater updater = new ImportedLibrariesConfigUpdater(robotProject);
final List<RobotDryRunLibraryImport> libraryImportsToAdd = updater.getLibraryImportsToAdd(libraryImports);
final SubMonitor subMonitor = SubMonitor.convert(monitor);
subMonitor.subTask("Adding libraries to project configuration...");
subMonitor.setWorkRemaining(libraryImportsToAdd.size() + 1);
for (final RobotDryRunLibraryImport libraryImport : libraryImportsToAdd) {
updater.addLibrary(libraryImport);
subMonitor.worked(1);
}
subMonitor.subTask("Updating project configuration...");
updater.finalizeLibrariesAdding(eventBroker);
subMonitor.worked(1);
}
}
private void showSummary(final Shell parent, final List<RobotDryRunLibraryImport> libraryImports) {
SwtThread.syncExec(new Runnable() {
@Override
public void run() {
new LibrariesAutoDiscovererWindow(parent, libraryImports).open();
}
});
}
private class DryRunTargetsCollector implements IDryRunTargetsCollector {
private final List<String> suiteNames = new ArrayList<>();
private final List<File> additionalProjectsLocations = new ArrayList<>();
@Override
public void collectSuiteNamesAndAdditionalProjectsLocations() {
final List<String> resourcesPaths = new ArrayList<>();
for (final IResource resource : suiteFiles) {
RobotSuiteFile suiteFile = null;
if (resource.getType() == IResource.FILE) {
suiteFile = RedPlugin.getModelManager().createSuiteFile((IFile) resource);
}
if (suiteFile != null && suiteFile.isResourceFile()) {
final IPath resourceFilePath = RedWorkspace.Paths
.toWorkspaceRelativeIfPossible(resource.getProjectRelativePath());
resourcesPaths.add(resourceFilePath.toString());
} else {
if (resource.isLinked()) {
collectLinkedSuiteNamesAndProjectsLocations(resource);
} else {
suiteNames.add(RobotSuitesNaming.createSuiteName(resource));
}
}
}
if (!resourcesPaths.isEmpty()) {
final File tempSuiteFileWithResources = dryRunHandler.createTempSuiteFile(resourcesPaths,
new ArrayList<String>());
if (tempSuiteFileWithResources != null) {
suiteNames.add(Files.getNameWithoutExtension(tempSuiteFileWithResources.getPath()));
additionalProjectsLocations.add(tempSuiteFileWithResources.getParentFile());
}
}
}
private void collectLinkedSuiteNamesAndProjectsLocations(final IResource resource) {
final IPath linkedFileLocation = resource.getLocation();
if (linkedFileLocation != null) {
final File linkedFile = linkedFileLocation.toFile();
if (linkedFile.exists()) {
suiteNames.add(Files.getNameWithoutExtension(linkedFile.getName()));
final File linkedFileParentPath = linkedFile.getParentFile();
if (!additionalProjectsLocations.contains(linkedFileParentPath)) {
additionalProjectsLocations.add(linkedFileParentPath);
}
}
}
}
@Override
public List<String> getSuiteNames() {
return suiteNames;
}
@Override
public List<File> getAdditionalProjectsLocations() {
return additionalProjectsLocations;
}
}
private static class ImportedLibrariesConfigUpdater extends LibrariesConfigUpdater {
ImportedLibrariesConfigUpdater(final RobotProject robotProject) {
super(robotProject);
}
List<RobotDryRunLibraryImport> getLibraryImportsToAdd(final List<RobotDryRunLibraryImport> libraryImports) {
final List<String> existingLibraryNames = new ArrayList<>();
for (final ReferencedLibrary existingLibrary : config.getLibraries()) {
existingLibraryNames.add(existingLibrary.getName());
}
final List<RobotDryRunLibraryImport> result = new ArrayList<>();
for (final RobotDryRunLibraryImport libraryImport : libraryImports) {
if (libraryImport.getType() != DryRunLibraryType.UNKNOWN) {
if (existingLibraryNames.contains(libraryImport.getName())) {
libraryImport.setStatus(DryRunLibraryImportStatus.ALREADY_EXISTING);
} else {
result.add(libraryImport);
}
}
}
return result;
}
void addLibrary(final RobotDryRunLibraryImport libraryImport) {
if (libraryImport.getType() == DryRunLibraryType.JAVA) {
addJavaLibrary(libraryImport);
} else if (libraryImport.getType() == DryRunLibraryType.PYTHON) {
addPythonLibrary(libraryImport);
}
}
private void addPythonLibrary(final RobotDryRunLibraryImport libraryImport) {
final PythonLibStructureBuilder pythonLibStructureBuilder = new PythonLibStructureBuilder(
robotProject.getRuntimeEnvironment(), robotProject.getRobotProjectConfig(),
robotProject.getProject());
try {
final Collection<ILibraryClass> libraryClasses = pythonLibStructureBuilder
.provideEntriesFromFile(libraryImport.getSourcePath(), libraryImport.getName());
addReferencedLibrariesFromClasses(libraryImport, libraryClasses);
} catch (final RobotEnvironmentException e) {
final Optional<File> modulePath = findPythonLibraryModulePath(libraryImport);
if (modulePath.isPresent()) {
final Path path = new Path(modulePath.get().getPath());
final ReferencedLibrary newLibrary = ReferencedLibrary.create(LibraryType.PYTHON,
libraryImport.getName(), path.toPortableString());
addLibraries(Collections.singletonList(newLibrary));
} else {
libraryImport.setStatus(DryRunLibraryImportStatus.NOT_ADDED);
libraryImport.setAdditionalInfo(e.getMessage());
}
}
}
private Optional<File> findPythonLibraryModulePath(final RobotDryRunLibraryImport libraryImport) {
try {
final EnvironmentSearchPaths envSearchPaths = new RedEclipseProjectConfig(config)
.createEnvironmentSearchPaths(robotProject.getProject());
return robotProject.getRuntimeEnvironment().getModulePath(libraryImport.getName(), envSearchPaths);
} catch (final RobotEnvironmentException e1) {
return Optional.empty();
}
}
private void addJavaLibrary(final RobotDryRunLibraryImport libraryImport) {
final JarStructureBuilder jarStructureBuilder = new JarStructureBuilder(
robotProject.getRuntimeEnvironment(), robotProject.getRobotProjectConfig(),
robotProject.getProject());
try {
final Collection<ILibraryClass> libraryClasses = jarStructureBuilder
.provideEntriesFromFile(libraryImport.getSourcePath());
addReferencedLibrariesFromClasses(libraryImport, libraryClasses);
} catch (final RobotEnvironmentException e) {
libraryImport.setStatus(DryRunLibraryImportStatus.NOT_ADDED);
libraryImport.setAdditionalInfo(e.getMessage());
}
}
private void addReferencedLibrariesFromClasses(final RobotDryRunLibraryImport libraryImport,
final Collection<ILibraryClass> libraryClasses) {
final Collection<ReferencedLibrary> librariesToAdd = new ArrayList<>();
for (final ILibraryClass libraryClass : libraryClasses) {
if (libraryClass.getQualifiedName().equalsIgnoreCase(libraryImport.getName())) {
librariesToAdd.add(libraryClass.toReferencedLibrary(libraryImport.getSourcePath()));
}
}
if (!librariesToAdd.isEmpty()) {
addLibraries(librariesToAdd);
} else {
libraryImport.setStatus(DryRunLibraryImportStatus.NOT_ADDED);
libraryImport.setAdditionalInfo("RED was unable to find class '" + libraryImport.getName()
+ "' inside '" + libraryImport.getSourcePath() + "' module.");
}
}
}
}