blob: 688a004c88528e714f97b9a4f6c655450b13ac89 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.CommandManager;
import org.eclipse.core.commands.ParameterType;
import org.eclipse.core.commands.State;
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.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.e4.core.commands.internal.HandlerServiceImpl;
import org.eclipse.e4.ui.internal.workbench.Parameter;
import org.eclipse.ui.PlatformUI;
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
*/
public 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 commandManager The command service to which the categories
* should be added; must not be
* <code>null</code>.
*/
private static void readCategoriesFromRegistry(final IConfigurationElement[] configurationElements,
final int configurationElementCount, final CommandManager commandManager) {
Category undefCat = commandManager.getCategory(null);
if (!undefCat.isDefined()) {
// Define the uncategorized category.
commandManager.defineUncategorizedCategory(WorkbenchMessages.CommandService_AutogeneratedCategoryName,
WorkbenchMessages.CommandService_AutogeneratedCategoryDescription);
}
final List<IStatus> 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 = commandManager.getCategory(categoryId);
if (!category.isDefined()) {
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 commandManager The command service to which the commands
* should be added; must not be
* <code>null</code>.
*/
private static void readCommandsFromRegistry(final IConfigurationElement[] configurationElements,
final int configurationElementCount, final CommandManager commandManager) {
final List<IStatus> 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.isEmpty())) {
categoryId = configurationElement.getAttribute(ATT_CATEGORY);
if ((categoryId != null) && (categoryId.isEmpty())) {
categoryId = null;
}
}
// Read out the parameters.
final Parameter[] parameters = readParameters(configurationElement, warningsToLog, commandManager);
// 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 = commandManager.getCommand(commandId);
final Category category = commandManager.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 = commandManager.getParameterType(returnTypeId);
}
if (parameters != null && parameters.length > 0) {
command.undefine();
}
if (!command.isDefined()) {
command.define(name, description, category, parameters, returnType, helpContextId);
command.setHandler(HandlerServiceImpl.getHandler(commandId));
}
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 commandManager 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 Parameter[] readParameters(final IConfigurationElement configurationElement,
final List<IStatus> warningsToLog, final CommandManager commandManager) {
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 (final IConfigurationElement parameterElement : parameterElements) {
// 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 = commandManager.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 commandManager The command service to which the commands
* should be added; must not be
* <code>null</code>.
* @since 3.2
*/
private static void readParameterTypesFromRegistry(final IConfigurationElement[] configurationElements,
final int configurationElementCount, final CommandManager commandManager) {
final List<IStatus> 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 = commandManager.getParameterType(parameterTypeId);
if (!parameterType.isDefined()) {
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 void readState(final IConfigurationElement configurationElement, final List<IStatus> warningsToLog,
final Command command) {
final IConfigurationElement[] stateElements = configurationElement.getChildren(TAG_STATE);
if ((stateElements == null) || (stateElements.length == 0)) {
return;
}
for (final IConfigurationElement stateElement : stateElements) {
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$
if (command.getState(id) == null) {
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 CommandManager commandManager;
/**
* 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>.
*/
public CommandPersistence(final CommandManager commandService) {
if (commandService == null) {
throw new NullPointerException("The command service cannot be null"); //$NON-NLS-1$
}
this.commandManager = commandService;
}
@Override
protected boolean isChangeImportant(final IRegistryChangeEvent event) {
return false;
}
public boolean commandsNeedUpdating(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 commandManager The command service which should be populated with the
* values from the registry; must not be
* <code>null</code>.
*/
@Override
protected void read() {
super.read();
reRead();
}
public void reRead() {
// 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 (final IConfigurationElement configurationElement : commandsExtensionPoint) {
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 (final IConfigurationElement configurationElement : actionDefinitionsExtensionPoint) {
final String name = configurationElement.getName();
if (TAG_ACTION_DEFINITION.equals(name)) {
addElementToIndexedArray(configurationElement, indexedConfigurationElements, INDEX_COMMAND_DEFINITIONS,
commandDefinitionCount++);
}
}
readCategoriesFromRegistry(indexedConfigurationElements[INDEX_CATEGORY_DEFINITIONS], categoryDefinitionCount,
commandManager);
readCommandsFromRegistry(indexedConfigurationElements[INDEX_COMMAND_DEFINITIONS], commandDefinitionCount,
commandManager);
readParameterTypesFromRegistry(indexedConfigurationElements[INDEX_PARAMETER_TYPE_DEFINITIONS],
parameterTypeDefinitionCount, commandManager);
}
}