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