/* * $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.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.common.base.Objects; import com.google.common.base.Strings; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.PropertyChangeEvent; import org.osgi.framework.Bundle; import org.osgi.framework.Version; import io.sarl.eclipse.SARLEclipsePlugin; import io.sarl.eclipse.util.Utilities; /** * Abstract implementation of a SRE install. * * <p>Clients implementing SRE installs must subclass this class. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public abstract class AbstractSREInstall implements ISREInstall { private String id; private String name; private String minimalSarlVersion; private String maximalSarlVersion; private String mainClass; private boolean isStandalone; private List<IRuntimeClasspathEntry> classPathEntries; private Map<String, String> attributeMap; private boolean dirty = true; /** Whether change events should be fired. */ private boolean notify = true; /** Construct a SRE installation. * * @param id - the identifier of this SRE installation. */ public AbstractSREInstall(String id) { this.id = id; } @Override public AbstractSREInstall clone() { try { final AbstractSREInstall clone = (AbstractSREInstall) super.clone(); clone.attributeMap = this.attributeMap == null ? null : new HashMap<>(this.attributeMap); if (this.classPathEntries != null) { clone.classPathEntries = new ArrayList<>(this.classPathEntries); } if (this.classPathEntries != null) { clone.classPathEntries = new ArrayList<>(this.classPathEntries); } return clone; } catch (CloneNotSupportedException e) { throw new Error(e); } } @Override public ISREInstall copy(String uId) { final AbstractSREInstall copy = clone(); copy.id = uId; return copy; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (this.getClass() == obj.getClass()) { return getId().equals(((ISREInstall) obj).getId()); } return false; } @Override public int hashCode() { return getId().hashCode(); } @Override public String toString() { return getName(); } @Override public void setNotify(boolean notify) { this.notify = notify; } @Override public boolean getNotify() { return this.notify; } /** Replies if this installation has one of its field unresolved. * * @return <code>true</code> if one field has a too old value. */ protected boolean isDirty() { return this.dirty; } /** Set if this installation has one of its field unresolved. * * @param dirty - <code>true</code> if one field has a too old value. */ protected void setDirty(boolean dirty) { this.dirty = dirty; } /** Force the computation of the installation validity. * * @param ignoreCauses - a set of bits that indicates the invalidity causes to ignore. * @return the validity status. */ protected IStatus revalidate(int ignoreCauses) { try { setDirty(false); resolveDirtyFields(false); return getValidity(ignoreCauses); } catch (Throwable e) { if ((ignoreCauses & CODE_GENERAL) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_GENERAL, e); } return SARLEclipsePlugin.getDefault().createOkStatus(); } } @Override public final IStatus revalidate() { return revalidate(0); } @Override public IStatus getValidity(int ignoreCauses) { if (isDirty()) { return revalidate(ignoreCauses); } IStatus status = null; try { if (!isStandalone() && (ignoreCauses & CODE_STANDALONE_SRE) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_STANDALONE_SRE, Messages.AbstractSREInstall_5); } final String iMainClass = getMainClass(); if (Strings.isNullOrEmpty(iMainClass) && (ignoreCauses & CODE_MAIN_CLASS) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_MAIN_CLASS, Messages.AbstractSREInstall_2); } final String iName = getName(); if (Strings.isNullOrEmpty(iName) && (ignoreCauses & CODE_NAME) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_NAME, Messages.AbstractSREInstall_3); } status = getLibraryLocationValidity(ignoreCauses); if (status == null) { status = getSARLVersionValidity(ignoreCauses); } } catch (Throwable e) { // } if (status == null) { status = SARLEclipsePlugin.getDefault().createOkStatus(); } return status; } private IStatus getLibraryLocationValidity(int ignoreCauses) { final List<IRuntimeClasspathEntry> locations = getClassPathEntries(); if ((locations == null || locations.isEmpty()) && (ignoreCauses & CODE_LIBRARY_LOCATION) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_LIBRARY_LOCATION, Messages.AbstractSREInstall_4); } return null; } private IStatus getSARLVersionValidity(int ignoreCauses) { final Bundle bundle = Platform.getBundle("io.sarl.lang"); //$NON-NLS-1$ if (bundle != null) { final Version sarlVersion = bundle.getVersion(); final Version minVersion = Utilities.parseVersion(getMinimalSARLVersion()); final Version maxVersion = Utilities.parseVersion(getMaximalSARLVersion()); final int cmp = Utilities.compareVersionToRange(sarlVersion, minVersion, maxVersion); if (cmp < 0 && (ignoreCauses & CODE_SARL_VERSION) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_SARL_VERSION, MessageFormat.format( Messages.AbstractSREInstall_0, sarlVersion.toString(), minVersion.toString())); } if (cmp > 0 && (ignoreCauses & CODE_SARL_VERSION) == 0) { return SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, CODE_SARL_VERSION, MessageFormat.format( Messages.AbstractSREInstall_1, sarlVersion.toString(), maxVersion.toString())); } } return null; } /** Invoked when the JAR file has changed for updating the other * fields. * * @param forceSettings - indicates if the fields of this SRE must be set. */ protected abstract void resolveDirtyFields(boolean forceSettings); @Override public String getId() { return this.id; } @Override public String getName() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } return this.name; } @Override public String getMinimalSARLVersion() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } return this.minimalSarlVersion; } @Override public String getMaximalSARLVersion() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } return this.maximalSarlVersion; } @Override public String getMainClass() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } return this.mainClass; } @Override public List<IRuntimeClasspathEntry> getClassPathEntries() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } if (this.classPathEntries == null) { return new ArrayList<>(); } return this.classPathEntries; } @Override public Map<String, String> getVMSpecificAttributesMap() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } if (this.attributeMap == null) { return Collections.emptyMap(); } return Collections.unmodifiableMap(this.attributeMap); } @Override public void setName(String name) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } final String normalizedName = Strings.nullToEmpty(name); if (!name.equals(Strings.nullToEmpty(this.name))) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_NAME, this.name, normalizedName); this.name = normalizedName; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } /** * Change the library locations of this ISREInstall. * * @param libraries - The library locations of this ISREInstall. * Must not be <code>null</code>. */ @Override public void setClassPathEntries(List<IRuntimeClasspathEntry> libraries) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } if ((libraries == null && this.classPathEntries != null) || (libraries != null && (this.classPathEntries == null || libraries != this.classPathEntries))) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_LIBRARY_LOCATIONS, this.classPathEntries, libraries); this.classPathEntries = libraries; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public void setMinimalSARLVersion(String version) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } final String newVersion = Strings.nullToEmpty(version); if (!newVersion.isEmpty()) { // Check version number try { Version.parseVersion(newVersion); } catch (Throwable exception) { return; } } if (!newVersion.equals(Strings.nullToEmpty(this.minimalSarlVersion))) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_MINIMAL_SARL_VERSION, this.minimalSarlVersion, newVersion); this.minimalSarlVersion = newVersion; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public void setMaximalSARLVersion(String version) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } final String newVersion = Strings.nullToEmpty(version); if (!newVersion.isEmpty()) { // Check version number try { Version.parseVersion(newVersion); } catch (Throwable exception) { return; } } if (!newVersion.equals(Strings.nullToEmpty(this.maximalSarlVersion))) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_MAXIMAL_SARL_VERSION, this.maximalSarlVersion, newVersion); this.maximalSarlVersion = newVersion; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public void setVMSpecificAttributesMap(Map<String, String> attributes) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } if (!Objects.equal(attributes, this.attributeMap)) { final Map<String, String> oldValues; final Map<String, String> newValues; if (attributes == null) { newValues = Collections.emptyMap(); } else { newValues = attributes; } if (this.attributeMap == null) { oldValues = Collections.emptyMap(); } else { oldValues = this.attributeMap; } final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_VM_ATTRIBUTES, oldValues, newValues); this.attributeMap = attributes; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public void setMainClass(String mainClass) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } final String normalizedName = Strings.nullToEmpty(mainClass); if (!normalizedName.equals(Strings.nullToEmpty(this.mainClass))) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_NAME, this.mainClass, normalizedName); this.mainClass = normalizedName; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public void setStandalone(boolean isStandalone) { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } if (isStandalone != this.isStandalone) { final PropertyChangeEvent event = new PropertyChangeEvent( this, ISREInstallChangedListener.PROPERTY_STANDALONE_SRE, this.isStandalone, isStandalone); this.isStandalone = isStandalone; if (this.notify) { SARLRuntime.fireSREChanged(event); } } } @Override public boolean isStandalone() { if (isDirty()) { setDirty(false); resolveDirtyFields(true); } return this.isStandalone; } /** Replies the string representation of a command-line option. * * @param name the name of the option. * @param value the value to put in the command-line. * @return the string representation of the command-line option. */ protected static String formatCommandLineOption(String name, String value) { final StringBuilder str = new StringBuilder(); str.append("--"); //$NON-NLS-1$ if (!Strings.isNullOrEmpty(name)) { str.append(name); if (!Strings.isNullOrEmpty(value)) { str.append("="); //$NON-NLS-1$ str.append(value); } } return str.toString(); } }