blob: 1c5bd0fa4bb5ff81c7afa590f5e8f99276fe9dbd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.runtime.spi;
import java.io.File;
import java.util.*;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.internal.registry.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
/**
* This is the basic registry strategy. It describes how the registry does logging,
* message translation, extra start/stop processing, event scheduling, caching,
* and debugging.
* <p>
* In this strategy:
* </p><p><ul>
* <li>Logging is done onto <code>System.out</code>;</li>
* <li>The translation routine assumes that keys are prefixed with <code>'%'/<code>;</li>
* <li>Caching is enabled and doesn't use state or time stamp validation;</li>
* <li>Standard Java class loading is used to create executable extensions.</li>
* </ul></p><p>
* This class can be used without OSGi running.
* </p><p>
* This class can be overridden and/or instantiated by clients.
* </p>
* @since org.eclipse.equinox.registry 3.2
*/
public class RegistryStrategy {
private SAXParserFactory theXMLParserFactory = null;
/**
* Array of file system directories to store cache files; might be <code>null</code>
*/
private final File[] storageDirs;
/**
* Specifies if the registry file cache is read only; might be <code>null</code>
*/
private final boolean[] cacheReadOnly;
/**
* Constructor for this default registry strategy.
* <p>
* The strategy sequentially checks the array of storage directories to
* discover the location of the registry cache formed by previous invocations of the extension
* registry. Once found, the location is used to store registry cache. If this value
* is <code>null</code> then caching of the registry content is disabled.
* </p><p>
* The cache read-only array is an array the same length as the storage directory array.
* It contains boolean values indicating whether or not each storage directory is read-only.
* If the value at an index is <code>true</code> then the location at the corresponding index
* in the storage directories array is read-only; if <code>false</code> then the cache location
* is read-write. The array can be <code>null</code> if the <code>storageDirs</code> parameter
* is <code>null</code>.
* </p>
*
* @param storageDirs array of file system directories, or <code>null</code>
* @param cacheReadOnly array of read only attributes, or <code>null</code>
*/
public RegistryStrategy(File[] storageDirs, boolean[] cacheReadOnly) {
this.storageDirs = storageDirs;
this.cacheReadOnly = cacheReadOnly;
}
/**
* Returns the number of possible cache locations for this registry.
*
* @return number of possible cache locations for this registry
*/
public final int getLocationsLength() {
if (storageDirs == null)
return 0;
return storageDirs.length;
}
/**
* Returns the possible registry cache location identified by the index.
*
* @param index index of the possible registry location
* @return potential registry cache location
*/
public final File getStorage(int index) {
if (storageDirs != null)
return storageDirs[index];
return null;
}
/**
* Returns the read-only status of the registry cache location.
*
* @param index the index of the possible registry location
* @return <code>true</code> if the location is read only and
* <code>false</code> if the location is read/write
*/
public final boolean isCacheReadOnly(int index) {
if (cacheReadOnly != null)
return cacheReadOnly[index];
return true;
}
/**
* Override this method to provide customized logging functionality
* to the registry. The method adds a log entry based on the supplied status.
* <p>
* This method writes a message to <code>System.out</code>
* in the following format:
* <pre>
* [Error|Warning|Log]: Main error message
* [Error|Warning|Log]: Child error message 1
* ...
* [Error|Warning|Log]: Child error message N
* </pre></p>
*
* @param status the status to log
*/
public void log(IStatus status) {
RegistrySupport.log(status, null);
}
/**
* Translates key using the supplied resource bundle. The resource bundle is
* optional and might be <code>null</code>.
* <p>
* The default translation routine assumes that keys are prefixed with '%'. If
* no resource bundle is present, the key itself (without leading '%') is returned.
* There is no decoding for the leading '%%'.
* </p>
*
* @param key message key to be translated
* @param resources resource bundle, or <code>null</code>
* @return the translated string, must not be <code>null</code>
*/
public String translate(String key, ResourceBundle resources) {
return RegistrySupport.translate(key, resources);
}
/**
* Override this method to provide additional processing performed
* when the registry is created and started. Overrides should call
* <code>super.onStart()</code> at the beginning of the processing.
* <p>
* <strong>NOTE</strong>: Avoid placing duplicate functionality in
* this method and {@link #onStart(IExtensionRegistry, boolean)} as
* both methods will be called on the registry startup.
* </p>
* @param registry the extension registry being started
*
* @deprecated use {@link #onStart(IExtensionRegistry, boolean)}.
*/
public void onStart(IExtensionRegistry registry) {
// The default implementation
}
/**
* Override this method to provide additional processing performed
* when the registry is created and started. Overrides should call
* <code>super.onStart()</code> at the beginning of the processing.
*
* @param registry the extension registry being started
* @param loadedFromCache true is registry contents was loaded from
* cache when the registry was created
*
* @since 3.4
*/
public void onStart(IExtensionRegistry registry, boolean loadedFromCache) {
// The default implementation
}
/**
* Override this method to provide additional processing to be performed
* just before the registry is stopped. Overrides should call
* <code>super.onStop()</code> at the end of the processing.
* @param registry the extension registry being stopped
*/
public void onStop(IExtensionRegistry registry) {
// The default implementation
}
/**
* Creates an executable extension. Override this method to supply an alternative processing
* for the creation of executable extensions.
* <p>
* This method receives the contributor of the executable extension and, possibly,
* an optional contributor name if specified by the executable extension. The overridden
* contributor name might be <code>null</code>.
* </p><p>
* In this implementation registry attempts to instantiate the class specified via
* the class name (must not be <code>null</code>) using standard Java reflection mechanism.
* This method assumes that such class has a default constructor with no arguments.
* </p>
*
* @param contributor the contributor of this executable extension
* @param className the name of the class to be instantiated
* @param overridenContributorName the contributor to be used, or <code>null</code> if not specified
* @return the object created, or <code>null</code>
* @throws CoreException if there was a problem creating the executable extension
* @see IConfigurationElement#createExecutableExtension(String)
* @see IExecutableExtension
*/
public Object createExecutableExtension(RegistryContributor contributor, String className, String overridenContributorName) throws CoreException {
Object result = null;
Class classInstance = null;
try {
classInstance = Class.forName(className);
} catch (ClassNotFoundException e1) {
String message = NLS.bind(RegistryMessages.exExt_findClassError, contributor.getActualName(), className);
throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e1));
}
try {
result = classInstance.newInstance();
} catch (Exception e) {
String message = NLS.bind(RegistryMessages.exExt_instantiateClassError, contributor.getActualName(), className);
throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e));
}
return result;
}
/**
* Override this method to customize scheduling of an extension registry event. Note that this method
* <strong>must</strong> make the following call to actually process the event:
* <p><pre><code>
* RegistryStrategy.processChangeEvent(listeners, deltas, registry);
* </code></pre></p><p>
* In the default implementation, the method registry events are executed in a queue
* on a separate thread (i.e. asynchronously, sequentially).
* </p>
*
* @param listeners the list of active listeners (thread safe); may not be <code>null</code>
* @param deltas the registry deltas (thread safe); may not be <code>null</code>
* @param registry the extension registry (NOT thread safe); may not be <code>null</code>
*/
public void scheduleChangeEvent(Object[] listeners, Map deltas, Object registry) {
((ExtensionRegistry) registry).scheduleChangeEvent(listeners, deltas);
}
/**
* This method performs actual processing of the registry change event. It should
* only be used by overrides of the RegistryStrategy.scheduleChangeEvent. It will
* return <code>null</code> if an unexpected registry type was encountered.
*
* @param listeners the list of active listeners; may not be <code>null</code>
* @param deltas the extension registry deltas; may not be <code>null</code>
* @param registry the extension registry; may not be <code>null</code>
* @return status of the operation or <code>null</code>
*/
public final static IStatus processChangeEvent(Object[] listeners, Map deltas, Object registry) {
if (registry instanceof ExtensionRegistry)
return ((ExtensionRegistry) registry).processChangeEvent(listeners, deltas);
return null;
}
/**
* Override this method to specify debug requirements to the registry. In the default
* implementation this method returns <code>false</code> indicating that debug functionality
* is turned off.
* <p>
* Note that in a general case the extension registry plug-in doesn't depend on OSGI and
* therefore cannot use Eclipse .options files to discover debug options.
* </p>
*
* @return <code>true</code> if debug logging and validation should be performed and
* <code>false</code> otherwise
*/
public boolean debug() {
return false;
}
/**
* Override this method to specify debug requirements for the registry event processing.
* In the default implementation this method returns <code>false</code> indicating that
* debug of the registry events is turned off.
* <p>
* Note that in a general case the extension registry plug-in doesn't depend on OSGI and
* therefore cannot use Eclipse .options files to discover debug options.
* </p>
*
* @return <code>true</code> if debug logging and validation of the registry events
* should be performed and <code>false</code> otherwise
*/
public boolean debugRegistryEvents() {
return false;
}
/**
* Specifies if the extension registry should use cache to store registry data between
* invocations.
* <p>
* The default implementation enables caching returning <code>true</code>.
* </p>
*
* @return <code>true</code> if the cache should be used and <code>false</code> otherwise
*/
public boolean cacheUse() {
return true;
}
/**
* Specifies if lazy cache loading is used.
* <p>
* The default implementation specifies that lazy cache loading is going to be used
* and therefore returns <code>true</code>.
* </p>
*
* @return <code>true</code> if lazy cache loading is used and <code>false</code> otherwise
*/
public boolean cacheLazyLoading() {
return true;
}
/**
* This method is called as a part of the registry cache validation. The cache is valid
* on the registry startup if the pair {container time stamp, contributors time stamp}
* supplied by the registry strategy is the same as the {container time stamp, contributors time stamp}
* stored in the registry cache. The goal of the validation is to be able to catch modifications
* made to the original data contributed into the registry and not reflected in the registry cache.
* <p>
* The method produces a number that corresponds to the current state of the data stored
* by the container. Increment the stamp if the data stored in the container has been updated
* so that the data cached by the registry is no longer valid. For instance, in Eclipse addition
* or removal of a bundle results in the number returned by this method being incremented. As a result,
* if a bundle that contributed plugin.xml into the extension registry was modified, the state doesn't
* match the state stored in the registry cache. In this case the cache content becomes invalid and
* the registry needs to be re-created from the original data.
* </p><p>
* Generally, treat this number as a hash code for the data stored in the registry.
* It stays the same as long as the registry content is not changing. It becomes a different
* number as the registry content gets modified.
* </p><p>
* Return 0 to indicate that state verification is not required.
* </p>
*
* @return number indicating state of the application data
*/
public long getContainerTimestamp() {
return 0;
}
/**
* This method is called as a part of the registry cache validation. The method calculates
* a number describing the time when the originating contributions (i.e., plugin.xml files
* in case of the Eclipse registry) were last modified.
* <p>
* The value returned by the method is compared with the timestamp tracked by the registry.
* If contributions changed since they have been added to the registry (i.e., plugin.xml
* file was modified since the last run), the value of the {@link #getContributionsTimestamp()}
* will change and no longer will be the same as the value tracked by the registry. In this case
* the cache is considered to be invalid and the registry is going to be re-populated form scratch.
* </p><p>
* (The word "timestamp" is used very loosely here. In this context, "timestamp" is more likely
* to be a hash value aggregating a number of actual timestamps from the contributions.)
* </p><p>
* This method may return 0 to indicate that no time stamp verification is required.
* </p>
* @return a value corresponding to the last modification time of contributions contained
* in the registry
*/
public long getContributionsTimestamp() {
return 0;
}
/**
* Returns the parser used by the registry to parse descriptions of extension points and extensions.
* This method must not return <code>null</code>.
*
* @return this strategy's parser
* @see org.eclipse.core.runtime.IExtensionRegistry#addContribution(java.io.InputStream, IContributor, boolean, String, ResourceBundle, Object)
*/
public SAXParserFactory getXMLParser() {
if (theXMLParserFactory == null)
theXMLParserFactory = SAXParserFactory.newInstance();
return theXMLParserFactory;
}
/**
* Translates array of keys supplied by the contributor to the requested locale.
* <p>
* This method is intended to be overridden by specialized registry strategies
* that know translation conventions for the contributors, for instance,
* the agreed upon locations of the translated keys for bundle contributors.
* The base implementation simply returns the array of non-translated keys.
* </p><p>
* This method is only used if multi-language support is enabled.
* </p>
* @param nonTranslated message keys to be translated
* @param contributor the contributor of the keys to be translated
* @param locale the requested locale for the keys
* @return the arrays of translated strings
* @see IExtensionRegistry#isMultiLanguage()
* @since org.eclipse.equinox.registry 3.5
*/
public String[] translate(String[] nonTranslated, IContributor contributor, String locale) {
return nonTranslated;
}
/**
* Returns the current locale for the extension registry with enabled
* multi-language support.
* <p>
* The default implementation assumes that there is a single system wide
* locale, equivalent to {@link Locale#getDefault()}.
* </p><p>
* The result of this method should not be retained or passed to other threads.
* The current locale can change any time and may be different for each thread.
* </p><p>
* This method can be overridden by subclasses that wish to provide a way
* to change the default locale.
* </p><p>
* This method is only used if multi-language support is enabled.
* </p>
* @see IExtensionRegistry#isMultiLanguage()
* @return the default locale
* @since org.eclipse.equinox.registry 3.5
*/
public String getLocale() {
return Locale.getDefault().toString();
}
}