| /******************************************************************************* |
| * Copyright (c) 2005, 2007 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.ui.internal.services; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.Command; |
| import org.eclipse.core.commands.IParameter; |
| import org.eclipse.core.commands.Parameterization; |
| import org.eclipse.core.commands.ParameterizedCommand; |
| import org.eclipse.core.commands.common.NotDefinedException; |
| import org.eclipse.core.expressions.ElementHandler; |
| import org.eclipse.core.expressions.EvaluationResult; |
| import org.eclipse.core.expressions.Expression; |
| import org.eclipse.core.expressions.ExpressionConverter; |
| import org.eclipse.core.expressions.IEvaluationContext; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.IRegistryChangeEvent; |
| import org.eclipse.core.runtime.IRegistryChangeListener; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; |
| import org.eclipse.ui.internal.util.Util; |
| import org.eclipse.ui.services.IDisposable; |
| |
| /** |
| * <p> |
| * A manager for items parsed from the registry. This attaches a listener to the |
| * registry after the first read, and monitors the registry for changes from |
| * that point on. When {@link #dispose()} is called, the listener is detached. |
| * </p> |
| * <p> |
| * This class is only intended for internal use within the |
| * <code>org.eclipse.ui.workbench</code> plug-in. |
| * </p> |
| * |
| * @since 3.2 |
| */ |
| public abstract class RegistryPersistence implements IDisposable, |
| IWorkbenchRegistryConstants { |
| |
| /** |
| * The expression to return when there is an error. Never <code>null</code>. |
| */ |
| protected static final Expression ERROR_EXPRESSION = new Expression() { |
| public final EvaluationResult evaluate(final IEvaluationContext context) { |
| return null; |
| } |
| }; |
| |
| /** |
| * Inserts the given element into the indexed two-dimensional array in the |
| * array at the index. The array is grown as necessary. |
| * |
| * @param elementToAdd |
| * The element to add to the indexed array; may be |
| * <code>null</code> |
| * @param indexedArray |
| * The two-dimensional array that is indexed by element type; |
| * must not be <code>null</code>. |
| * @param index |
| * The index at which the element should be added; must be a |
| * valid index. |
| * @param currentCount |
| * The current number of items in the array at the index. |
| */ |
| protected static final void addElementToIndexedArray( |
| final IConfigurationElement elementToAdd, |
| final IConfigurationElement[][] indexedArray, final int index, |
| final int currentCount) { |
| final IConfigurationElement[] elements; |
| if (currentCount == 0) { |
| elements = new IConfigurationElement[1]; |
| indexedArray[index] = elements; |
| } else { |
| if (currentCount >= indexedArray[index].length) { |
| final IConfigurationElement[] copy = new IConfigurationElement[indexedArray[index].length * 2]; |
| System.arraycopy(indexedArray[index], 0, copy, 0, currentCount); |
| elements = copy; |
| indexedArray[index] = elements; |
| } else { |
| elements = indexedArray[index]; |
| } |
| } |
| elements[currentCount] = elementToAdd; |
| } |
| |
| /** |
| * Adds a warning to be logged at some later point in time. |
| * |
| * @param warningsToLog |
| * The collection of warnings to be logged; must not be |
| * <code>null</code>. |
| * @param message |
| * The mesaage to log; must not be <code>null</code>. |
| * @param element |
| * The element from which the warning originates; may be |
| * <code>null</code>. |
| */ |
| protected static final void addWarning(final List warningsToLog, |
| final String message, final IConfigurationElement element) { |
| addWarning(warningsToLog, message, element, null, null, null); |
| } |
| |
| /** |
| * Adds a warning to be logged at some later point in time. This logs the |
| * identifier of the item. |
| * |
| * @param warningsToLog |
| * The collection of warnings to be logged; must not be |
| * <code>null</code>. |
| * @param message |
| * The mesaage to log; must not be <code>null</code>. |
| * @param element |
| * The element from which the warning originates; may be |
| * <code>null</code>. |
| * @param id |
| * The identifier of the item for which a warning is being |
| * logged; may be <code>null</code>. |
| */ |
| protected static final void addWarning(final List warningsToLog, |
| final String message, final IConfigurationElement element, |
| final String id) { |
| addWarning(warningsToLog, message, element, id, null, null); |
| } |
| |
| /** |
| * Adds a warning to be logged at some later point in time. This logs the |
| * identifier of the item, as well as an extra attribute. |
| * |
| * @param warningsToLog |
| * The collection of warnings to be logged; must not be |
| * <code>null</code>. |
| * @param message |
| * The mesaage to log; must not be <code>null</code>. |
| * @param element |
| * The element from which the warning originates; may be |
| * <code>null</code>. |
| * @param id |
| * The identifier of the item for which a warning is being |
| * logged; may be <code>null</code>. |
| * @param extraAttributeName |
| * The name of extra attribute to be logged; may be |
| * <code>null</code>. |
| * @param extraAttributeValue |
| * The value of the extra attribute to be logged; may be |
| * <code>null</code>. |
| */ |
| protected static final void addWarning(final List warningsToLog, |
| final String message, final IConfigurationElement element, |
| final String id, final String extraAttributeName, |
| final String extraAttributeValue) { |
| String statusMessage = message; |
| if (element != null) { |
| statusMessage = statusMessage |
| + ": plug-in='" + element.getNamespace() + '\''; //$NON-NLS-1$ |
| } |
| if (id != null) { |
| if (element != null) { |
| statusMessage = statusMessage + ','; |
| } else { |
| statusMessage = statusMessage + ':'; |
| } |
| statusMessage = statusMessage + " id='" + id + '\''; //$NON-NLS-1$ |
| } |
| if (extraAttributeName != null) { |
| if ((element != null) || (id != null)) { |
| statusMessage = statusMessage + ','; |
| } else { |
| statusMessage = statusMessage + ':'; |
| } |
| statusMessage = statusMessage + ' ' + extraAttributeName + "='" //$NON-NLS-1$ |
| + extraAttributeValue + '\''; |
| } |
| |
| final IStatus status = new Status(IStatus.WARNING, |
| WorkbenchPlugin.PI_WORKBENCH, 0, statusMessage, null); |
| warningsToLog.add(status); |
| } |
| |
| /** |
| * Checks that the class attribute or element exists for this element. This |
| * is used for executable extensions that are being read in. |
| * |
| * @param configurationElement |
| * The configuration element which should contain a class |
| * attribute or a class child element; must not be |
| * <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings to be logged; never <code>null</code>. |
| * @param message |
| * The message to log if something goes wrong; may be |
| * <code>null</code>. |
| * @param id |
| * The identifier of the handle object; may be <code>null</code>. |
| * @return <code>true</code> if the class attribute or element exists; |
| * <code>false</code> otherwise. |
| */ |
| protected static final boolean checkClass( |
| final IConfigurationElement configurationElement, |
| final List warningsToLog, final String message, final String id) { |
| // Check to see if we have a handler class. |
| if ((configurationElement.getAttribute(ATT_CLASS) == null) |
| && (configurationElement.getChildren(TAG_CLASS).length == 0)) { |
| addWarning(warningsToLog, message, configurationElement, id); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Checks to see whether the configuration element represents a pulldown |
| * action. This involves reading the <code>style</code> and |
| * <code>pulldown</code> attributes. |
| * |
| * @param element |
| * The element to check; must not be <code>null</code>. |
| * @return <code>true</code> if the element is a pulldown action; |
| * <code>false</code> otherwise. |
| */ |
| protected static final boolean isPulldown( |
| final IConfigurationElement element) { |
| final String style = readOptional(element, ATT_STYLE); |
| final boolean pulldown = readBoolean(element, ATT_PULLDOWN, false); |
| return (pulldown || STYLE_PULLDOWN.equals(style)); |
| } |
| |
| /** |
| * Logs any warnings in <code>warningsToLog</code>. |
| * |
| * @param warningsToLog |
| * The warnings to log; may be <code>null</code>. |
| * @param message |
| * The message to include in the log entry; must not be |
| * <code>null</code>. |
| */ |
| protected static final void logWarnings(final List warningsToLog, |
| final String message) { |
| // If there were any warnings, then log them now. |
| if ((warningsToLog != null) && (!warningsToLog.isEmpty())) { |
| final IStatus status = new MultiStatus( |
| WorkbenchPlugin.PI_WORKBENCH, 0, (IStatus[]) warningsToLog |
| .toArray(new IStatus[warningsToLog.size()]), |
| message, null); |
| WorkbenchPlugin.log(status); |
| } |
| } |
| |
| /** |
| * Reads a boolean attribute from an element. |
| * |
| * @param configurationElement |
| * The configuration element from which to read the attribute; |
| * must not be <code>null</code>. |
| * @param attribute |
| * The attribute to read; must not be <code>null</code>. |
| * @param defaultValue |
| * The default boolean value. |
| * @return The attribute's value; may be <code>null</code> if none. |
| */ |
| protected static final boolean readBoolean( |
| final IConfigurationElement configurationElement, |
| final String attribute, final boolean defaultValue) { |
| final String value = configurationElement.getAttribute(attribute); |
| if (value == null) { |
| return defaultValue; |
| } |
| |
| if (defaultValue) { |
| return !value.equalsIgnoreCase("false"); //$NON-NLS-1$ |
| } |
| |
| return value.equalsIgnoreCase("true"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Reads an optional attribute from an element. This converts zero-length |
| * strings into <code>null</code>. |
| * |
| * @param configurationElement |
| * The configuration element from which to read the attribute; |
| * must not be <code>null</code>. |
| * @param attribute |
| * The attribute to read; must not be <code>null</code>. |
| * @return The attribute's value; may be <code>null</code> if none. |
| */ |
| protected static final String readOptional( |
| final IConfigurationElement configurationElement, |
| final String attribute) { |
| String value = configurationElement.getAttribute(attribute); |
| if ((value != null) && (value.length() == 0)) { |
| value = null; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Reads the parameterized command from a parent configuration element. This |
| * is used to read the parameter sub-elements from a key element, as well as |
| * the command id. Each parameter is guaranteed to be valid. If invalid |
| * parameters are found, then a warning status will be appended to the |
| * <code>warningsToLog</code> list. The command id is required, or a |
| * warning will be logged. |
| * |
| * @param configurationElement |
| * The configuration element from which the parameters should be |
| * read; must not be <code>null</code>. |
| * @param commandService |
| * The service providing commands for the workbench; must not be |
| * <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings found during parsing. Warnings found will |
| * parsing the parameters will be appended to this list. This |
| * value must not be <code>null</code>. |
| * @param message |
| * The message to print if the command identifier is not present; |
| * must not be <code>null</code>. |
| * @return The array of parameters found for this configuration element; |
| * <code>null</code> if none can be found. |
| */ |
| protected static final ParameterizedCommand readParameterizedCommand( |
| final IConfigurationElement configurationElement, |
| final ICommandService commandService, final List warningsToLog, |
| final String message, final String id) { |
| final String commandId = readRequired(configurationElement, |
| ATT_COMMAND_ID, warningsToLog, message, id); |
| if (commandId == null) { |
| return null; |
| } |
| |
| final Command command = commandService.getCommand(commandId); |
| final ParameterizedCommand parameterizedCommand = readParameters( |
| configurationElement, warningsToLog, command); |
| |
| return parameterizedCommand; |
| } |
| |
| /** |
| * Reads the parameters from a parent configuration element. This is used to |
| * read the parameter sub-elements from a key element. Each parameter is |
| * guaranteed to be valid. If invalid parameters are found, then a warning |
| * status will be appended to the <code>warningsToLog</code> list. |
| * |
| * @param configurationElement |
| * The configuration element from which the parameters should be |
| * read; must not be <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings found during parsing. Warnings found will |
| * parsing the parameters will be appended to this list. This |
| * value must not be <code>null</code>. |
| * @param command |
| * The command around which the parameterization should be |
| * created; must not be <code>null</code>. |
| * @return The array of parameters found for this configuration element; |
| * <code>null</code> if none can be found. |
| */ |
| protected static final ParameterizedCommand readParameters( |
| final IConfigurationElement configurationElement, |
| final List warningsToLog, final Command command) { |
| final IConfigurationElement[] parameterElements = configurationElement |
| .getChildren(TAG_PARAMETER); |
| if ((parameterElements == null) || (parameterElements.length == 0)) { |
| return new ParameterizedCommand(command, null); |
| } |
| |
| final Collection parameters = new ArrayList(); |
| for (int i = 0; i < parameterElements.length; i++) { |
| final IConfigurationElement parameterElement = parameterElements[i]; |
| |
| // Read out the id. |
| final String id = parameterElement.getAttribute(ATT_ID); |
| if ((id == null) || (id.length() == 0)) { |
| // The name should never be null. This is invalid. |
| addWarning(warningsToLog, "Parameters need a name", //$NON-NLS-1$ |
| configurationElement); |
| continue; |
| } |
| |
| // Find the parameter on the command. |
| IParameter parameter = null; |
| try { |
| final IParameter[] commandParameters = command.getParameters(); |
| if (parameters != null) { |
| for (int j = 0; j < commandParameters.length; j++) { |
| final IParameter currentParameter = commandParameters[j]; |
| if (Util.equals(currentParameter.getId(), id)) { |
| parameter = currentParameter; |
| break; |
| } |
| } |
| |
| } |
| } catch (final NotDefinedException e) { |
| // This should not happen. |
| } |
| if (parameter == null) { |
| // The name should never be null. This is invalid. |
| addWarning(warningsToLog, |
| "Could not find a matching parameter", //$NON-NLS-1$ |
| configurationElement, id); |
| continue; |
| } |
| |
| // Read out the value. |
| final String value = parameterElement.getAttribute(ATT_VALUE); |
| if ((value == null) || (value.length() == 0)) { |
| // The name should never be null. This is invalid. |
| addWarning(warningsToLog, "Parameters need a value", //$NON-NLS-1$ |
| configurationElement, id); |
| continue; |
| } |
| |
| parameters.add(new Parameterization(parameter, value)); |
| } |
| |
| if (parameters.isEmpty()) { |
| return new ParameterizedCommand(command, null); |
| } |
| |
| return new ParameterizedCommand(command, |
| (Parameterization[]) parameters |
| .toArray(new Parameterization[parameters.size()])); |
| } |
| |
| /** |
| * Reads a required attribute from the configuration element. |
| * |
| * @param configurationElement |
| * The configuration element from which to read; must not be |
| * <code>null</code>. |
| * @param attribute |
| * The attribute to read; must not be <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings; must not be <code>null</code>. |
| * @param message |
| * The warning message to use if the attribute is missing; must |
| * not be <code>null</code>. |
| * @return The required attribute; may be <code>null</code> if missing. |
| */ |
| protected static final String readRequired( |
| final IConfigurationElement configurationElement, |
| final String attribute, final List warningsToLog, |
| final String message) { |
| return readRequired(configurationElement, attribute, warningsToLog, |
| message, null); |
| } |
| |
| /** |
| * Reads a required attribute from the configuration element. This logs the |
| * identifier of the item if this required element cannot be found. |
| * |
| * @param configurationElement |
| * The configuration element from which to read; must not be |
| * <code>null</code>. |
| * @param attribute |
| * The attribute to read; must not be <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings; must not be <code>null</code>. |
| * @param message |
| * The warning message to use if the attribute is missing; must |
| * not be <code>null</code>. |
| * @param id |
| * The identifier of the element for which this is a required |
| * attribute; may be <code>null</code>. |
| * @return The required attribute; may be <code>null</code> if missing. |
| */ |
| protected static final String readRequired( |
| final IConfigurationElement configurationElement, |
| final String attribute, final List warningsToLog, |
| final String message, final String id) { |
| final String value = configurationElement.getAttribute(attribute); |
| if ((value == null) || (value.length() == 0)) { |
| addWarning(warningsToLog, message, configurationElement, id); |
| return null; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Reads a <code>when</code> child element from the given configuration |
| * element. Warnings will be appended to <code>warningsToLog</code>. |
| * |
| * @param parentElement |
| * The configuration element which might have a <code>when</code> |
| * element as a child; never <code>null</code>. |
| * @param whenElementName |
| * The name of the when element to find; never <code>null</code>. |
| * @param id |
| * The identifier of the menu element whose <code>when</code> |
| * expression is being read; never <code>null</code>. |
| * @param warningsToLog |
| * The list of warnings while parsing the extension point; never |
| * <code>null</code>. |
| * @return The <code>when</code> expression for the |
| * <code>configurationElement</code>, if any; otherwise, |
| * <code>null</code>. |
| */ |
| protected static final Expression readWhenElement( |
| final IConfigurationElement parentElement, |
| final String whenElementName, final String id, |
| final List warningsToLog) { |
| // Check to see if we have an when expression. |
| final IConfigurationElement[] whenElements = parentElement |
| .getChildren(whenElementName); |
| Expression whenExpression = null; |
| if (whenElements.length > 0) { |
| // Check if we have too many when elements. |
| if (whenElements.length > 1) { |
| // There should only be one when element |
| addWarning(warningsToLog, |
| "There should only be one when element", parentElement, //$NON-NLS-1$ |
| id, "whenElementName", //$NON-NLS-1$ |
| whenElementName); |
| return ERROR_EXPRESSION; |
| } |
| |
| final IConfigurationElement whenElement = whenElements[0]; |
| final IConfigurationElement[] expressionElements = whenElement |
| .getChildren(); |
| if (expressionElements.length > 0) { |
| // Check if we have too many expression elements |
| if (expressionElements.length > 1) { |
| // There should only be one expression element |
| addWarning( |
| warningsToLog, |
| "There should only be one expression element", parentElement, //$NON-NLS-1$ |
| id, "whenElementName", //$NON-NLS-1$ |
| whenElementName); |
| return ERROR_EXPRESSION; |
| } |
| |
| // Convert the activeWhen element into an expression. |
| final ElementHandler elementHandler = ElementHandler |
| .getDefault(); |
| final ExpressionConverter converter = ExpressionConverter |
| .getDefault(); |
| final IConfigurationElement expressionElement = expressionElements[0]; |
| try { |
| whenExpression = elementHandler.create(converter, |
| expressionElement); |
| } catch (final CoreException e) { |
| // There when expression could not be created. |
| addWarning( |
| warningsToLog, |
| "Problem creating when element", //$NON-NLS-1$ |
| parentElement, id, |
| "whenElementName", whenElementName); //$NON-NLS-1$ |
| return ERROR_EXPRESSION; |
| } |
| } |
| } |
| |
| return whenExpression; |
| } |
| |
| /** |
| * The registry change listener for this class. |
| */ |
| private final IRegistryChangeListener registryChangeListener; |
| |
| /** |
| * Whether the preference and registry change listeners have been attached |
| * yet. |
| */ |
| protected boolean registryListenerAttached = false; |
| |
| /** |
| * Constructs a new instance of {@link RegistryPersistence}. A registry |
| * change listener is created. |
| */ |
| protected RegistryPersistence() { |
| registryChangeListener = new IRegistryChangeListener() { |
| public final void registryChanged(final IRegistryChangeEvent event) { |
| if (isChangeImportant(event)) { |
| Display.getDefault().asyncExec(new Runnable() { |
| public final void run() { |
| read(); |
| } |
| }); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Detaches the registry change listener from the registry. |
| */ |
| public void dispose() { |
| final IExtensionRegistry registry = Platform.getExtensionRegistry(); |
| registry.removeRegistryChangeListener(registryChangeListener); |
| registryListenerAttached = false; |
| } |
| |
| /** |
| * Checks whether the registry change could affect this persistence class. |
| * |
| * @param event |
| * The event indicating the registry change; must not be |
| * <code>null</code>. |
| * @return <code>true</code> if the persistence instance is affected by |
| * this change; <code>false</code> otherwise. |
| */ |
| protected abstract boolean isChangeImportant( |
| final IRegistryChangeEvent event); |
| |
| /** |
| * Reads the various elements from the registry. Subclasses should extend, |
| * but must not override. |
| */ |
| protected void read() { |
| if (!registryListenerAttached) { |
| final IExtensionRegistry registry = Platform.getExtensionRegistry(); |
| registry.addRegistryChangeListener(registryChangeListener); |
| registryListenerAttached = true; |
| } |
| } |
| } |