/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.eclipse.runtime;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.internal.launching.LaunchingMessages;
import org.eclipse.jdt.launching.PropertyChangeEvent;
import org.osgi.framework.Version;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import io.sarl.eclipse.SARLEclipseConfig;
import io.sarl.eclipse.SARLEclipsePlugin;
/**
* The central access point for launching support. This class manages
* the registered SRE types contributed through the
* extension point with the name {@link SARLEclipseConfig#EXTENSION_POINT_SARL_RUNTIME_ENVIRONMENT}.
* As well, this class provides SRE install change notification.
*
* <p>This class was inspired from <code>JavaRuntime</code>.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @noinstantiate This class is not intended to be instantiated by clients.
*/
@SuppressWarnings({"checkstyle:classfanoutcomplexity", "checkstyle:classdataabstractioncoupling"})
public final class SARLRuntime {
/**
* Preference key for the String of XML that defines all installed SREs.
*/
public static final String DEFAULT_PREFERENCE_KEY = SARLEclipsePlugin.PLUGIN_ID + ".runtime.PREF_SRE_XML"; //$NON-NLS-1$
/** Flag that enables to turn on/off the SRE extensions points.
* This flag is mainly used for unit tests. It is not expected to be changed outside unit tests.
*/
private static boolean enableSreExtensionPoints = true;
private static String currentPreferenceKey = DEFAULT_PREFERENCE_KEY;
/**
* SRE change listeners.
*/
private static final ListenerList<ISREInstallChangedListener> SRE_LISTENERS = new ListenerList<>();
private static final Map<String, ISREInstall> ALL_SRE_INSTALLS = new HashMap<>();
private static Set<String> platformSREInstalls;
private static String defaultSREId;
private static final ReentrantLock LOCK = new ReentrantLock();
private SARLRuntime() {
//
}
/** Replies the key used for storing the SARL runtime configuration
* into the preferences.
*
* @return the current preference key.
*/
public static String getCurrentPreferenceKey() {
LOCK.lock();
try {
return currentPreferenceKey;
} finally {
LOCK.unlock();
}
}
/** Change the key used for storing the SARL runtime configuration
* into the preferences.
*
* <p>If the given key is <code>null</code> or empty, the preference key
* is reset to the {@link #DEFAULT_PREFERENCE_KEY}.
*
* @param key - the new key or <code>null</code>.
*/
public static void setCurrentPreferenceKey(String key) {
LOCK.lock();
try {
currentPreferenceKey = (Strings.isNullOrEmpty(key)) ? DEFAULT_PREFERENCE_KEY : key;
} finally {
LOCK.unlock();
}
}
/**
* Adds the given listener to the list of registered SRE install changed
* listeners. Has no effect if an identical listener is already registered.
*
* @param listener - the listener to add
*/
public static void addSREInstallChangedListener(ISREInstallChangedListener listener) {
SRE_LISTENERS.add(listener);
}
/**
* Removes the given listener from the list of registered SRE install changed
* listeners. Has no effect if an identical listener is not already registered.
*
* @param listener - the listener to remove
*/
public static void removeSREInstallChangedListener(ISREInstallChangedListener listener) {
SRE_LISTENERS.remove(listener);
}
/**
* Notifies all SRE install changed listeners of the given property change.
*
* @param event - event describing the change.
*/
public static void fireSREChanged(PropertyChangeEvent event) {
for (final Object listener : SRE_LISTENERS.getListeners()) {
((ISREInstallChangedListener) listener).sreChanged(event);
}
}
/**
* Notifies all SRE install changed listeners of the addition of a SRE.
*
* @param installation - the installed SRE.
*/
public static void fireSREAdded(ISREInstall installation) {
for (final Object listener : SRE_LISTENERS.getListeners()) {
((ISREInstallChangedListener) listener).sreAdded(installation);
}
}
/**
* Notifies all SRE install changed listeners of the removed of a SRE.
*
* @param installation - the removed SRE.
*/
public static void fireSRERemoved(ISREInstall installation) {
for (final Object listener : SRE_LISTENERS.getListeners()) {
((ISREInstallChangedListener) listener).sreRemoved(installation);
}
}
/**
* Return the SRE corresponding to the specified Id.
*
* @param id - the id that specifies an instance of ISREInstall
* @return the SRE corresponding to the specified Id, or <code>null</code>.
*/
public static ISREInstall getSREFromId(String id) {
if (Strings.isNullOrEmpty(id)) {
return null;
}
initializeSREs();
LOCK.lock();
try {
return ALL_SRE_INSTALLS.get(id);
} finally {
LOCK.unlock();
}
}
/**
* Returns the default SRE id determined during the initialization of the SRE types.
*
* @return the id of the default SRE, or <code>null</code> if none.
*/
private static String getDefaultSREId() {
LOCK.lock();
try {
initializeSREs();
return defaultSREId;
} finally {
LOCK.unlock();
}
}
/**
* Return the default SRE set with <code>setDefaultSRE()</code>.
*
* @return Returns the default SRE. May return <code>null</code> when no default
* SRE was set or when the default SRE has been disposed.
*/
public static ISREInstall getDefaultSREInstall() {
final ISREInstall install = getSREFromId(getDefaultSREId());
if (install != null && install.getValidity().isOK()) {
return install;
}
// if the default SRE goes missing, re-detect
LOCK.lock();
try {
//defaultSREId = null;
initializeSREs();
} finally {
LOCK.unlock();
}
return getSREFromId(getDefaultSREId());
}
/**
* Sets the installed SREs.
*
* @param sres - The installed SREs.
* @param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
* to call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
* reported and that the operation cannot be cancelled.
* @throws CoreException if trying to set the default SRE install encounters problems
*/
@SuppressWarnings("checkstyle:npathcomplexity")
public static void setSREInstalls(ISREInstall[] sres, IProgressMonitor monitor) throws CoreException {
final SubMonitor mon = SubMonitor.convert(monitor,
io.sarl.eclipse.runtime.Messages.SARLRuntime_0,
sres.length * 2 + ALL_SRE_INSTALLS.size());
initializeSREs();
final String oldDefaultId;
String newDefaultId;
final List<ISREInstall> newElements = new ArrayList<>();
final Map<String, ISREInstall> allKeys;
LOCK.lock();
try {
oldDefaultId = getDefaultSREId();
newDefaultId = oldDefaultId;
allKeys = new TreeMap<>(ALL_SRE_INSTALLS);
for (final ISREInstall sre : sres) {
if (allKeys.remove(sre.getId()) == null) {
newElements.add(sre);
ALL_SRE_INSTALLS.put(sre.getId(), sre);
}
mon.worked(1);
}
for (final ISREInstall sre : allKeys.values()) {
ALL_SRE_INSTALLS.remove(sre.getId());
platformSREInstalls.remove(sre.getId());
mon.worked(1);
}
if (oldDefaultId != null && !ALL_SRE_INSTALLS.containsKey(oldDefaultId)) {
newDefaultId = null;
}
} finally {
LOCK.unlock();
}
boolean changed = false;
mon.subTask(io.sarl.eclipse.runtime.Messages.SARLRuntime_1);
if (oldDefaultId != null && newDefaultId == null) {
changed = true;
setDefaultSREInstall(null, monitor);
}
mon.worked(1);
mon.subTask(io.sarl.eclipse.runtime.Messages.SARLRuntime_2);
for (final ISREInstall sre : allKeys.values()) {
changed = true;
fireSRERemoved(sre);
}
for (final ISREInstall sre : newElements) {
changed = true;
fireSREAdded(sre);
}
mon.worked(1);
if (changed) {
saveSREConfiguration(mon.newChild(sres.length - 2));
}
}
/**
* Sets a SRE as the system-wide default SRE, and notifies registered SRE install
* change listeners of the change.
*
* @param sre - The SRE to make the default. May be <code>null</code> to clear
* the default.
* @param monitor - progress monitor or <code>null</code>
* @throws CoreException if trying to set the default SRE install encounters problems
*/
public static void setDefaultSREInstall(ISREInstall sre, IProgressMonitor monitor) throws CoreException {
setDefaultSREInstall(sre, monitor, true);
}
/**
* Sets a SRE as the system-wide default SRE, and notifies registered SRE install
* change listeners of the change.
*
* @param sre - The SRE to make the default. May be <code>null</code> to clear
* the default.
* @param monitor - progress monitor or <code>null</code>
* @param savePreference - If <code>true</code>, update workbench preferences to reflect
* the new default SRE.
* @throws CoreException if trying to set the default SRE install encounters problems
*/
public static void setDefaultSREInstall(ISREInstall sre, IProgressMonitor monitor,
boolean savePreference) throws CoreException {
initializeSREs();
ISREInstall previous = null;
ISREInstall current = null;
LOCK.lock();
try {
if (defaultSREId != null) {
previous = getSREFromId(defaultSREId);
}
defaultSREId = sre == null ? null : sre.getId();
if (savePreference) {
saveSREConfiguration(monitor);
}
if (defaultSREId != null) {
current = getSREFromId(defaultSREId);
}
} finally {
LOCK.unlock();
}
if (previous != current) {
fireDefaultSREChanged(previous, current);
}
}
/**
* Notifies registered listeners that the default SRE has changed.
*
* @param previous - the previous SRE
* @param current - the new current default SRE
*/
private static void fireDefaultSREChanged(ISREInstall previous, ISREInstall current) {
for (final Object listener : SRE_LISTENERS.getListeners()) {
((ISREInstallChangedListener) listener).defaultSREInstallChanged(previous, current);
}
}
/**
* Returns the list of registered SREs. SRE types are registered via
* extension point with the name {@link SARLEclipseConfig#EXTENSION_POINT_SARL_RUNTIME_ENVIRONMENT}.
* Returns an empty list if there are no registered SREs.
*
* @return the list of registered SREs.
*/
public static ISREInstall[] getSREInstalls() {
initializeSREs();
LOCK.lock();
try {
return ALL_SRE_INSTALLS.values().toArray(new ISREInstall[ALL_SRE_INSTALLS.size()]);
} finally {
LOCK.unlock();
}
}
/** Replies if the given SRE is provided by the Eclipse platform
* through an extension point.
*
* @param sre - the sre.
* @return <code>true</code> if the SRE was provided through an extension
* point.
*/
public static boolean isPlatformSRE(ISREInstall sre) {
if (sre != null) {
LOCK.lock();
try {
return platformSREInstalls.contains(sre.getId());
} finally {
LOCK.unlock();
}
}
return false;
}
/**
* Saves the SRE configuration information to the preferences. This includes
* the following information:
* <ul>
* <li>The list of all defined ISREInstall instances.</li>
* <li>The default SRE.</li>
* </ul>
* This state will be read again upon first access to SRE
* configuration information.
*
* @param monitor - the progression monitor, or <code>null</code>.
* @throws CoreException if trying to save the current state of SREs encounters a problem
*/
public static void saveSREConfiguration(IProgressMonitor monitor) throws CoreException {
final SARLEclipsePlugin plugin = SARLEclipsePlugin.getDefault();
plugin.getPreferences().put(getCurrentPreferenceKey(), getSREsAsXML(monitor));
plugin.savePreferences();
}
/**
* Remove the SRE configuration information from the preferences.
*
* @throws CoreException if trying to save the current state of SREs encounters a problem
*/
public static void clearSREConfiguration() throws CoreException {
final SARLEclipsePlugin plugin = SARLEclipsePlugin.getDefault();
plugin.getPreferences().remove(getCurrentPreferenceKey());
plugin.savePreferences();
}
/**
* Initializes SRE extensions.
*/
private static void initializeSREExtensions() {
final MultiStatus status = new MultiStatus(SARLEclipsePlugin.PLUGIN_ID,
IStatus.OK, "Exceptions occurred", null); //$NON-NLS-1$
final IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(
SARLEclipsePlugin.PLUGIN_ID, SARLEclipseConfig.EXTENSION_POINT_SARL_RUNTIME_ENVIRONMENT);
if (extensionPoint != null) {
Object obj;
for (final IConfigurationElement element : extensionPoint.getConfigurationElements()) {
try {
obj = element.createExecutableExtension("class"); //$NON-NLS-1$
if (obj instanceof ISREInstall) {
final ISREInstall sre = (ISREInstall) obj;
platformSREInstalls.add(sre.getId());
ALL_SRE_INSTALLS.put(sre.getId(), sre);
} else {
SARLEclipsePlugin.getDefault().logErrorMessage(
"Cannot instance extension point: " + element.getName()); //$NON-NLS-1$
}
} catch (CoreException e) {
status.add(e.getStatus());
}
}
if (!status.isOK()) {
//only happens on a CoreException
SARLEclipsePlugin.getDefault().getLog().log(status);
}
}
}
/**
* Returns the XML representation of the given SRE.
*
* @param sre - the SRE to serialize.
* @return an XML representation of the given SRE.
* @throws CoreException if trying to compute the XML for the SRE state encounters a problem.
*/
public static String getSREAsXML(ISREInstall sre) throws CoreException {
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document xmldocument = builder.newDocument();
final Element sreNode = xmldocument.createElement("SRE"); //$NON-NLS-1$
sreNode.setAttribute("platform", Boolean.toString(isPlatformSRE(sre))); //$NON-NLS-1$
sreNode.setAttribute("id", sre.getId()); //$NON-NLS-1$
sreNode.setAttribute("class", sre.getClass().getName()); //$NON-NLS-1$
sre.getAsXML(xmldocument, sreNode);
xmldocument.appendChild(sreNode);
final TransformerFactory transFactory = TransformerFactory.newInstance();
final Transformer trans = transFactory.newTransformer();
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final DOMSource source = new DOMSource(xmldocument);
final PrintWriter flot = new PrintWriter(baos);
final StreamResult xmlStream = new StreamResult(flot);
trans.transform(source, xmlStream);
return new String(baos.toByteArray());
}
} catch (Throwable e) {
throw new CoreException(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, e));
}
}
/**
* Returns the XML representation of the given SRE.
*
* @param sre - the SRE to serialize.
* @param xml - the XML representation of the given SRE.
* @throws CoreException if trying to compute the XML for the SRE state encounters a problem.
*/
public static void setSREFromXML(ISREInstall sre, String xml) throws CoreException {
try {
final Element root = parseXML(xml, false);
sre.setFromXML(root);
} catch (Throwable e) {
throw new CoreException(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, e));
}
}
/**
* Returns the listing of currently installed SREs as a single XML file.
*
* @param monitor - monitor on the XML building.
* @return an XML representation of all of the currently installed SREs.
* @throws CoreException if trying to compute the XML for the SRE state encounters a problem.
*/
public static String getSREsAsXML(IProgressMonitor monitor) throws CoreException {
initializeSREs();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document xmldocument = builder.newDocument();
final Element rootElement = getXml(xmldocument);
xmldocument.appendChild(rootElement);
final TransformerFactory transFactory = TransformerFactory.newInstance();
final Transformer trans = transFactory.newTransformer();
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final DOMSource source = new DOMSource(xmldocument);
final PrintWriter flot = new PrintWriter(baos);
final StreamResult xmlStream = new StreamResult(flot);
trans.transform(source, xmlStream);
return new String(baos.toByteArray());
}
} catch (Throwable e) {
throw new CoreException(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, e));
}
}
private static Element getXml(Document xmlDocument) throws IOException {
final Element sresNode = xmlDocument.createElement("SREs"); //$NON-NLS-1$
LOCK.lock();
try {
for (final ISREInstall sre : ALL_SRE_INSTALLS.values()) {
final Element sreNode = xmlDocument.createElement("SRE"); //$NON-NLS-1$
sreNode.setAttribute("platform", Boolean.toString(isPlatformSRE(sre))); //$NON-NLS-1$
sreNode.setAttribute("id", sre.getId()); //$NON-NLS-1$
sreNode.setAttribute("class", sre.getClass().getName()); //$NON-NLS-1$
sre.getAsXML(xmlDocument, sreNode);
sresNode.appendChild(sreNode);
}
if (!Strings.isNullOrEmpty(defaultSREId)) {
sresNode.setAttribute("defaultSRE", defaultSREId); //$NON-NLS-1$
}
} finally {
LOCK.unlock();
}
return sresNode;
}
private static Element parseXML(String rawXml, boolean isMultiple) throws IOException {
try (InputStream stream = new BufferedInputStream(new ByteArrayInputStream(rawXml.getBytes()))) {
return parseXML(stream, isMultiple);
}
}
private static Element parseXML(InputStream stream, boolean isMultiple) throws IOException {
Element config = null;
try {
final DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
config = parser.parse(new InputSource(stream)).getDocumentElement();
} catch (SAXException e) {
throw new IOException(e);
} catch (ParserConfigurationException e) {
throw new IOException(LaunchingMessages.JavaRuntime_badFormat);
}
// If the top-level node wasn't what we expected, bail out
if (config == null
|| (isMultiple && !config.getNodeName().equalsIgnoreCase("SREs")) //$NON-NLS-1$
|| (!isMultiple && !config.getNodeName().equalsIgnoreCase("SRE"))) { //$NON-NLS-1$
throw new IOException("Invalid XML format of the SRE preferences"); //$NON-NLS-1$
}
return config;
}
private static ISREInstall createSRE(String classname, String id) {
if (!Strings.isNullOrEmpty(id)) {
try {
final Class<? extends ISREInstall> type = Class.forName(classname).asSubclass(ISREInstall.class);
final Constructor<? extends ISREInstall> cons = type.getConstructor(String.class);
return cons.newInstance(id);
} catch (Throwable exception) {
//
}
}
return null;
}
/**
* This method loads installed SREs based an existing user preference
* or old SRE configurations file.
*/
@SuppressWarnings("checkstyle:cyclomaticcomplexity")
private static String initializePersistedSREs() {
// // FOR DEBUG
// try {
// clearSREConfiguration();
// } catch (CoreException e1) {
// e1.printStackTrace();
// }
final String rawXml = SARLEclipsePlugin.getDefault().getPreferences().get(
getCurrentPreferenceKey(), ""); //$NON-NLS-1$
try {
Element config = null;
// If the preference was found, load SREs from it into memory
if (!Strings.isNullOrEmpty(rawXml)) {
config = parseXML(rawXml, true);
} else {
// Otherwise, look for the old file that previously held the SRE definitions
final SARLEclipsePlugin plugin = SARLEclipsePlugin.getDefault();
if (plugin.getBundle() != null) {
final IPath stateLocation = plugin.getStateLocation();
final IPath stateFile = stateLocation.append("sreConfiguration.xml"); //$NON-NLS-1$
final File file = stateFile.toFile();
if (file.exists()) {
// If file exists, load SRE definitions from it into memory and
// write the definitions to the preference store WITHOUT triggering
// any processing of the new value
try (InputStream fileInputStream = new BufferedInputStream(new FileInputStream(file))) {
config = parseXML(fileInputStream, true);
} catch (IOException e) {
SARLEclipsePlugin.getDefault().log(e);
}
}
}
}
if (config != null) {
final String defaultId = config.getAttribute("defaultSRE"); //$NON-NLS-1$
final NodeList children = config.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
try {
final Node child = children.item(i);
if ("SRE".equalsIgnoreCase(child.getNodeName()) //$NON-NLS-1$
&& child instanceof Element) {
final Element element = (Element) child;
final boolean isPlatform = Boolean.parseBoolean(element.getAttribute("platform")); //$NON-NLS-1$
final String id = element.getAttribute("id"); //$NON-NLS-1$
if (!isPlatform || !(ALL_SRE_INSTALLS.containsKey(id))) {
final ISREInstall sre = createSRE(
element.getAttribute("class"), //$NON-NLS-1$
id);
if (sre == null) {
throw new IOException("Invalid XML format of the SRE preferences of " + id); //$NON-NLS-1$
}
try {
sre.setFromXML(element);
} catch (IOException e) {
SARLEclipsePlugin.getDefault().log(e);
}
ALL_SRE_INSTALLS.put(id, sre);
if (isPlatform) {
platformSREInstalls.add(id);
}
} else {
final ISREInstall sre = ALL_SRE_INSTALLS.get(id);
if (sre != null) {
try {
sre.setFromXML(element);
} catch (IOException e) {
SARLEclipsePlugin.getDefault().log(e);
}
}
}
}
} catch (IOException e) {
SARLEclipsePlugin.getDefault().log(e);
}
}
return defaultId;
}
} catch (IOException e) {
SARLEclipsePlugin.getDefault().log(e);
}
return null;
}
/**
* Perform SRE install initialization. Does not hold locks
* while performing change notification.
*
* @since 3.2
*/
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:variabledeclarationusagedistance",
"checkstyle:npathcomplexity"})
private static void initializeSREs() {
ISREInstall[] newSREs = new ISREInstall[0];
boolean savePrefs = false;
LOCK.lock();
final String previousDefault = defaultSREId;
try {
if (platformSREInstalls == null) {
platformSREInstalls = new HashSet<>();
ALL_SRE_INSTALLS.clear();
// Install the SREs from the Eclipse extension points
if (enableSreExtensionPoints) {
initializeSREExtensions();
}
// install the SREs from the user-defined preferences.
final String predefinedDefaultId = Strings.nullToEmpty(initializePersistedSREs());
newSREs = new ISREInstall[ALL_SRE_INSTALLS.size()];
// Verify default SRE is valid
ISREInstall initDefaultSRE = null;
final Iterator<ISREInstall> iterator = ALL_SRE_INSTALLS.values().iterator();
for (int i = 0; iterator.hasNext(); ++i) {
final ISREInstall sre = iterator.next();
newSREs[i] = sre;
if (sre.getValidity().isOK()) {
if (initDefaultSRE == null
&& sre.getId().equals(predefinedDefaultId)) {
initDefaultSRE = sre;
}
}
}
final String oldDefaultId = initDefaultSRE == null ? null : initDefaultSRE.getId();
defaultSREId = oldDefaultId;
savePrefs = true;
}
if (Strings.isNullOrEmpty(defaultSREId)) {
ISREInstall firstSRE = null;
ISREInstall firstValidSRE = null;
final Iterator<ISREInstall> iterator = ALL_SRE_INSTALLS.values().iterator();
while (firstValidSRE == null && iterator.hasNext()) {
final ISREInstall sre = iterator.next();
if (firstSRE == null) {
firstSRE = sre;
}
if (sre.getValidity().isOK()) {
firstValidSRE = sre;
}
}
if (firstValidSRE == null) {
firstValidSRE = firstSRE;
}
if (firstValidSRE != null) {
savePrefs = true;
defaultSREId = firstValidSRE.getId();
}
}
} finally {
LOCK.unlock();
}
// Save the preferences.
if (savePrefs) {
safeSaveSREConfiguration();
}
if (newSREs.length > 0) {
for (final ISREInstall sre : newSREs) {
fireSREAdded(sre);
}
}
if (!Objects.equal(previousDefault, defaultSREId)) {
fireDefaultSREChanged(
getSREFromId(previousDefault),
getSREFromId(defaultSREId));
}
}
private static void safeSaveSREConfiguration() {
try {
saveSREConfiguration(null);
} catch (CoreException e) {
SARLEclipsePlugin.getDefault().log(e);
}
}
/** Replies an unique identifier.
*
* @return a unique identifier.
*/
public static String createUniqueIdentifier() {
String id;
do {
id = UUID.randomUUID().toString();
} while (getSREFromId(id) != null);
return id;
}
/** Reset the list of the SREs to the default ones (the platform SREs).
*
* @throws CoreException if a problem occurs during the reset.
*/
@SuppressWarnings("checkstyle:variabledeclarationusagedistance")
public static void reset() throws CoreException {
LOCK.lock();
try {
// Clear the SRE configuration stored into the preferences.
clearSREConfiguration();
// Reset the internal data structures.
final ISREInstall previous = getDefaultSREInstall();
final Map<String, ISREInstall> oldSREs = new HashMap<>(ALL_SRE_INSTALLS);
ALL_SRE_INSTALLS.clear();
platformSREInstalls = null;
defaultSREId = null;
// Notify about the removals
for (final ISREInstall sre : oldSREs.values()) {
fireSRERemoved(sre);
}
if (previous != null) {
fireDefaultSREChanged(previous, null);
}
// Re-read the data
initializeSREs();
} finally {
LOCK.unlock();
}
}
/** Replies if the given directory contains a SRE.
*
* @param directory - the directory.
* @return <code>true</code> if the given directory contains a SRE. Otherwise <code>false</code>.
* @see #isPackedSRE(File)
*/
public static boolean isUnpackedSRE(File directory) {
File manifestFile = new File(directory, "META-INF"); //$NON-NLS-1$
manifestFile = new File(manifestFile, "MANIFEST.MF"); //$NON-NLS-1$
if (manifestFile.canRead()) {
try (InputStream manifestStream = new FileInputStream(manifestFile)) {
final Manifest manifest = new Manifest(manifestStream);
final Attributes sarlSection = manifest.getAttributes(SREConstants.MANIFEST_SECTION_SRE);
if (sarlSection == null) {
return false;
}
final String sarlVersion = sarlSection.getValue(SREConstants.MANIFEST_SARL_SPEC_VERSION);
if (sarlVersion == null || sarlVersion.isEmpty()) {
return false;
}
final Version sarlVer = Version.parseVersion(sarlVersion);
return sarlVer != null;
} catch (IOException exception) {
return false;
}
}
return false;
}
/** Replies if the given directory contains a SRE.
*
* @param directory - the directory.
* @return <code>true</code> if the given directory contains a SRE. Otherwise <code>false</code>.
* @see #isPackedSRE(File)
*/
public static boolean isUnpackedSRE(IPath directory) {
final IFile location = ResourcesPlugin.getWorkspace().getRoot().getFile(directory);
if (location != null) {
final IPath path = location.getLocation();
if (path != null) {
final File file = path.toFile();
if (file.exists()) {
if (file.isDirectory()) {
return isUnpackedSRE(file);
}
return false;
}
}
}
return isUnpackedSRE(directory.makeAbsolute().toFile());
}
/** Replies if the given JAR file contains a SRE.
*
* @param jarFile - the JAR file to test.
* @return <code>true</code> if the given directory contains a SRE. Otherwise <code>false</code>.
* @see #isUnpackedSRE(File)
*/
public static boolean isPackedSRE(File jarFile) {
try (JarFile jFile = new JarFile(jarFile)) {
final Manifest manifest = jFile.getManifest();
if (manifest == null) {
return false;
}
final Attributes sarlSection = manifest.getAttributes(SREConstants.MANIFEST_SECTION_SRE);
if (sarlSection == null) {
return false;
}
final String sarlVersion = sarlSection.getValue(SREConstants.MANIFEST_SARL_SPEC_VERSION);
if (sarlVersion == null || sarlVersion.isEmpty()) {
return false;
}
final Version sarlVer = Version.parseVersion(sarlVersion);
return sarlVer != null;
} catch (IOException exception) {
return false;
}
}
/** Replies if the given JAR file contains a SRE.
*
* @param jarFile - the JAR file to test.
* @return <code>true</code> if the given directory contains a SRE. Otherwise <code>false</code>.
* @see #isUnpackedSRE(File)
*/
public static boolean isPackedSRE(IPath jarFile) {
try {
final IFile location = ResourcesPlugin.getWorkspace().getRoot().getFile(jarFile);
if (location != null) {
final IPath path = location.getLocation();
if (path != null) {
final File file = path.toFile();
if (file.exists()) {
if (file.isFile()) {
return isPackedSRE(file);
}
return false;
}
}
}
return isPackedSRE(jarFile.makeAbsolute().toFile());
} catch (Exception exception) {
return false;
}
}
}