blob: 402771338433b731eabed214a2a7b733d3ec6bf9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Sierra Wireless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.ldt.core.internal.buildpath;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathContainer;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.ldt.core.buildpath.LuaExecutionEnvironment;
import org.eclipse.ldt.core.internal.Activator;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import com.naef.jnlua.LuaException;
import com.naef.jnlua.LuaState;
public final class LuaExecutionEnvironmentManager {
private static final String EXTENSION_POINT_ID = "org.eclipse.ldt.executionEnvironment"; //$NON-NLS-1$
private static final String ATTRIBUTE_ID = "id"; //$NON-NLS-1$
private static final String ATTRIBUTE_VERSION = "version"; //$NON-NLS-1$
private static final String ATTRIBUTE_RESOURCEDIRECTORY = "resourcedirectory"; //$NON-NLS-1$
private static final String MANIFEST_NAME = "package"; //$NON-NLS-1$
private static final String MANIFEST_VERSION = "version"; //$NON-NLS-1$
private static final String MANIFEST_GRAMMAR = "grammar"; //$NON-NLS-1$
private static final String MANIFEST_TEMPLATES = "templates"; //$NON-NLS-1$
private static final String INSTALLATION_FOLDER = "ee"; //$NON-NLS-1$
private LuaExecutionEnvironmentManager() {
}
/**
* Detect if an installed {@link LuaExecutionEnvironment} follows current recommendations. Useful for UI warnings.
*
* @param ee
* Execution Environment to check
* @return null when everything is all right, warning {@link String} else way.
*/
public static String check(final LuaExecutionEnvironment ee) {
// Check if default template exists
if (ee.getTemplatesPath() != null) {
if (ee.getDefaultTemplatePath() == null || !ee.getDefaultTemplatePath().toFile().exists())
return NLS.bind(Messages.LuaExecutionEnvironmentManagerNoDefaultTemplate, ee.getEEIdentifier());
}
// Check if old template folder is being used
if (ee.getOldTemplatePath() != null)
return NLS.bind(Messages.LuaExecutionEnvironmentManagerLegacyTemplateFolder, ee.getEEIdentifier());
// All good
return null;
}
public static LuaExecutionEnvironment getExecutionEnvironmentFromCompressedFile(final String filePath) throws CoreException {
/*
* Extract manifest file
*/
ZipFile zipFile = null;
String manifestString = null;
try {
zipFile = new ZipFile(filePath);
final Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
final ZipEntry zipEntry = zipEntries.nextElement();
if ((!zipEntry.getName().contains("/")) && zipEntry.getName().endsWith(LuaExecutionEnvironmentConstants.MANIFEST_EXTENSION)) { //$NON-NLS-1$
// check there are only one manifest.
if (manifestString != null) {
throwException(
MessageFormat
.format("Invalid Execution Environment : more than one \"{0}\" file.", LuaExecutionEnvironmentConstants.MANIFEST_EXTENSION), null, IStatus.ERROR); //$NON-NLS-1$
}
// read manifest
final InputStream input = zipFile.getInputStream(zipEntry);
manifestString = IOUtils.toString(input);
}
}
} catch (IOException e) {
throwException(MessageFormat.format("Unable to extract manifest from zip file {0}", filePath), e, IStatus.ERROR); //$NON-NLS-1$
} finally {
if (zipFile != null)
try {
zipFile.close();
} catch (IOException e) {
Activator.logWarning(MessageFormat.format("Unable to close zip file {0}", filePath), e); //$NON-NLS-1$
}
}
// if no manifest extract
if (manifestString == null) {
throwException(MessageFormat.format("No manifest \"{0}\" file found", //$NON-NLS-1$
LuaExecutionEnvironmentConstants.MANIFEST_EXTENSION), null, IStatus.ERROR);
}
return getLuaExecutionEnvironmentFromManifest(manifestString, null);
}
private static LuaExecutionEnvironment getExecutionEnvironmentFromDir(final File executionEnvironmentDirectory) throws CoreException {
// check if the directory exist
if (!executionEnvironmentDirectory.exists() || !executionEnvironmentDirectory.isDirectory())
return null;
// list manifest files
String manifestString = null;
File[] manifests = executionEnvironmentDirectory.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(LuaExecutionEnvironmentConstants.MANIFEST_EXTENSION);
}
});
// check number of manifest file
if (manifests == null || manifests.length != 1) {
final String message = MessageFormat.format("0 or more than 1 \"{0}\" file in given file.", //$NON-NLS-1$
LuaExecutionEnvironmentConstants.MANIFEST_EXTENSION);
throwException(message, null, IStatus.ERROR);
}
// try to read it
InputStream manifestInputStream = null;
try {
manifestInputStream = new FileInputStream(manifests[0]);
manifestString = IOUtils.toString(manifestInputStream);
} catch (IOException e) {
throwException("Unable to read manifest file.", e, IStatus.ERROR); //$NON-NLS-1$
} finally {
if (manifestInputStream != null)
try {
manifestInputStream.close();
} catch (IOException e) {
Activator.logWarning(MessageFormat.format("Unable to close file {0}", manifests[0]), e); //$NON-NLS-1$
}
}
// extract execution environment from manifest
return getLuaExecutionEnvironmentFromManifest(manifestString, new Path(executionEnvironmentDirectory.getPath()));
}
// create a map which store all this data in the JVM
private static Map<?, ?> createJavaCopy(Map<?, ?> mapToCopy) {
HashMap<Object, Object> result = new HashMap<Object, Object>();
for (Entry<?, ?> entry : mapToCopy.entrySet()) {
// manage key
Object key = entry.getKey();
Object newKey = null;
if (key instanceof String || key instanceof Number || key instanceof Character) {
newKey = key;
} else if (key instanceof Map<?, ?>) {
newKey = createJavaCopy((Map<?, ?>) key);
}
// manage value
Object value = entry.getValue();
Object newValue = null;
if (value instanceof String || value instanceof Number || value instanceof Character) {
newValue = value;
} else if (value instanceof Map<?, ?>) {
newValue = createJavaCopy((Map<?, ?>) value);
}
// add the key/value
if (newKey == null || newValue == null) {
Activator.logWarning(MessageFormat.format(
"An execution environment contains invalid map in manifest : unexpected key/value {0}/{1}", key, value)); //$NON-NLS-1$
} else {
result.put(newKey, newValue);
}
}
return result;
}
private static LuaExecutionEnvironment getLuaExecutionEnvironmentFromManifest(String manifestString, final IPath installDirectory)
throws CoreException {
// execute the manifest
LuaState luaState = new LuaState();
try {
luaState.load(manifestString, "Lua error:"); //$NON-NLS-1$
luaState.call(0, 0);
// Retrieve field as global variables
luaState.getGlobal(MANIFEST_NAME);
String name = luaState.toString(-1);
luaState.getGlobal(MANIFEST_VERSION);
String version = luaState.toString(-1);
luaState.getGlobal(MANIFEST_GRAMMAR);
String grammar = luaState.toString(-1);
// Create object representing a valid Execution Environment
if (name == null || version == null) {
throwException("Manifest from given file has no package name or version.", null, IStatus.ERROR); //$NON-NLS-1$
}
Map<?, ?> templates = null;
luaState.getGlobal(MANIFEST_TEMPLATES);
Map<?, ?> luatemplates = luaState.toJavaObject(-1, Map.class);
if (luatemplates != null)
templates = createJavaCopy(luatemplates);
return new LuaExecutionEnvironment(name, version, templates, installDirectory, grammar);
} catch (LuaException e) {
luaState.close();
throwException("Error while loading the manifest", e, IStatus.ERROR); //$NON-NLS-1$
} catch (ClassCastException e) {
throwException("Unable to parse the templates attribute in the EE manifest", e, IStatus.ERROR); //$NON-NLS-1$
} finally {
luaState.close();
}
return null;
}
private static LuaExecutionEnvironment getExecutionEnvironmentFromContribution(IConfigurationElement contribution) throws CoreException {
String resourceDirectory = contribution.getAttribute(ATTRIBUTE_RESOURCEDIRECTORY);
String contributor = contribution.getContributor().getName();
Bundle bundle = Platform.getBundle(contributor);
if (bundle != null && resourceDirectory != null) {
// get execution environment directory
URL entry = bundle.getEntry(resourceDirectory);
try {
URL fileURL = FileLocator.toFileURL(entry);
File file = new File(fileURL.getFile());
if (file.exists()) {
LuaExecutionEnvironment embeddedEE = LuaExecutionEnvironmentManager.getExecutionEnvironmentFromDir(file);
embeddedEE.setEmbedded(true);
return embeddedEE;
}
} catch (IOException e) {
throwException(
MessageFormat.format("Unable to extract embedded execution environment from {0} - {1}", bundle, resourceDirectory), e, IStatus.ERROR); //$NON-NLS-1$
} catch (CoreException e) {
throwException(
MessageFormat.format("Unable to extract embedded execution environment from {0} - {1}", bundle, resourceDirectory), e, IStatus.ERROR); //$NON-NLS-1$
}
}
return null;
}
public static void uninstallLuaExecutionEnvironment(final LuaExecutionEnvironment ee) throws CoreException {
if (ee == null)
throwException("No Execution Environment provided.", null, IStatus.ERROR); //$NON-NLS-1$
if (ee.isEmbedded())
throwException("Embedded Execution Environment could not be uninstalled.", null, IStatus.ERROR); //$NON-NLS-1$
final IPath pathToEE = ee.getPath();
if (pathToEE == null)
throwException("The install path should not be null", null, IStatus.ERROR); //$NON-NLS-1$
final File eeInstallationDir = pathToEE.toFile();
if (eeInstallationDir.exists()) {
try {
FileUtils.deleteDirectory(eeInstallationDir);
refreshDLTKModel(ee);
} catch (final IOException e) {
throwException(MessageFormat.format("Unable to delete install directory : {0}", pathToEE.toOSString()), e, IStatus.ERROR); //$NON-NLS-1$
}
}
}
/**
* Will deploy files from a valid Execution Environment file in installation directory. File will be considered as installed when its name will be
* appended in {@link LuaExecutionEnvironmentConstants#PREF_EXECUTION_ENVIRONMENTS_LIST}
*
* @param zipPath
* Path to file to deploy
* @return {@link LuaExecutionEnvironmentException} when deployment succeeded.
* @throws CoreException
*/
public static LuaExecutionEnvironment installLuaExecutionEnvironment(final String zipPath) throws CoreException {
/*
* Ensure there are no folder named like the one we are about to create
*/
LuaExecutionEnvironment ee = null;
ee = getExecutionEnvironmentFromCompressedFile(zipPath);
if (ee == null)
throwException(MessageFormat.format("Unable to extract execution environment information from {0}.", zipPath), null, IStatus.ERROR); //$NON-NLS-1$
// check if it is already installed
if (getInstalledExecutionEnvironments().contains(ee)) {
throwException("Execution environment is already installed.", null, IStatus.ERROR); //$NON-NLS-1$
}
// prepare/clean the directory where the Execution environment will be installed
final IPath eePath = getInstallDirectory().append(ee.getEEIdentifier());
final File installDirectory = eePath.toFile();
// clean install directory if it exists
if (installDirectory.exists()) {
if (installDirectory.isFile()) {
if (!installDirectory.delete())
throwException(MessageFormat.format("Unable to clean installation directory : {0}", eePath.toOSString()), null, IStatus.ERROR); //$NON-NLS-1$
} else {
try {
FileUtils.deleteDirectory(installDirectory);
} catch (IOException e) {
throwException(MessageFormat.format("Unable to clean installation directory : {0}", eePath.toOSString()), e, IStatus.ERROR); //$NON-NLS-1$
}
}
}
// in all case create the install directory
if (!installDirectory.mkdirs()) {
throwException(MessageFormat.format("Unable to create installation directory : {0}", eePath.toOSString()), null, IStatus.ERROR); //$NON-NLS-1$
}
// Extract Execution environment from zip
// Loop over content
ZipInputStream zipStream = null;
FileInputStream input = null;
try {
// Open given file
input = new FileInputStream(zipPath);
zipStream = new ZipInputStream(new BufferedInputStream(input));
for (ZipEntry entry = zipStream.getNextEntry(); entry != null; entry = zipStream.getNextEntry()) {
/*
* Flush current file on disk
*/
final File outputFile = new File(installDirectory, entry.getName());
// Define output file
if (entry.isDirectory()) {
// Create sub directory if needed
if (!outputFile.mkdir()) {
throwException(MessageFormat.format("Unable to create install directory {0}", outputFile.toString()), null, IStatus.ERROR); //$NON-NLS-1$
}
} else {
// copy file
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = FileUtils.openOutputStream(outputFile);
// Inflate file
IOUtils.copy(zipStream, fileOutputStream);
// Flush on disk
fileOutputStream.flush();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
Activator.logWarning(MessageFormat.format("Unable to close file {0}", outputFile), e); //$NON-NLS-1$
}
}
}
}
}
} catch (IOException e) {
throwException(MessageFormat.format("Unable to extract zip file : {0}", zipPath), e, IStatus.ERROR); //$NON-NLS-1$
} finally {
/*
* Make sure all streams are closed
*/
if (input != null) {
try {
input.close();
} catch (IOException e) {
Activator.logWarning(MessageFormat.format("Unable to close file {0}", zipPath), e); //$NON-NLS-1$
}
}
if (zipStream != null) {
try {
zipStream.close();
} catch (IOException e) {
Activator.logWarning(MessageFormat.format("Unable to close file {0}", zipPath), e); //$NON-NLS-1$
}
}
}
// try to get installed Execution Environment to be sure, it is well installed
ee = getInstalledExecutionEnvironment(ee.getID(), ee.getVersion());
refreshDLTKModel(ee);
return ee;
}
private static IPath getInstallDirectory() {
return Activator.getDefault().getStateLocation().append(INSTALLATION_FOLDER);
}
public static List<LuaExecutionEnvironment> getInstalledExecutionEnvironments() {
// list of execution environment installed
final ArrayList<LuaExecutionEnvironment> result = new ArrayList<LuaExecutionEnvironment>();
// search in the install directory
IPath installDirectoryPath = getInstallDirectory();
File installDirectory = installDirectoryPath.toFile();
if (installDirectory.exists() && installDirectory.isDirectory()) {
File[] content = installDirectory.listFiles();
for (File executionEnvironmentDirectory : content) {
if (executionEnvironmentDirectory.exists() && executionEnvironmentDirectory.isDirectory()) {
LuaExecutionEnvironment executionEnvironment;
try {
executionEnvironment = getExecutionEnvironmentFromDir(executionEnvironmentDirectory);
if (executionEnvironment != null)
result.add(executionEnvironment);
} catch (CoreException e) {
Activator.logWarning(MessageFormat.format("Unable to extract execution environment from {0}", executionEnvironmentDirectory), //$NON-NLS-1$
e);
}
}
}
}
return result;
}
public static List<LuaExecutionEnvironment> getEmbeddedExecutionEnvironments() {
// list of execution environment installed
final ArrayList<LuaExecutionEnvironment> result = new ArrayList<LuaExecutionEnvironment>();
// search plug-in contribution
IConfigurationElement[] contributions = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID);
for (int i = 0; i < contributions.length; i++) {
try {
LuaExecutionEnvironment embeddedEE = getExecutionEnvironmentFromContribution(contributions[i]);
result.add(embeddedEE);
} catch (CoreException e) {
Activator.log(e.getStatus());
}
}
return result;
}
/**
* List all the installed and contributed EEs. To have only activities enabled EE see LuaExecutionEnvironmentUIManager
*
*/
public static List<LuaExecutionEnvironment> getAvailableExecutionEnvironments() {
List<LuaExecutionEnvironment> availableExecutionEnvironments = getInstalledExecutionEnvironments();
List<LuaExecutionEnvironment> embeddedExecutionEnvironments = getEmbeddedExecutionEnvironments();
// add embedded execution environments only if there are not an installed version.
for (LuaExecutionEnvironment luaExecutionEnvironment : embeddedExecutionEnvironments) {
if (!availableExecutionEnvironments.contains(luaExecutionEnvironment)) {
availableExecutionEnvironments.add(luaExecutionEnvironment);
}
}
return availableExecutionEnvironments;
}
private static IPath getLuaExecutionEnvironmentPath(final String id, final String version) {
return getInstallDirectory().append(NLS.bind("{0}-{1}", id, version)); //$NON-NLS-1$
}
public static LuaExecutionEnvironment getInstalledExecutionEnvironment(String name, String version) throws CoreException {
IPath luaExecutionEnvironmentPath = getLuaExecutionEnvironmentPath(name, version);
return getExecutionEnvironmentFromDir(luaExecutionEnvironmentPath.toFile());
}
public static LuaExecutionEnvironment getEmbeddedExecutionEnvironment(String name, String version) throws CoreException {
// search plug-in contribution
IConfigurationElement[] contributions = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID);
for (int i = 0; i < contributions.length; i++) {
String embeddedID = contributions[i].getAttribute(ATTRIBUTE_ID);
String embeddedVersion = contributions[i].getAttribute(ATTRIBUTE_VERSION);
if (name != null && name.equals(embeddedID) && version != null && version.equals(embeddedVersion)) {
LuaExecutionEnvironment embeddedEE = getExecutionEnvironmentFromContribution(contributions[i]);
if (embeddedEE != null)
return embeddedEE;
}
}
return null;
}
public static LuaExecutionEnvironment getAvailableExecutionEnvironment(String name, String version) throws CoreException {
// search in installed execution environments
LuaExecutionEnvironment ee = getInstalledExecutionEnvironment(name, version);
if (ee != null)
return ee;
// if not found, search in embedded environments
return getEmbeddedExecutionEnvironment(name, version);
}
private static void refreshDLTKModel(LuaExecutionEnvironment ee) {
try {
// get path for this execution environment
IPath containerPath = LuaExecutionEnvironmentBuildpathUtil.getLuaExecutionEnvironmentContainerPath(ee);
// find all project which references it
IScriptProject[] scriptProjects = DLTKCore.create(ResourcesPlugin.getWorkspace().getRoot()).getScriptProjects();
ArrayList<IScriptProject> affectedProjects = new ArrayList<IScriptProject>();
for (int i = 0; i < scriptProjects.length; i++) {
IScriptProject scriptProject = scriptProjects[i];
IBuildpathEntry[] entries = scriptProject.getRawBuildpath();
for (int j = 0; j < entries.length; j++) {
IBuildpathEntry entry = entries[j];
if (entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER) {
if (containerPath.equals(entry.getPath())) {
affectedProjects.add(scriptProject);
break;
}
}
}
}
// update affected projects
int length = affectedProjects.size();
if (length == 0)
return;
IScriptProject[] projects = new IScriptProject[length];
affectedProjects.toArray(projects);
IBuildpathContainer[] containers = new IBuildpathContainer[length];
LuaExecutionEnvironmentBuildpathContainer container = new LuaExecutionEnvironmentBuildpathContainer(ee.getID(), ee.getVersion(),
containerPath);
for (int i = 0; i < length; i++) {
containers[i] = container;
}
DLTKCore.setBuildpathContainer(containerPath, projects, containers, null);
} catch (ModelException e) {
Activator.logError("Unable to refresh Model after Execution Environment change.", e); //$NON-NLS-1$
}
}
private static void throwException(String message, Throwable t, int severity) throws CoreException {
throw new CoreException(new Status(severity, Activator.PLUGIN_ID, message, t));
}
}