blob: 8c2cf9728a18d80a82ef9669980634ca7f32c00b [file] [log] [blame]
package org.eclipse.update.internal.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.*;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.update.core.*;
import org.eclipse.update.core.IPluginEntry;
import org.xml.sax.SAXException;
/**
* Abstract Class that implements most of the behavior of a feature
* A feature ALWAYS belongs to an ISite
*/
// VK: we need to rework this as an API base class. Also need to rework which methods
// VK: need to be exposed on IFeature (same comment for Site, ISite)
// VK: Also, need to be able to poof-up a feature model from XML without having a site,
// VK: or passing null site (needed in PDE, build code, etc). Need a constructor
// VK: that takes a stream
public abstract class Feature implements IFeature {
/**
*
*/
private static CoreException CANCEL_EXCEPTION;
/**
*
*/
public static final String FEATURE_FILE = "feature";
/**
*
*/
public static final String FEATURE_XML = FEATURE_FILE + ".xml";
/**
* Identifier of the Feature
*/
private VersionedIdentifier versionIdentifier;
/**
* Site in which teh feature resides
*/
private ISite site;
/**
* User label fo the Feature
*/
private String label;
/**
* reference to the feature inside the site.
* This URL can be a Jar file, a directory or any URL that is understood by the
* Subclass of AbstractFeature.
*/
private URL url;
/**
* Url and label of site where update of this feature can ve found
*/
private IInfo updateInfo;
/**
* Url and label of site where other informations related to this feature can be found
*/
private List discoveryInfos;
/**
* The primary application
*/
private String application;
/**
* provider of the Feature
*/
private String provider;
/**
* Short description and url for long description of this feature
*/
private IInfo description;
/**
* Short copyright and url for long copyright of this feature
*/
private IInfo copyright;
/**
* Short license and url for long license of this feature
*/
private IInfo license;
/**
* Image (shoudl be either GIF or JPG)
*/
private URL image;
private String nl;
private String os;
private String ws;
/**
* List of plugin entries teh feature contains
* read from teh xml file
*/
private List pluginEntries;
/**
* List of data entries the feature contains
* read from the xml file
*/
private List dataEntries;
/**
* List of plugin the feature require
* to be installed in the site before this feature
* can be installed
*/
private List requires;
/**
* private internal
* used for lazy instantiation and
* hydration with the XML file
*/
private boolean isInitialized = false;
/**
* Static block to initialize the possible CANCEL ERROR
* thrown when the USER cancels teh operation
*/
static {
// in case we throw a cancel exception
String pluginId = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus cancelStatus = new Status(IStatus.ERROR, pluginId, IStatus.OK, "Install has been Cancelled", null);
CANCEL_EXCEPTION = new CoreException(cancelStatus);
}
/**
* Copy constructor
*/
public Feature(IFeature sourceFeature, ISite targetSite) throws CoreException {
// do not call other ctr as we do not want t parse XML file
this.site = targetSite;
this.url = sourceFeature.getURL();
this.versionIdentifier = sourceFeature.getIdentifier();
this.label = sourceFeature.getLabel();
this.updateInfo = sourceFeature.getUpdateInfo();
this.setDiscoveryInfos(sourceFeature.getDiscoveryInfos());
this.provider = sourceFeature.getProvider();
this.description = sourceFeature.getDescription();
this.copyright = sourceFeature.getCopyright();
this.license = sourceFeature.getLicense();
this.setPluginEntries(sourceFeature.getPluginEntries());
this.isInitialized = true;
}
/**
* Constructor
*/
public Feature(URL url, ISite targetSite) throws CoreException {
this.site = targetSite;
this.url = url;
initializeFeature();
}
/**
* @see IFeature#getIdentifier()
*/
public VersionedIdentifier getIdentifier() {
if (versionIdentifier == null && !isInitialized)
logNotInitialized();
return versionIdentifier;
}
/**
* @see IFeature#getSite()
* Do not hydrate, value set ins constructor
*/
public ISite getSite() {
return site;
}
/**
* @see IFeature#getLabel()
*/
public String getLabel() {
if (label == null && !isInitialized)
logNotInitialized();
return label;
}
/**
* @see IFeature#getURL()
* Do not hydrate. Initialization will not populate the url.
* If the URL is null, then the creation hasn't set the URL, so return null.
* It has to be set at creation time or using the set method
* Usually done from the site when creating the Feature.
*
* The DefaultSiteParser is setting it at creation time
*/
public URL getURL() {
return url;
}
/**
* @see IFeature#getRootURL()
* In general, the Root URL is the URL of teh Feature
*
* The RootURL is used to calculate relative URL for teh feature
* In case of a file feature, you can just append teh relative path
* to the URL of teh feature
*
* In case of a JAR file, you cannot *just* append the file
* You have to transfrom the URL
*
* Can be overriden
*/
public URL getRootURL() throws MalformedURLException, IOException, CoreException {
return url;
}
/**
* @see IFeature#getUpdateInfo()
*/
public IInfo getUpdateInfo() {
if (updateInfo == null && !isInitialized)
logNotInitialized();
return updateInfo;
}
/**
* @see IFeature#getDiscoveryInfos()
*/
//VK: method name: getDiscoveryInfo (plural==singular)
public IInfo[] getDiscoveryInfos() {
IInfo[] result = new IInfo[0];
if (discoveryInfos == null && !isInitialized)
logNotInitialized();
if (!(discoveryInfos == null || discoveryInfos.isEmpty())) {
result = new IInfo[discoveryInfos.size()];
discoveryInfos.toArray(result);
}
return result;
}
/*
* @see IFeature#getApplication()
*/
public String getApplication() {
return application;
}
/**
* @see IFeature#getProvider()
*/
public String getProvider() {
if (provider == null && !isInitialized)
logNotInitialized();
return provider;
}
/**
* @see IFeature#getDescription()
*/
public IInfo getDescription() {
if (description == null && !isInitialized)
logNotInitialized();
return description;
}
/**
* @see IFeature#getCopyright()
*/
public IInfo getCopyright() {
if (copyright == null && !isInitialized)
logNotInitialized();
return copyright;
}
/**
* @see IFeature#getLicense()
*/
public IInfo getLicense() {
if (license == null && !isInitialized)
logNotInitialized();
return license;
}
/**
* @see IFeature#getImage()
*/
public URL getImage() {
if (image == null && !isInitialized)
logNotInitialized();
return image;
}
/**
* @see IFeature#getNL()
*/
public String getNL() {
if (nl == null && !isInitialized)
logNotInitialized();
return nl;
}
/**
* @see IFeature#getOS()
*/
public String getOS() {
if (os == null && !isInitialized)
logNotInitialized();
return os;
}
/**
* @see IFeature#getWS()
*/
public String getWS() {
if (ws == null && !isInitialized)
logNotInitialized();
return ws;
}
/**
* Sets the site
* @param site The site to set
*/
public void setSite(ISite site) {
this.site = site;
}
/**
* Sets the identifier
* @param identifier The identifier to set
*/
public void setIdentifier(VersionedIdentifier identifier) {
this.versionIdentifier = identifier;
}
/**
* Sets the label
* @param label The label to set
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Sets the url
* @param url The url to set
*/
public void setURL(URL url) {
this.url = url;
}
/**
* Sets the updateInfo
* @param updateInfo The updateInfo to set
*/
public void setUpdateInfo(IInfo updateInfo) {
this.updateInfo = updateInfo;
}
/**
* Sets the discoveryInfos
* @param discoveryInfos The discoveryInfos to set
*/
//VK: method name: getDiscoveryInfo (plural==singular)
public void setDiscoveryInfos(IInfo[] discoveryInfos) {
if (discoveryInfos != null) {
this.discoveryInfos = (new ArrayList());
for (int i = 0; i < discoveryInfos.length; i++) {
this.discoveryInfos.add(discoveryInfos[i]);
}
}
}
/**
* Adds a discoveryInfo
* @param discoveryInfo The discoveryInfo to add
*/
public void addDiscoveryInfo(IInfo discoveryInfo) {
if (discoveryInfos == null)
discoveryInfos = new ArrayList(0);
discoveryInfos.add(discoveryInfo);
}
/**
* Sets the application
* @param application the application to set
*/
public void setApplication(String application) {
this.application = application;
}
/**
* Sets the provider
* @param provider The provider to set
*/
public void setProvider(String provider) {
this.provider = provider;
}
/**
* Sets the description
* @param description The description to set
*/
public void setDescription(IInfo description) {
this.description = description;
}
/**
* Sets the copyright
* @param copyright The copyright to set
*/
public void setCopyright(IInfo copyright) {
this.copyright = copyright;
}
/**
* Sets the license
* @param license The license to set
*/
public void setLicense(IInfo license) {
this.license = license;
}
/**
* Sets the image
* @param image The image to set
*/
public void setImage(URL image) {
this.image = image;
}
/**
* Sets the nl
* @param nl The nl to set
*/
public void setNL(String nl) {
this.nl = nl;
}
/**
* Sets the os
* @param os The os to set
*/
public void setOS(String os) {
this.os = os;
}
/**
* Sets the ws
* @param ws The ws to set
*/
public void setWS(String ws) {
this.ws = ws;
}
/**
* @see IPluginContainer#getDownloadSize(IPluginEntry)
*/
public int getDownloadSize(IPluginEntry entry) {
Assert.isTrue(entry instanceof PluginEntry);
return ((PluginEntry) entry).getDownloadSize();
}
/**
* @see IPluginContainer#getInstallSize(IPluginEntry)
*/
public int getInstallSize(IPluginEntry entry) {
Assert.isTrue(entry instanceof PluginEntry);
return ((PluginEntry) entry).getInstallSize();
}
/**
* returns the download size
* of the feature to be installed on the site.
* If the site is <code>null</code> returns the maximum size
*
* If one plug-in entry has an unknown size.
* then the download size is unknown.
*
* @see IFeature#getDownloadSize(ISite)
*
*/
public long getDownloadSize(ISite site) throws CoreException {
int result = 0;
IPluginEntry[] entriesToInstall = this.getPluginEntries();
if (site != null) {
IPluginEntry[] siteEntries = site.getPluginEntries();
entriesToInstall = intersection(entriesToInstall, siteEntries);
}
if (entriesToInstall == null || entriesToInstall.length == 0) {
result = -1;
} else {
int pluginSize = 0;
int i = 0;
while (i < entriesToInstall.length && pluginSize != -1) {
pluginSize = getDownloadSize(entriesToInstall[i]);
result = pluginSize == -1 ? -1 : result + pluginSize;
i++;
}
}
return result;
}
/**
* returns the install size
* of the feature to be installed on the site.
* If the site is <code>null</code> returns the maximum size
*
* If one plug-in entry has an unknown size.
* then the install size is unknown.
*
* @see IFeature#getInstallSize(ISite)
*/
public long getInstallSize(ISite site) throws CoreException {
int result = 0;
IPluginEntry[] entriesToInstall = this.getPluginEntries();
if (site != null) {
IPluginEntry[] siteEntries = site.getPluginEntries();
entriesToInstall = intersection(entriesToInstall, siteEntries);
}
if (entriesToInstall == null || entriesToInstall.length == 0) {
result = -1;
} else {
int pluginSize = 0;
int i = 0;
while (i < entriesToInstall.length && pluginSize != -1) {
pluginSize = getInstallSize(entriesToInstall[i]);
result = pluginSize == -1 ? -1 : result + pluginSize;
i++;
}
}
return result;
}
/**
* @see IFeature#isExecutable()
*/
public boolean isExecutable() {
return false;
}
/**
* @see IFeature#isInstallable()
*/
public boolean isInstallable() {
return false;
}
/**
* Method install.
* @param targetFeature
* @param monitor
* @throws CoreException
*/
public void install(IFeature targetFeature, IProgressMonitor monitor) throws CoreException {
IPluginEntry[] sourceFeaturePluginEntries = getPluginEntries();
IPluginEntry[] targetSitePluginEntries = targetFeature.getSite().getPluginEntries();
Site tempSite = (Site) SiteManager.getTempSite();
// VK: why are we creating a temp site (vs. simple temp directory)
// VK: why are we handling feature jar, plugin jar and data files differently wrt download
// determine list of plugins to install
// find the intersection between the two arrays of IPluginEntry...
// The one teh site contains and teh one the feature contains
IPluginEntry[] pluginsToInstall = intersection(sourceFeaturePluginEntries, targetSitePluginEntries);
// private abstract - Determine list of content references id /archives id /bundles id that
// map the list of plugins to install
String[] archiveIDToInstall = getContentReferenceToInstall(pluginsToInstall);
try {
// download and install data bundles
// before we set the site of teh feature to the TEMP site
IDataEntry[] dataEntries = getDataEntries();
if (dataEntries.length > 0) {
downloadDataLocally(targetFeature, dataEntries, monitor);
}
// optmization, may be private to implementation
// copy *blobs/content references/archives/bundles* in TEMP space
if (((Site) getSite()).optimize()) {
if (archiveIDToInstall != null) {
downloadArchivesLocally(tempSite, archiveIDToInstall, monitor);
}
}
// obtain the list of *Streamable Storage Unit*
// from the archive
if (monitor != null) {
int total = pluginsToInstall == null ? 1 : pluginsToInstall.length + 1;
monitor.beginTask("Install feature " + getLabel(), total);
}
if (pluginsToInstall != null) {
InputStream inStream = null;
for (int i = 0; i < pluginsToInstall.length; i++) {
if (monitor != null) {
monitor.subTask("Installing plug-in: " + pluginsToInstall[i]);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
open(pluginsToInstall[i]);
String[] names = getStorageUnitNames(pluginsToInstall[i]);
if (names != null) {
for (int j = 0; j < names.length; j++) {
if ((inStream = getInputStreamFor(pluginsToInstall[i], names[j])) != null)
targetFeature.store(pluginsToInstall[i], names[j], inStream);
}
}
close(pluginsToInstall[i]);
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
}
// install the Feature info
InputStream inStream = null;
String[] names = getStorageUnitNames();
if (names != null) {
openFeature();
if (monitor != null) {
monitor.subTask("Installing Feature information");
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
for (int j = 0; j < names.length; j++) {
if ((inStream = getInputStreamFor(names[j])) != null)
((Site) targetFeature.getSite()).storeFeatureInfo(getIdentifier(), names[j], inStream);
}
closeFeature();
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
} catch (IOException e) {
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "Error during Install", e);
throw new CoreException(status);
} finally {
//do not clean up TEMP drive
// as other feature may be there... clean up when exiting the plugin
}
}
/**
* remove myself...
*/
public void remove(IProgressMonitor monitor) throws CoreException {
// remove the feature and the plugins if they are not used and not activated
// get the plugins from the feature
IPluginEntry[] pluginsToRemove = ((SiteLocal) SiteManager.getLocalSite()).getDeltaPluginEntries(this);
try {
// obtain the list of *Streamable Storage Unit*
// from the archive
if (monitor != null) {
int total = pluginsToRemove == null ? 1 : pluginsToRemove.length + 1;
monitor.beginTask("Uninstall feature " + getLabel(), total);
}
if (pluginsToRemove != null) {
for (int i = 0; i < pluginsToRemove.length; i++) {
if (monitor != null) {
monitor.subTask("Removing plug-in: " + pluginsToRemove[i]);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
remove(pluginsToRemove[i]);
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
}
// install the Feature info
String[] names = getStorageUnitNames();
if (names != null) {
if (monitor != null) {
monitor.subTask("Removing Feature information");
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
((Site)this.getSite()).removeFeatureInfo(getIdentifier());
closeFeature();
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
} catch (IOException e) {
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "Error during Uninstall", e);
throw new CoreException(status);
}
}
/*
* @see IPluginContainer#remove(IPluginEntry)
*/
public void remove(IPluginEntry entry) throws CoreException {
((Site)getSite()).remove(entry);
}
/**
* initialize teh feature by reading the feature.xml if it exists
*/
// VK: why is this public ???
public void initializeFeature() throws CoreException {
if (!isInitialized) {
isInitialized = true;
InputStream featureStream = null;
try {
featureStream = getInputStreamFor(FEATURE_XML);
// VK: this forces everyone to implement feature.xml handling
// VK: regardless of the packaging scheme used for the feature. WHY ???
new FeatureParser(featureStream, this);
} catch (IOException e) {
// if we cannot find the feature and or the feature.xml...
// We should not stop the execution
// but we must Log it all the time, not only when debugging...
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "Error opening feature.xml in the feature archive:" + url.toExternalForm(), e);
UpdateManagerPlugin.getPlugin().getLog().log(status);
} catch (SAXException e) {
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "Error parsing feature.xml in the feature archive:" + url.toExternalForm(), e);
throw new CoreException(status);
} finally {
try {
featureStream.close();
} catch (Exception e) {
}
try {
closeFeature();
} catch (Exception e) {
}
}
}
}
/**
*/
// VK: method name ... downloadArchives(...)
private void downloadArchivesLocally(ISite tempSite, String[] archiveIDToInstall, IProgressMonitor monitor) throws CoreException, IOException {
URL sourceURL;
String newFile;
URL newURL;
if (monitor != null) {
monitor.beginTask("Download archives bundles to Temporary Space", archiveIDToInstall.length);
}
for (int i = 0; i < archiveIDToInstall.length; i++) {
// transform the id by asking the site to map them to real URL inside the SITE
sourceURL = ((Site) getSite()).getURL(archiveIDToInstall[i]);
if (monitor != null) {
monitor.subTask("..." + archiveIDToInstall[i]);
}
// the name of the file in the temp directory
// should be the regular plugins/pluginID_ver as the Temp site is OUR site
newFile = Site.DEFAULT_PLUGIN_PATH + archiveIDToInstall[i];
newURL = UpdateManagerUtils.resolveAsLocal(sourceURL, newFile, monitor);
// VK: this is a very confusing way to do a download
// transfer the possible mapping to the temp site
((Site) tempSite).addArchive(new Info(archiveIDToInstall[i], newURL));
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
// the site of this feature now becomes the TEMP directory
// FIXME: make sure there is no other issue
// like asking for stuff that hasn't been copied
// or reusing this feature
// of having an un-manageable temp site
this.setSite(tempSite);
}
/**
*/
// VK: downloadData ... why are we handling the download of plugin and non-plugin
// VK: file differently??? Why not just have one download method that takes a source
// VK: and target??? Would have one download code handling progress, recovery, etc
private void downloadDataLocally(IFeature targetFeature, IDataEntry[] dataToInstall, IProgressMonitor monitor) throws CoreException, IOException {
URL sourceURL;
// any other data
IDataEntry[] entries = getDataEntries();
// VK: why getDataEntries() when this is passed as dataToInstall ??? It is not used anywhere else.
if (entries != null) {
if (monitor != null) {
monitor.beginTask("Installing Other Data information", dataToInstall.length);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
for (int j = 0; j < entries.length; j++) {
// VK: see above ... use "entries.length" but iterate over dataToInstall[j] !!!!
String name = dataToInstall[j].getIdentifier();
if (monitor != null) {
monitor.subTask("..." + name);
}
// the id is URL format with "/"
String dataEntryId = Site.DEFAULT_FEATURE_PATH + getIdentifier().toString() + "/" + name;
// transform the id by asking the site to map them to real URL inside the SITE
sourceURL = ((Site) getSite()).getURL(dataEntryId);
((Site) targetFeature.getSite()).storeFeatureInfo(getIdentifier(), name, sourceURL.openStream());
if (monitor != null) {
monitor.worked(1);
if (monitor.isCanceled()) {
throw CANCEL_EXCEPTION;
}
}
}
}
}
/**
* Logs that an attempt to read a non initialize variable has been made
*/
private void logNotInitialized() {
Exception trace = new Exception("Attempt to read uninitialized variable");
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "the program is reading a variable of Feature before loading it", trace);
UpdateManagerPlugin.getPlugin().getLog().log(status);
}
/**
* Returns the intersection between two array of PluginEntries.
*/
private IPluginEntry[] intersection(IPluginEntry[] array1, IPluginEntry[] array2) {
if (array1 == null || array1.length == 0) {
return array2;
}
if (array2 == null || array2.length == 0) {
return array1;
}
List list1 = Arrays.asList(array1);
List result = new ArrayList(0);
for (int i = 0; i < array2.length; i++) {
if (!list1.contains(array2[i]))
result.add(array2[i]);
}
return (IPluginEntry[]) result.toArray();
}
/**
* @see IPluginContainer#getPluginEntries()
*/
public IPluginEntry[] getPluginEntries() {
IPluginEntry[] result = new IPluginEntry[0];
if (pluginEntries == null && !isInitialized)
logNotInitialized();
if (!(pluginEntries == null || pluginEntries.isEmpty())) {
result = new IPluginEntry[pluginEntries.size()];
pluginEntries.toArray(result);
}
return result;
}
/**
* @see IFeature#getDataEntries()
*/
public IDataEntry[] getDataEntries() {
IDataEntry[] result = new IDataEntry[0];
if (dataEntries == null && !isInitialized)
logNotInitialized();
if (!(dataEntries == null || dataEntries.isEmpty())) {
result = new IDataEntry[dataEntries.size()];
dataEntries.toArray(result);
}
return result;
}
/**
* @see IPluginContainer#getPluginEntryCount()
*/
public int getPluginEntryCount() {
return getPluginEntries().length;
}
/**
* @see IFeature#getImports()
*/
public IImport[] getImports() {
IImport[] result = new IImport[0];
if (!(requires == null || requires.isEmpty())) {
result = new IImport[requires.size()];
requires.toArray(result);
}
return result;
}
/**
* Sets the pluginEntries
* @param pluginEntries The pluginEntries to set
*/
public void setPluginEntries(IPluginEntry[] pluginEntries) {
if (pluginEntries != null) {
for (int i = 0; i < pluginEntries.length; i++) {
addPluginEntry(pluginEntries[i]);
}
}
}
/**
* Sets the import
* @param imports The imports to set
*/
public void setImports(IImport[] imports) {
if (imports != null) {
for (int i = 0; i < imports.length; i++) {
addImport(imports[i]);
}
}
}
/**
* @see IPluginContainer#addPluginEntry(IPluginEntry)
*/
public void addPluginEntry(IPluginEntry pluginEntry) {
if (pluginEntries == null)
pluginEntries = new ArrayList(0);
pluginEntries.add(pluginEntry);
}
/**
* @see IFeature#addDataEntry(IDataEntry)
*/
public void addDataEntry(IDataEntry dataEntry) {
if (dataEntries == null)
dataEntries = new ArrayList(0);
dataEntries.add(dataEntry);
}
/**
* Adds an import
* @param anImport The import to add
*/
public void addImport(IImport anImport) {
if (this.requires == null)
this.requires = new ArrayList(0);
this.requires.add(anImport);
}
/**
* @see IPluginContainer#store(IPluginEntry, String, InputStream)
*/
public void store(IPluginEntry pluginEntry, String contentKey, InputStream inStream) throws CoreException {
// check if pluginEntry already exists before passing to the site
// anything else ?
boolean found = false;
int i = 0;
IPluginEntry[] entries = getPluginEntries();
while (i < entries.length && !found) {
if (entries[i].equals(pluginEntry)) {
found = true;
}
i++;
}
if (!found) {
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "The plugin:" + pluginEntry.getIdentifier().toString() + " is not part of the plugins of the feature:" + this.getIdentifier().toString(), null);
throw new CoreException(status);
}
getSite().store(pluginEntry, contentKey, inStream);
}
/**
* perform pre processing before opening a plugin archive
* @param entry the plugin about to be opened
*/
protected void open(IPluginEntry entry) {
};
/**
* perform post processing to close a plugin archive
* @param entry the plugin about to be closed
*/
protected void close(IPluginEntry entry) throws IOException {
};
/**
* perform pre processing before opening the feature archive
*/
// VK: why is open protected and close public
protected void openFeature() {
};
/**
* perform post processing to close a feature archive
*/
public void closeFeature() throws IOException {
};
/**
* return the appropriate resource bundle for this feature
*/
public ResourceBundle getResourceBundle() throws IOException, CoreException {
ResourceBundle bundle = null;
try {
ClassLoader l = new URLClassLoader(new URL[] { this.getURL()}, null);
bundle = ResourceBundle.getBundle(FEATURE_FILE, Locale.getDefault(), l);
} catch (MissingResourceException e) {
//ok, there is no bundle, keep it as null
//DEBUG:
if (UpdateManagerPlugin.DEBUG && UpdateManagerPlugin.DEBUG_SHOW_WARNINGS) {
UpdateManagerPlugin.getPlugin().debug(e.getLocalizedMessage() + ":" + this.getURL().toExternalForm());
}
}
return bundle;
}
/**
* @see IFeature#getArchives()
* Private implementation of the feature. return the list of ID.
* Call the site with the ID to get the URL of the contentReference of the Site
*/
public abstract String[] getArchives();
/**
* return the archive ID for a plugin
* The id is based on the feature
* the default ID is plugins/pluginId_pluginVer or
* the default ID is fragments/pluginId_pluginVer or
*/
// VK: what do we want the concept of the id to be? we override it in PackagedFeature.
// VK: the whole handling of pluginEntry, dataEntry, archive is CONFUSING !!!
// VK: what about non-plugin data ??? are these archives???
public String getArchiveID(IPluginEntry entry){
//FIXME: fragments
String type = (entry.isFragment())?Site.DEFAULT_FRAGMENT_PATH:Site.DEFAULT_PLUGIN_PATH;
return type+entry.getIdentifier().toString();
}
/**
* return the list of FILE to be transfered for a Plugin
*/
// VK: why are some of the abstract methods public and others are protected. All
// VK: methods that need to be called by other objects (ec. Site) need to be public
protected abstract String[] getStorageUnitNames(IPluginEntry pluginEntry) throws CoreException;
/**
* return the list of FILE to be transfered from within the Feature
*/
// VK: the term "storage unit" is confusing ...
protected abstract String[] getStorageUnitNames() throws CoreException;
/**
* return the Stream of the FILE to be transfered for a Plugin
*/
protected abstract InputStream getInputStreamFor(IPluginEntry pluginEntry, String name) throws CoreException;
/**
* return the Stream of FILE to be transfered from within the Feature
*/
protected abstract InputStream getInputStreamFor(String name) throws IOException, CoreException;
/**
* returns the list of archive to transfer/install
* in order to install the list of plugins
*
* @param pluginsToInstall list of plugin to install
*/
protected abstract String[] getContentReferenceToInstall(IPluginEntry[] pluginsToInstall);
/*
* @see IAdaptable#getAdapter(Class)
*/
//VK: who calls this. Why is this needed?
public Object getAdapter(Class adapter) {
return null;
}
}