blob: 22a71e535aed00a708a0275973bf9b74eb737ec8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2006 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.commands;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.commands.AbstractParameterValueConverter;
import org.eclipse.core.commands.Category;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ParameterType;
import org.eclipse.core.commands.State;
import org.eclipse.core.commands.common.HandleObject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.services.RegistryPersistence;
import org.eclipse.ui.internal.util.PrefUtil;
/**
* <p>
* A static class for accessing the registry and the preference store.
* </p>
*
* @since 3.1
*/
final class CommandPersistence extends RegistryPersistence {
/**
* The index of the category elements in the indexed array.
*
* @see CommandPersistence#read()
*/
private static final int INDEX_CATEGORY_DEFINITIONS = 0;
/**
* The index of the command elements in the indexed array.
*
* @see CommandPersistence#read()
*/
private static final int INDEX_COMMAND_DEFINITIONS = 1;
/**
* The index of the commandParameterType elements in the indexed array.
*
* @see CommandPersistence#read()
* @since 3.2
*/
private static final int INDEX_PARAMETER_TYPE_DEFINITIONS = 2;
/**
* Reads all of the category definitions from the commands extension point.
*
* @param configurationElements
* The configuration elements in the commands extension point;
* must not be <code>null</code>, but may be empty.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param commandService
* The command service to which the categories should be added;
* must not be <code>null</code>.
*/
private static final void readCategoriesFromRegistry(
final IConfigurationElement[] configurationElements,
final int configurationElementCount,
final ICommandService commandService) {
// Undefine all the previous handle objects.
final HandleObject[] handleObjects = commandService
.getDefinedCategories();
if (handleObjects != null) {
for (int i = 0; i < handleObjects.length; i++) {
handleObjects[i].undefine();
}
}
// Define the uncategorized category.
commandService
.defineUncategorizedCategory(
WorkbenchMessages.CommandService_AutogeneratedCategoryName,
WorkbenchMessages.CommandService_AutogeneratedCategoryDescription);
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement configurationElement = configurationElements[i];
// Read out the category identifier.
final String categoryId = readRequired(configurationElement,
ATT_ID, warningsToLog, "Categories need an id"); //$NON-NLS-1$
if (categoryId == null) {
continue;
}
// Read out the name.
final String name = readRequired(configurationElement, ATT_NAME,
warningsToLog, "Categories need a name", //$NON-NLS-1$
categoryId);
if (name == null) {
continue;
}
// Read out the description.
final String description = readOptional(configurationElement,
ATT_DESCRIPTION);
final Category category = commandService.getCategory(categoryId);
category.define(name, description);
}
// If there were any warnings, then log them now.
logWarnings(
warningsToLog,
"Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points."); //$NON-NLS-1$
}
/**
* Reads all of the command definitions from the commands extension point.
*
* @param configurationElements
* The configuration elements in the commands extension point;
* must not be <code>null</code>, but may be empty.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param commandService
* The command service to which the commands should be added;
* must not be <code>null</code>.
*/
private static final void readCommandsFromRegistry(
final IConfigurationElement[] configurationElements,
final int configurationElementCount,
final ICommandService commandService) {
// Undefine all the previous handle objects.
final HandleObject[] handleObjects = commandService
.getDefinedCommands();
if (handleObjects != null) {
for (int i = 0; i < handleObjects.length; i++) {
handleObjects[i].undefine();
}
}
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement configurationElement = configurationElements[i];
// Read out the command identifier.
final String commandId = readRequired(configurationElement, ATT_ID,
warningsToLog, "Commands need an id"); //$NON-NLS-1$
if (commandId == null) {
continue;
}
// Read out the name.
final String name = readRequired(configurationElement, ATT_NAME,
warningsToLog, "Commands need a name"); //$NON-NLS-1$
if (name == null) {
continue;
}
// Read out the description.
final String description = readOptional(configurationElement,
ATT_DESCRIPTION);
// Read out the category id.
String categoryId = configurationElement
.getAttribute(ATT_CATEGORY_ID);
if ((categoryId == null) || (categoryId.length() == 0)) {
categoryId = configurationElement.getAttribute(ATT_CATEGORY);
if ((categoryId != null) && (categoryId.length() == 0)) {
categoryId = null;
}
}
// Read out the parameters.
final Parameter[] parameters = readParameters(configurationElement,
warningsToLog, commandService);
// Read out the returnTypeId.
final String returnTypeId = readOptional(configurationElement,
ATT_RETURN_TYPE_ID);
// Read out the help context identifier.
final String helpContextId = readOptional(configurationElement,
ATT_HELP_CONTEXT_ID);
final Command command = commandService.getCommand(commandId);
final Category category = commandService.getCategory(categoryId);
if (!category.isDefined()) {
addWarning(
warningsToLog,
"Commands should really have a category", //$NON-NLS-1$
configurationElement, commandId,
"categoryId", categoryId); //$NON-NLS-1$
}
final ParameterType returnType;
if (returnTypeId == null) {
returnType = null;
} else {
returnType = commandService.getParameterType(returnTypeId);
}
command.define(name, description, category, parameters, returnType,
helpContextId);
readState(configurationElement, warningsToLog, command);
}
// If there were any warnings, then log them now.
logWarnings(
warningsToLog,
"Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points."); //$NON-NLS-1$
}
/**
* Reads the parameters from a parent configuration element. This is used to
* read the parameter sub-elements from a command 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
* while parsing the parameters will be appended to this list.
* This value must not be <code>null</code>.
* @param commandService
* The command service from which the parameter can get parameter
* types; must not be <code>null</code>.
* @return The array of parameters found for this configuration element;
* <code>null</code> if none can be found.
*/
private static final Parameter[] readParameters(
final IConfigurationElement configurationElement,
final List warningsToLog, final ICommandService commandService) {
final IConfigurationElement[] parameterElements = configurationElement
.getChildren(TAG_COMMAND_PARAMETER);
if ((parameterElements == null) || (parameterElements.length == 0)) {
return null;
}
int insertionIndex = 0;
Parameter[] parameters = new Parameter[parameterElements.length];
for (int i = 0; i < parameterElements.length; i++) {
final IConfigurationElement parameterElement = parameterElements[i];
// Read out the id
final String id = readRequired(parameterElement, ATT_ID,
warningsToLog, "Parameters need an id"); //$NON-NLS-1$
if (id == null) {
continue;
}
// Read out the name.
final String name = readRequired(parameterElement, ATT_NAME,
warningsToLog, "Parameters need a name"); //$NON-NLS-1$
if (name == null) {
continue;
}
/*
* The IParameterValues will be initialized lazily as an
* IExecutableExtension.
*/
// Read out the typeId attribute, if present.
final String typeId = readOptional(parameterElement, ATT_TYPE_ID);
// Read out the optional attribute, if present.
final boolean optional = readBoolean(parameterElement,
ATT_OPTIONAL, true);
final ParameterType type;
if (typeId == null) {
type = null;
} else {
type = commandService.getParameterType(typeId);
}
final Parameter parameter = new Parameter(id, name,
parameterElement, type, optional);
parameters[insertionIndex++] = parameter;
}
if (insertionIndex != parameters.length) {
final Parameter[] compactedParameters = new Parameter[insertionIndex];
System.arraycopy(parameters, 0, compactedParameters, 0,
insertionIndex);
parameters = compactedParameters;
}
return parameters;
}
/**
* Reads all of the commandParameterType definitions from the commands
* extension point.
*
* @param configurationElements
* The configuration elements in the commands extension point;
* must not be <code>null</code>, but may be empty.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param commandService
* The command service to which the commands should be added;
* must not be <code>null</code>.
* @since 3.2
*/
private static final void readParameterTypesFromRegistry(
final IConfigurationElement[] configurationElements,
final int configurationElementCount,
final ICommandService commandService) {
// Undefine all the previous handle objects.
final HandleObject[] handleObjects = commandService
.getDefinedParameterTypes();
if (handleObjects != null) {
for (int i = 0; i < handleObjects.length; i++) {
handleObjects[i].undefine();
}
}
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement configurationElement = configurationElements[i];
// Read out the commandParameterType identifier.
final String parameterTypeId = readRequired(configurationElement,
ATT_ID, warningsToLog, "Command parameter types need an id"); //$NON-NLS-1$
if (parameterTypeId == null) {
continue;
}
// Read out the type.
final String type = readOptional(configurationElement, ATT_TYPE);
// Read out the converter.
final String converter = readOptional(configurationElement,
ATT_CONVERTER);
/*
* if the converter attribute was given, create a proxy
* AbstractParameterValueConverter for the ParameterType, otherwise
* null indicates there is no converter
*/
final AbstractParameterValueConverter parameterValueConverter = (converter == null) ? null
: new ParameterValueConverterProxy(configurationElement);
final ParameterType parameterType = commandService
.getParameterType(parameterTypeId);
parameterType.define(type, parameterValueConverter);
}
// If there were any warnings, then log them now.
logWarnings(
warningsToLog,
"Warnings while parsing the commandParameterTypes from the 'org.eclipse.ui.commands' extension point."); //$NON-NLS-1$
}
/**
* Reads the states from a parent configuration element. This is used to
* read the state sub-elements from a command element. Each state is
* guaranteed to be valid. If invalid states are found, then a warning
* status will be appended to the <code>warningsToLog</code> list.
*
* @param configurationElement
* The configuration element from which the states should be
* read; must not be <code>null</code>.
* @param warningsToLog
* The list of warnings found during parsing. Warnings found
* while parsing the parameters will be appended to this list.
* This value must not be <code>null</code>.
* @param command
* The command for which the state is being read; may be
* <code>null</code>.
*/
private static final void readState(
final IConfigurationElement configurationElement,
final List warningsToLog, final Command command) {
final IConfigurationElement[] stateElements = configurationElement
.getChildren(TAG_STATE);
if ((stateElements == null) || (stateElements.length == 0)) {
return;
}
for (int i = 0; i < stateElements.length; i++) {
final IConfigurationElement stateElement = stateElements[i];
final String id = readRequired(stateElement, ATT_ID, warningsToLog,
"State needs an id"); //$NON-NLS-1$
if (id == null) {
continue;
}
if (checkClass(stateElement, warningsToLog,
"State must have an associated class", id)) { //$NON-NLS-1$
final State state = new CommandStateProxy(stateElement,
ATT_CLASS, PrefUtil.getInternalPreferenceStore(),
CommandService.createPreferenceKey(command, id));
command.addState(id, state);
}
}
}
/**
* The command service with which this persistence class is associated;
* never <code>null</code>.
*/
private final ICommandService commandService;
/**
* Constructs a new instance of <code>CommandPersistence</code>.
*
* @param commandService
* The command service which should be populated with the values
* from the registry; must not be <code>null</code>.
*/
CommandPersistence(final ICommandService commandService) {
if (commandService == null) {
throw new NullPointerException("The command service cannot be null"); //$NON-NLS-1$
}
this.commandService = commandService;
}
protected final boolean isChangeImportant(final IRegistryChangeEvent event) {
final IExtensionDelta[] commandDeltas = event.getExtensionDeltas(
PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_COMMANDS);
if (commandDeltas.length == 0) {
final IExtensionDelta[] actionDefinitionDeltas = event
.getExtensionDeltas(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_ACTION_DEFINITIONS);
if (actionDefinitionDeltas.length == 0) {
return false;
}
}
return true;
}
/**
* Reads all of the commands and categories from the registry,
*
* @param commandService
* The command service which should be populated with the values
* from the registry; must not be <code>null</code>.
*/
protected final void read() {
super.read();
// Create the extension registry mementos.
final IExtensionRegistry registry = Platform.getExtensionRegistry();
int commandDefinitionCount = 0;
int categoryDefinitionCount = 0;
int parameterTypeDefinitionCount = 0;
final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[3][];
// Sort the commands extension point based on element name.
final IConfigurationElement[] commandsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_COMMANDS);
for (int i = 0; i < commandsExtensionPoint.length; i++) {
final IConfigurationElement configurationElement = commandsExtensionPoint[i];
final String name = configurationElement.getName();
// Check if it is a binding definition.
if (TAG_COMMAND.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_COMMAND_DEFINITIONS, commandDefinitionCount++);
} else if (TAG_CATEGORY.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_CATEGORY_DEFINITIONS, categoryDefinitionCount++);
} else if (TAG_COMMAND_PARAMETER_TYPE.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_PARAMETER_TYPE_DEFINITIONS,
parameterTypeDefinitionCount++);
}
}
final IConfigurationElement[] actionDefinitionsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_ACTION_DEFINITIONS);
for (int i = 0; i < actionDefinitionsExtensionPoint.length; i++) {
final IConfigurationElement configurationElement = actionDefinitionsExtensionPoint[i];
final String name = configurationElement.getName();
if (TAG_ACTION_DEFINITION.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_COMMAND_DEFINITIONS, commandDefinitionCount++);
}
}
readCategoriesFromRegistry(
indexedConfigurationElements[INDEX_CATEGORY_DEFINITIONS],
categoryDefinitionCount, commandService);
readCommandsFromRegistry(
indexedConfigurationElements[INDEX_COMMAND_DEFINITIONS],
commandDefinitionCount, commandService);
readParameterTypesFromRegistry(
indexedConfigurationElements[INDEX_PARAMETER_TYPE_DEFINITIONS],
parameterTypeDefinitionCount, commandService);
}
}