1 /******************************************************************************* 2 * Copyright (c) 2005, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 15 package org.eclipse.ui.internal.handlers; 16 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.Iterator; 20 import java.util.List; 21 import org.eclipse.core.commands.IHandler; 22 import org.eclipse.core.expressions.Expression; 23 import org.eclipse.core.runtime.IConfigurationElement; 24 import org.eclipse.core.runtime.IExtensionDelta; 25 import org.eclipse.core.runtime.IExtensionRegistry; 26 import org.eclipse.core.runtime.IRegistryChangeEvent; 27 import org.eclipse.core.runtime.IStatus; 28 import org.eclipse.core.runtime.Platform; 29 import org.eclipse.ui.PlatformUI; 30 import org.eclipse.ui.handlers.IHandlerActivation; 31 import org.eclipse.ui.handlers.IHandlerService; 32 import org.eclipse.ui.internal.WorkbenchPlugin; 33 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; 34 import org.eclipse.ui.internal.services.RegistryPersistence; 35 import org.eclipse.ui.services.IEvaluationService; 36 37 /** 38 * <p> 39 * A static class for accessing the registry. 40 * </p> 41 * 42 * @since 3.1 43 */ 44 public final class HandlerPersistence extends RegistryPersistence { 45 46 /** 47 * The index of the command elements in the indexed array. 48 * 49 * @see HandlerPersistence#read() 50 */ 51 private static final int INDEX_COMMAND_DEFINITIONS = 0; 52 53 /** 54 * The index of the command elements in the indexed array. 55 * 56 * @see HandlerPersistence#read() 57 */ 58 private static final int INDEX_HANDLER_DEFINITIONS = 1; 59 60 /** 61 * The index of the handler submissions in the indexed array. 62 * 63 * @see HandlerPersistence#read() 64 */ 65 private static final int INDEX_HANDLER_SUBMISSIONS = 2; 66 67 /** 68 * The handler activations that have come from the registry. This is used to 69 * flush the activations when the registry is re-read. This value is never 70 * <code>null</code> 71 */ 72 private final Collection<IHandlerActivation> handlerActivations = new ArrayList<>(); 73 74 /** 75 * The handler service with which this persistence class is associated. This 76 * value must not be <code>null</code>. 77 */ 78 private final IHandlerService handlerService; 79 80 private IEvaluationService evaluationService; 81 82 /** 83 * Constructs a new instance of <code>HandlerPersistence</code>. 84 * 85 * @param handlerService The handler service with which the handlers should 86 * be registered; must not be <code>null</code>. 87 * @param evaluationService The evaluation service used by handler proxies with 88 * enabled when expressions 89 */ HandlerPersistence(final IHandlerService handlerService, IEvaluationService evaluationService)90 HandlerPersistence(final IHandlerService handlerService, IEvaluationService evaluationService) { 91 this.handlerService = handlerService; 92 this.evaluationService = evaluationService; 93 } 94 95 /** 96 * Deactivates all of the activations made by this class, and then clears the 97 * collection. This should be called before every read. 98 * 99 * @param handlerService The service handling the activations; must not be 100 * <code>null</code>. 101 */ clearActivations(final IHandlerService handlerService)102 private void clearActivations(final IHandlerService handlerService) { 103 handlerService.deactivateHandlers(handlerActivations); 104 Iterator<IHandlerActivation> i = handlerActivations.iterator(); 105 while (i.hasNext()) { 106 IHandlerActivation activation = i.next(); 107 if (activation.getHandler() != null) { 108 try { 109 activation.getHandler().dispose(); 110 } catch (Exception | LinkageError e) { 111 WorkbenchPlugin.log("Failed to dispose handler for " //$NON-NLS-1$ 112 + activation.getCommandId(), e); 113 } 114 } 115 } 116 handlerActivations.clear(); 117 } 118 119 @Override dispose()120 public void dispose() { 121 super.dispose(); 122 clearActivations(handlerService); 123 } 124 125 @Override isChangeImportant(final IRegistryChangeEvent event)126 protected boolean isChangeImportant(final IRegistryChangeEvent event) { 127 return false; 128 } 129 handlersNeedUpdating(final IRegistryChangeEvent event)130 public boolean handlersNeedUpdating(final IRegistryChangeEvent event) { 131 /* 132 * Handlers will need to be re-read (i.e., re-verified) if any of the handler 133 * extensions change (i.e., handlers, commands), or if any of the command 134 * extensions change (i.e., action definitions). 135 */ 136 final IExtensionDelta[] handlerDeltas = event.getExtensionDeltas(PlatformUI.PLUGIN_ID, 137 IWorkbenchRegistryConstants.PL_HANDLERS); 138 if (handlerDeltas.length == 0) { 139 final IExtensionDelta[] commandDeltas = event.getExtensionDeltas(PlatformUI.PLUGIN_ID, 140 IWorkbenchRegistryConstants.PL_COMMANDS); 141 if (commandDeltas.length == 0) { 142 final IExtensionDelta[] actionDefinitionDeltas = event.getExtensionDeltas(PlatformUI.PLUGIN_ID, 143 IWorkbenchRegistryConstants.PL_ACTION_DEFINITIONS); 144 if (actionDefinitionDeltas.length == 0) { 145 return false; 146 } 147 } 148 } 149 150 return true; 151 } 152 153 /** 154 * Reads all of the handlers from the registry 155 * 156 * @param handlerService The handler service which should be populated with the 157 * values from the registry; must not be 158 * <code>null</code>. 159 */ 160 @Override read()161 protected void read() { 162 super.read(); 163 reRead(); 164 } 165 reRead()166 public void reRead() { 167 // Create the extension registry mementos. 168 final IExtensionRegistry registry = Platform.getExtensionRegistry(); 169 int commandDefinitionCount = 0; 170 int handlerDefinitionCount = 0; 171 int handlerSubmissionCount = 0; 172 final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[3][]; 173 174 // Sort the commands extension point based on element name. 175 final IConfigurationElement[] commandsExtensionPoint = registry.getConfigurationElementsFor(EXTENSION_COMMANDS); 176 for (final IConfigurationElement configurationElement : commandsExtensionPoint) { 177 final String name = configurationElement.getName(); 178 179 // Check if it is a handler submission or a command definition. 180 if (TAG_HANDLER_SUBMISSION.equals(name)) { 181 addElementToIndexedArray(configurationElement, indexedConfigurationElements, INDEX_HANDLER_SUBMISSIONS, 182 handlerSubmissionCount++); 183 } else if (TAG_COMMAND.equals(name)) { 184 addElementToIndexedArray(configurationElement, indexedConfigurationElements, INDEX_COMMAND_DEFINITIONS, 185 commandDefinitionCount++); 186 } 187 } 188 189 // Sort the handler extension point based on element name. 190 final IConfigurationElement[] handlersExtensionPoint = registry.getConfigurationElementsFor(EXTENSION_HANDLERS); 191 for (final IConfigurationElement configurationElement : handlersExtensionPoint) { 192 final String name = configurationElement.getName(); 193 194 // Check if it is a handler submission or a command definition. 195 if (TAG_HANDLER.equals(name)) { 196 addElementToIndexedArray(configurationElement, indexedConfigurationElements, INDEX_HANDLER_DEFINITIONS, 197 handlerDefinitionCount++); 198 } 199 } 200 201 clearActivations(handlerService); 202 readDefaultHandlersFromRegistry(indexedConfigurationElements[INDEX_COMMAND_DEFINITIONS], 203 commandDefinitionCount); 204 readHandlerSubmissionsFromRegistry(indexedConfigurationElements[INDEX_HANDLER_SUBMISSIONS], 205 handlerSubmissionCount); 206 readHandlersFromRegistry(indexedConfigurationElements[INDEX_HANDLER_DEFINITIONS], handlerDefinitionCount); 207 } 208 209 /** 210 * Reads the default handlers from an array of command elements from the 211 * commands extension point. 212 * 213 * @param configurationElements The configuration elements in the commands 214 * extension point; must not be 215 * <code>null</code>, but may be empty. 216 * @param configurationElementCount The number of configuration elements that 217 * are really in the array. 218 */ readDefaultHandlersFromRegistry(final IConfigurationElement[] configurationElements, final int configurationElementCount)219 private void readDefaultHandlersFromRegistry(final IConfigurationElement[] configurationElements, 220 final int configurationElementCount) { 221 for (int i = 0; i < configurationElementCount; i++) { 222 final IConfigurationElement configurationElement = configurationElements[i]; 223 224 /* 225 * Read out the command identifier. This was already checked by 226 * <code>CommandPersistence</code>, so we'll just ignore any problems here. 227 */ 228 final String commandId = readOptional(configurationElement, ATT_ID); 229 if (commandId == null) { 230 continue; 231 } 232 233 // Check to see if we have a default handler of any kind. 234 if ((configurationElement.getAttribute(ATT_DEFAULT_HANDLER) == null) 235 && (configurationElement.getChildren(TAG_DEFAULT_HANDLER).length == 0)) { 236 continue; 237 } 238 239 handlerActivations.add(handlerService.activateHandler(commandId, 240 new HandlerProxy(commandId, configurationElement, ATT_DEFAULT_HANDLER))); 241 } 242 } 243 244 /** 245 * Reads all of the handlers from the handlers extension point. 246 * 247 * @param configurationElements The configuration elements in the commands 248 * extension point; must not be 249 * <code>null</code>, but may be empty. 250 * @param configurationElementCount The number of configuration elements that 251 * are really in the array. 252 * @param handlerService The handler service to which the handlers 253 * should be added; must not be 254 * <code>null</code>. 255 */ readHandlersFromRegistry(final IConfigurationElement[] configurationElements, final int configurationElementCount)256 private void readHandlersFromRegistry(final IConfigurationElement[] configurationElements, 257 final int configurationElementCount) { 258 final List<IStatus> warningsToLog = new ArrayList<>(1); 259 260 for (int i = 0; i < configurationElementCount; i++) { 261 final IConfigurationElement configurationElement = configurationElements[i]; 262 263 // Read out the command identifier. 264 final String commandId = readRequired(configurationElement, ATT_COMMAND_ID, warningsToLog, 265 "Handlers need a command id"); //$NON-NLS-1$ 266 if (commandId == null) { 267 continue; 268 } 269 270 // Check to see if we have a handler class. 271 if (!checkClass(configurationElement, warningsToLog, "Handlers need a class", commandId)) { //$NON-NLS-1$ 272 continue; 273 } 274 275 // Get the activeWhen and enabledWhen expressions. 276 final Expression activeWhenExpression = readWhenElement(configurationElement, TAG_ACTIVE_WHEN, commandId, 277 warningsToLog); 278 if (activeWhenExpression == ERROR_EXPRESSION) { 279 continue; 280 } 281 final Expression enabledWhenExpression = readWhenElement(configurationElement, TAG_ENABLED_WHEN, commandId, 282 warningsToLog); 283 if (enabledWhenExpression == ERROR_EXPRESSION) { 284 continue; 285 } 286 287 final IHandler proxy = new HandlerProxy(commandId, configurationElement, ATT_CLASS, enabledWhenExpression, 288 evaluationService); 289 handlerActivations.add(handlerService.activateHandler(commandId, proxy, activeWhenExpression)); 290 291 // Read out the help context identifier. 292 final String helpContextId = readOptional(configurationElement, ATT_HELP_CONTEXT_ID); 293 handlerService.setHelpContextId(proxy, helpContextId); 294 } 295 296 logWarnings(warningsToLog, 297 "Warnings while parsing the handlers from the 'org.eclipse.ui.handlers' extension point."); //$NON-NLS-1$ 298 } 299 300 /** 301 * Reads all of the handler submissions from the commands extension point. 302 * 303 * @param configurationElements The configuration elements in the commands 304 * extension point; must not be 305 * <code>null</code>, but may be empty. 306 * @param configurationElementCount The number of configuration elements that 307 * are really in the array. 308 * @param handlerService The handler service to which the handlers 309 * should be added; must not be 310 * <code>null</code>. 311 */ readHandlerSubmissionsFromRegistry(final IConfigurationElement[] configurationElements, final int configurationElementCount)312 private void readHandlerSubmissionsFromRegistry(final IConfigurationElement[] configurationElements, 313 final int configurationElementCount) { 314 final List<IStatus> warningsToLog = new ArrayList<>(1); 315 316 for (int i = 0; i < configurationElementCount; i++) { 317 final IConfigurationElement configurationElement = configurationElements[i]; 318 319 // Read out the command identifier. 320 final String commandId = readRequired(configurationElement, ATT_COMMAND_ID, warningsToLog, 321 "Handler submissions need a command id"); //$NON-NLS-1$ 322 if (commandId == null) { 323 continue; 324 } 325 326 handlerActivations.add(handlerService.activateHandler(commandId, 327 new LegacyHandlerWrapper(new LegacyHandlerProxy(configurationElement)))); 328 } 329 330 logWarnings(warningsToLog, 331 "Warnings while parsing the handler submissions from the 'org.eclipse.ui.commands' extension point."); //$NON-NLS-1$ 332 } 333 } 334