blob: e85c7caddc41d5151433fcc11f1e2201823454ab [file] [log] [blame]
/*
* Copyright (c) Robert Bosch GmbH. All rights reserved.
*/
package org.eclipse.blockchain.core;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.blockchain.core.events.BlockchainEnvironmentChangedTrigger;
import org.eclipse.blockchain.core.events.IBlockchainEnvironmentChangedEvent;
import org.eclipse.blockchain.core.events.IBlockchainEnvironmentChangedListener;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.InstanceScope;
/**
* @author ADG5COB
*/
public class ProjectCreator implements IResourceChangeListener, IBlockchainEnvironmentChangedListener {
private static ProjectCreator instance;
private final Map<String, EthereumProject> projectMap = new HashMap<>();
private String selectedEnvironment = "";
private ProjectCreator() {}
/**
* There will be only 1 Project creator instance which can be used to create multiple Ethereum projects
*
* @return - The Project creator instance
*/
public static ProjectCreator getInstance() {
if (instance == null) {
instance = new ProjectCreator();
ResourcesPlugin.getWorkspace().addResourceChangeListener(instance);
BlockchainEnvironmentChangedTrigger.getInstance().addBlockchainEnvironmentChangedListener(instance);
}
return instance;
}
/**
* This API is used to create an Ethereum project inside the workspace
*
* @param projectName - Name of the project
* @param projectPath
* @return - Project creation message if error occurs then it will return the error message else empty
* @throws IOException - If any problem with file access then this will be thrown
* @throws CoreException -
*/
public String createNewProject(final String projectName, final String projectPath) throws IOException, CoreException {
if (this.projectMap.containsKey(projectName)) {
return "A project with same name already exists in the workspace";
}
EthereumProject ethProject = new EthereumProject(projectPath + File.separator + projectName);
String directoryCreation = createFolderInWorkspaceForNewProject(ethProject);
if (directoryCreation.equals("")) {
// Contracts folder and sample contract
File contractsFolder = new File(ethProject.getProjectLocation() + File.separator + "contracts");
contractsFolder.mkdir();
File sampleSolFile = new File(contractsFolder.getAbsolutePath() + File.separator + "HelloWorld.sol");
if (!sampleSolFile.createNewFile()) {
throw new IOException(sampleSolFile.getPath());
}
try (
BufferedReader br = new BufferedReader(
new InputStreamReader(CoreCommandExecutor.class.getResourceAsStream("HelloWorld.template")));
BufferedWriter bw = new BufferedWriter(new FileWriter(sampleSolFile));) {
String sampleSolContent = "";
while ((sampleSolContent = br.readLine()) != null) {
bw.append(sampleSolContent);
bw.append(System.lineSeparator());
}
}
configureAndImportProject(ethProject, projectName, URIUtil.toURI(ethProject.getProjectLocation()));
this.projectMap.put(projectName, ethProject);
}
return directoryCreation;
}
/**
* To check whether the project has ethereum nature before importing into workspace
*
* @param projectPathString
* @return
*/
public String isItAnEthereumProject(final String projectPathString) {
// Check if sucha directory exists
File projectDirectory = new File(projectPathString);
if (projectDirectory.exists()) {
// valid directory
String[] splitProjPath = projectPathString.split("\\\\");
if (splitProjPath.length >= 2) {
// check if there is a .project in the location
File projDescriptorFile = new File(projectPathString + File.separator + ".project");
if (projDescriptorFile.exists()) {
IProjectDescription description;
try {
Path projectIPath = new Path(projectPathString);
IWorkspace workspace = ResourcesPlugin.getWorkspace();
description = workspace.loadProjectDescription(
projectIPath.append(IPath.SEPARATOR + IProjectDescription.DESCRIPTION_FILE_NAME));
String[] natureIds = description.getNatureIds();
for (String natureID : natureIds) {
if ((EthereumProjectNature.ETHEREUM_NATURE.equalsIgnoreCase(natureID))) {
return "";
}
}
return "Not Ethereum project";
}
catch (CoreException e) {
BlockchainCore.getInstance().logException("", e.getMessage(), e);
}
}
return "No Ethereum project exists at: " + projectPathString;
}
return "Invalid directory path: " + projectPathString;
}
return "Directory " + projectPathString + " does not exist";
}
/**
* @param projectPathString
* @return
*/
public String createExistingProject(final String projectPathString) {
// Check if sucha directory exists
File projectDirectory = new File(projectPathString);
if (projectDirectory.exists()) {
// valid directory
String[] splitProjPath = projectPathString.split("\\\\");
if (splitProjPath.length >= 2) {
String projName = splitProjPath[splitProjPath.length - 1];
EthereumProject ethProject = new EthereumProject(projectPathString);
// check if there is a .project in the location
File projDescriptorFile = new File(projectPathString + File.separator + ".project");
if (projDescriptorFile.exists()) {
return invokeProjectCreationWithDescription(projectPathString, projectDirectory, projName, ethProject);
}
return "No Ethereum project exists at: " + projectPathString;
}
return "Invalid directory path: " + projectPathString;
}
// directory doesn't exist. Should we create?
return "Directory " + projectPathString + " does not exist";
}
/**
* @param projectPathString
* @param projectDirectory
* @param projName
* @param ethProject
* @return
*/
private String invokeProjectCreationWithDescription(final String projectPathString, final File projectDirectory,
final String projName, final EthereumProject ethProject) {
// project exists at this location
IProgressMonitor monitor = new NullProgressMonitor();
Path projectIPath = new Path(projectPathString);
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProjectDescription description;
try {
description = workspace
.loadProjectDescription(projectIPath.append(IPath.SEPARATOR + IProjectDescription.DESCRIPTION_FILE_NAME));
// String[] natureIds = description.getNatureIds();
// boolean hasEthereumNature = false;
// for (String natureID : natureIds) {
// if ((EthereumProjectNature.ETHEREUM_NATURE.equalsIgnoreCase(natureID))) {
// hasEthereumNature = true;
// break;
// }
// }
// if (!hasEthereumNature) {
// String[] newNatureIds = new String[natureIds.length + 1];
// for (int z = 0; z < natureIds.length; z++) {
// newNatureIds[z] = natureIds[z];
// }
// newNatureIds[newNatureIds.length - 1] = EthereumProjectNature.ETHEREUM_NATURE;
// description.setNatureIds(newNatureIds);
// }
createProjectWithDescription(projectDirectory, projName, ethProject, monitor, projectIPath, workspace,
description);
}
catch (CoreException e) {
BlockchainCore.getInstance().logException(BlockchainCore.PLUGIN_ID, e.getMessage(), e);
return "Exception occured during project creation";
}
// Should we check if the project has at least one solc file? If not create contracts folder and add file?
return "";
}
/**
* @param projectDirectory
* @param projName
* @param ethProject
* @param monitor
* @param projectIPath
* @param workspace
* @param description
* @throws CoreException
*/
private void createProjectWithDescription(final File projectDirectory, final String projName,
final EthereumProject ethProject, final IProgressMonitor monitor, final Path projectIPath,
final IWorkspace workspace, final IProjectDescription description)
throws CoreException {
IProject project;
if (!description.getName().equals(projectDirectory.getName())) {
project = workspace.getRoot().getProject(projectDirectory.getName());
}
else {
project = workspace.getRoot().getProject(description.getName());
}
if (Platform.getLocation().isPrefixOf(projectIPath)) {
description.setLocation(null);
}
else {
description.setLocation(projectIPath);
}
ethProject.setProjectDescription(description);
ethProject.setProject(project);
boolean isProjectExists = project.exists();
if (!isProjectExists) {
project.create(description, 0, monitor);
}
project.open(monitor);
if (!project.getDescription().hasNature(EthereumProjectNature.ETHEREUM_NATURE)) {
IProjectDescription description2 = project.getDescription();
String[] natureIds = description2.getNatureIds();
String[] newNatureIds = new String[natureIds.length + 1];
for (int z = 0; z < natureIds.length; z++) {
newNatureIds[z] = natureIds[z];
}
newNatureIds[newNatureIds.length - 1] = EthereumProjectNature.ETHEREUM_NATURE;
description2.setNatureIds(newNatureIds);
project.setDescription(description2, monitor);
}
if (isProjectExists) {
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
this.projectMap.put(projName, ethProject);
}
/**
* @param projectName -
* @return -
* @throws CoreException -
*/
public IFile getFirstMatchingSolFile(final String projectName) throws CoreException {
return this.projectMap.get(projectName).getSolFile();
}
/**
* This is used to configure and import a ethereum project into prduct
*
* @param projectName - Project name
* @param projectURI - The location URI of the project contents
* @throws CoreException -
* @throws IOException -
*/
private void configureAndImportProject(final EthereumProject ethProject, final String projectName,
final URI projectURI)
throws CoreException, IOException {
ethProject.setProjectDescription(ResourcesPlugin.getWorkspace().newProjectDescription(projectName));
ethProject.getProjectDescription().setLocationURI(projectURI);
ethProject.addDefaultNatures();
// Validate and set nature
String[] natureIds = ethProject.getProjectDescription().getNatureIds();
String[] newNatures = new String[natureIds.length + ethProject.getNatureIds().length];
int i = natureIds.length;
for (String n : ethProject.getNatureIds()) {
newNatures[i++] = n;
}
if (IStatus.OK == ResourcesPlugin.getWorkspace().validateNatureSet(newNatures).getCode()) {
ethProject.getProjectDescription().setNatureIds(newNatures);
}
else {
BlockchainCore.getInstance().logException("", "Problem configuring natures",
new Exception(Arrays.toString(ethProject.getNatureIds())));
}
ethProject.setProject(ResourcesPlugin.getWorkspace().getRoot().getProject(projectName));
ethProject.getProject().create(ethProject.getProjectDescription(), new NullProgressMonitor());
ethProject.getProject().open(new NullProgressMonitor());
createPackageJsonDepFile(ethProject);
}
private String createFolderInWorkspaceForNewProject(final EthereumProject ethProject) {
File projectDirectory = new File(ethProject.getProjectLocation());
if (projectDirectory.mkdir()) {
return "";
}
return "Some Problem in directory creation, please check the location " + ethProject.getProjectLocation();
}
private void createPackageJsonDepFile(final EthereumProject ethProject) throws IOException {
File packageJson = new File(ethProject.getProject().getLocation().toOSString() + IPath.SEPARATOR + "package.json");
if (!packageJson.createNewFile()) {
throw new IOException(packageJson.getPath());
}
}
/**
* {@inheritDoc} On deletion of project from workspace it should be removed from project map as-well.
*/
@Override
public void resourceChanged(final IResourceChangeEvent event) {
IResource resource = event.getResource();
if ((resource != null) && (resource instanceof IProject) && (event.getType() == IResourceChangeEvent.PRE_DELETE) &&
this.projectMap.containsKey(resource.getName())) {
this.projectMap.remove(resource.getName());
}
else {
IResourceDelta rootDelta = event.getDelta();
try {
rootDelta.accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(final IResourceDelta delta) throws CoreException {
IResource resource2 = delta.getResource();
if ((resource2 instanceof IFile) && (delta.getResource().getFileExtension() != null) &&
(resource2.getFileExtension().equals("sol")) && (delta.getKind() == IResourceDelta.REMOVED) &&
(getEthProject(resource2.getProject().getName()) != null)) {
getEthProject(resource2.getProject().getName()).getCompiledSolidityFiles().remove(resource2);
getEthProject(resource2.getProject().getName()).removeDeploymentModelForResource(resource2,
getSelectedEnvironment());
}
return true;
}
});
}
catch (CoreException e) {
BlockchainCore.getInstance().logException("", e.getMessage(), e);
}
}
}
/**
* @param projectName
* @return
*/
public EthereumProject getEthProject(final String projectName) {
return this.projectMap.get(projectName);
}
private String getSelectedEnvironment() {
if (!this.selectedEnvironment.isEmpty()) {
return this.selectedEnvironment;
}
return this.selectedEnvironment = InstanceScope.INSTANCE.getNode(SecoBlocksPreferenceConstants.SECOBLOCKS_PREF_NODE)
.get(SecoBlocksPreferenceConstants.ENVIRONMENT_PREF_KEY,
SecoBlocksPreferenceConstants.EnvironmentType.EMBEDDED_EVM.toString());
}
/**
* {@inheritDoc}
*/
@Override
public void blockchainEnvironmentChanged(final IBlockchainEnvironmentChangedEvent event) {
this.selectedEnvironment = event.getActiveEvironment();
}
}