/* | |
* 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(); | |
} | |
} |