1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 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.activities;
16 
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Iterator;
20 import java.util.List;
21 import org.eclipse.core.expressions.Expression;
22 import org.eclipse.core.expressions.ExpressionConverter;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IConfigurationElement;
25 import org.eclipse.core.runtime.IExtension;
26 import org.eclipse.core.runtime.IExtensionDelta;
27 import org.eclipse.core.runtime.IExtensionRegistry;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.jface.preference.IPreferenceStore;
31 import org.eclipse.ui.PlatformUI;
32 import org.eclipse.ui.internal.WorkbenchPlugin;
33 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
34 import org.eclipse.ui.internal.util.ConfigurationElementMemento;
35 import org.eclipse.ui.statushandlers.StatusManager;
36 
37 final class ExtensionActivityRegistry extends AbstractActivityRegistry {
38 
39 	/**
40 	 * Prefix for all activity preferences
41 	 */
42 	private static final String PREFIX = "UIActivities."; //$NON-NLS-1$
43 
44 	private List<ActivityRequirementBindingDefinition> activityRequirementBindingDefinitions;
45 
46 	private List<ActivityDefinition> activityDefinitions;
47 
48 	private List<ActivityPatternBindingDefinition> activityPatternBindingDefinitions;
49 
50 	private List<CategoryActivityBindingDefinition> categoryActivityBindingDefinitions;
51 
52 	private List<CategoryDefinition> categoryDefinitions;
53 
54 	private List<String> defaultEnabledActivities;
55 
56 	private IExtensionRegistry extensionRegistry;
57 
ExtensionActivityRegistry(IExtensionRegistry extensionRegistry)58 	ExtensionActivityRegistry(IExtensionRegistry extensionRegistry) {
59 		if (extensionRegistry == null) {
60 			throw new NullPointerException();
61 		}
62 
63 		this.extensionRegistry = extensionRegistry;
64 
65 		this.extensionRegistry.addRegistryChangeListener(registryChangeEvent -> {
66 			IExtensionDelta[] extensionDeltas = registryChangeEvent.getExtensionDeltas(Persistence.PACKAGE_PREFIX,
67 					Persistence.PACKAGE_BASE);
68 
69 			if (extensionDeltas.length != 0) {
70 				load();
71 			}
72 		});
73 
74 		load();
75 	}
76 
getNamespace(IConfigurationElement configurationElement)77 	private String getNamespace(IConfigurationElement configurationElement) {
78 		String namespace = null;
79 
80 		if (configurationElement != null) {
81 			IExtension extension = configurationElement.getDeclaringExtension();
82 
83 			if (extension != null) {
84 				namespace = extension.getContributor().getName();
85 			}
86 		}
87 
88 		return namespace;
89 	}
90 
91 	/**
92 	 * Returns the activity definition found at this id.
93 	 *
94 	 * @param id <code>ActivityDefinition</code> id.
95 	 * @return <code>ActivityDefinition</code> with given id or <code>null</code> if
96 	 *         not found.
97 	 */
getActivityDefinitionById(String id)98 	private ActivityDefinition getActivityDefinitionById(String id) {
99 		int size = activityDefinitions.size();
100 		for (int i = 0; i < size; i++) {
101 			ActivityDefinition activityDef = activityDefinitions.get(i);
102 			if (activityDef.getId().equals(id)) {
103 				return activityDef;
104 			}
105 		}
106 		return null;
107 	}
108 
load()109 	private void load() {
110 		if (activityRequirementBindingDefinitions == null) {
111 			activityRequirementBindingDefinitions = new ArrayList<>();
112 		} else {
113 			activityRequirementBindingDefinitions.clear();
114 		}
115 
116 		if (activityDefinitions == null) {
117 			activityDefinitions = new ArrayList<>();
118 		} else {
119 			activityDefinitions.clear();
120 		}
121 
122 		if (activityPatternBindingDefinitions == null) {
123 			activityPatternBindingDefinitions = new ArrayList<>();
124 		} else {
125 			activityPatternBindingDefinitions.clear();
126 		}
127 
128 		if (categoryActivityBindingDefinitions == null) {
129 			categoryActivityBindingDefinitions = new ArrayList<>();
130 		} else {
131 			categoryActivityBindingDefinitions.clear();
132 		}
133 
134 		if (categoryDefinitions == null) {
135 			categoryDefinitions = new ArrayList<>();
136 		} else {
137 			categoryDefinitions.clear();
138 		}
139 
140 		if (defaultEnabledActivities == null) {
141 			defaultEnabledActivities = new ArrayList<>();
142 		} else {
143 			defaultEnabledActivities.clear();
144 		}
145 
146 		IConfigurationElement[] configurationElements = extensionRegistry
147 				.getConfigurationElementsFor(Persistence.PACKAGE_FULL);
148 
149 		for (IConfigurationElement configurationElement : configurationElements) {
150 			String name = configurationElement.getName();
151 
152 			switch (name) {
153 			case Persistence.TAG_ACTIVITY_REQUIREMENT_BINDING:
154 				readActivityRequirementBindingDefinition(configurationElement);
155 				break;
156 			case Persistence.TAG_ACTIVITY:
157 				readActivityDefinition(configurationElement);
158 				break;
159 			case Persistence.TAG_ACTIVITY_PATTERN_BINDING:
160 				readActivityPatternBindingDefinition(configurationElement);
161 				break;
162 			case Persistence.TAG_CATEGORY_ACTIVITY_BINDING:
163 				readCategoryActivityBindingDefinition(configurationElement);
164 				break;
165 			case Persistence.TAG_CATEGORY:
166 				readCategoryDefinition(configurationElement);
167 				break;
168 			case Persistence.TAG_DEFAULT_ENABLEMENT:
169 				readDefaultEnablement(configurationElement);
170 				break;
171 			default:
172 				break;
173 			}
174 		}
175 
176 		// merge enablement overrides from plugin_customization.ini
177 		IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
178 		for (ActivityDefinition activityDef : activityDefinitions) {
179 			String id = activityDef.getId();
180 			String preferenceKey = createPreferenceKey(id);
181 			if ("".equals(store.getDefaultString(preferenceKey))) //$NON-NLS-1$
182 				continue;
183 			if (store.getDefaultBoolean(preferenceKey)) {
184 				if (!defaultEnabledActivities.contains(id) && activityDef.getEnabledWhen() == null)
185 					defaultEnabledActivities.add(id);
186 			} else {
187 				defaultEnabledActivities.remove(id);
188 			}
189 		}
190 
191 		// Removal of all defaultEnabledActivites which target to expression
192 		// controlled activities.
193 		for (int i = 0; i < defaultEnabledActivities.size();) {
194 			String id = defaultEnabledActivities.get(i);
195 			ActivityDefinition activityDef = getActivityDefinitionById(id);
196 			if (activityDef != null && activityDef.getEnabledWhen() != null) {
197 				defaultEnabledActivities.remove(i);
198 				StatusManager.getManager().handle(new Status(IStatus.WARNING, PlatformUI.PLUGIN_ID,
199 						"Default enabled activity declarations will be ignored (id: " + id + ")")); //$NON-NLS-1$ //$NON-NLS-2$
200 			} else {
201 				i++;
202 			}
203 		}
204 
205 		// remove all requirement bindings that reference expression-bound activities
206 		for (Iterator<ActivityRequirementBindingDefinition> i = activityRequirementBindingDefinitions.iterator(); i
207 				.hasNext();) {
208 			ActivityRequirementBindingDefinition bindingDef = i.next();
209 			ActivityDefinition activityDef = getActivityDefinitionById(bindingDef.getRequiredActivityId());
210 			if (activityDef != null && activityDef.getEnabledWhen() != null) {
211 				i.remove();
212 				StatusManager.getManager().handle(new Status(IStatus.WARNING, PlatformUI.PLUGIN_ID,
213 						"Expression activity cannot have requirements (id: " + activityDef.getId() + ")")); //$NON-NLS-1$ //$NON-NLS-2$
214 				continue;
215 			}
216 
217 			activityDef = getActivityDefinitionById(bindingDef.getActivityId());
218 			if (activityDef != null && activityDef.getEnabledWhen() != null) {
219 				i.remove();
220 				StatusManager.getManager().handle(new Status(IStatus.WARNING, PlatformUI.PLUGIN_ID,
221 						"Expression activity cannot be required (id: " + activityDef.getId() + ")")); //$NON-NLS-1$ //$NON-NLS-2$
222 			}
223 		}
224 
225 		boolean activityRegistryChanged = false;
226 
227 		if (!activityRequirementBindingDefinitions.equals(super.activityRequirementBindingDefinitions)) {
228 			super.activityRequirementBindingDefinitions = Collections
229 					.unmodifiableList(new ArrayList<>(activityRequirementBindingDefinitions));
230 			activityRegistryChanged = true;
231 		}
232 
233 		if (!activityDefinitions.equals(super.activityDefinitions)) {
234 			super.activityDefinitions = Collections.unmodifiableList(new ArrayList<>(activityDefinitions));
235 			activityRegistryChanged = true;
236 		}
237 
238 		if (!activityPatternBindingDefinitions.equals(super.activityPatternBindingDefinitions)) {
239 			super.activityPatternBindingDefinitions = Collections
240 					.unmodifiableList(new ArrayList<>(activityPatternBindingDefinitions));
241 			activityRegistryChanged = true;
242 		}
243 
244 		if (!categoryActivityBindingDefinitions.equals(super.categoryActivityBindingDefinitions)) {
245 			super.categoryActivityBindingDefinitions = Collections
246 					.unmodifiableList(new ArrayList<>(categoryActivityBindingDefinitions));
247 			activityRegistryChanged = true;
248 		}
249 
250 		if (!categoryDefinitions.equals(super.categoryDefinitions)) {
251 			super.categoryDefinitions = Collections.unmodifiableList(new ArrayList<>(categoryDefinitions));
252 			activityRegistryChanged = true;
253 		}
254 
255 		if (!defaultEnabledActivities.equals(super.defaultEnabledActivities)) {
256 			super.defaultEnabledActivities = Collections.unmodifiableList(new ArrayList<>(defaultEnabledActivities));
257 			activityRegistryChanged = true;
258 		}
259 
260 		if (activityRegistryChanged) {
261 			fireActivityRegistryChanged();
262 		}
263 	}
264 
265 	/**
266 	 * Create the preference key for the activity.
267 	 *
268 	 * @param activityId the activity id.
269 	 * @return String a preference key representing the activity.
270 	 */
createPreferenceKey(String activityId)271 	private String createPreferenceKey(String activityId) {
272 		return PREFIX + activityId;
273 	}
274 
readDefaultEnablement(IConfigurationElement configurationElement)275 	private void readDefaultEnablement(IConfigurationElement configurationElement) {
276 		String enabledActivity = Persistence
277 				.readDefaultEnablement(new ConfigurationElementMemento(configurationElement));
278 
279 		if (enabledActivity != null) {
280 			defaultEnabledActivities.add(enabledActivity);
281 		}
282 
283 	}
284 
readActivityRequirementBindingDefinition(IConfigurationElement configurationElement)285 	private void readActivityRequirementBindingDefinition(IConfigurationElement configurationElement) {
286 		ActivityRequirementBindingDefinition activityRequirementBindingDefinition = Persistence
287 				.readActivityRequirementBindingDefinition(new ConfigurationElementMemento(configurationElement),
288 						getNamespace(configurationElement));
289 
290 		if (activityRequirementBindingDefinition != null) {
291 			activityRequirementBindingDefinitions.add(activityRequirementBindingDefinition);
292 		}
293 	}
294 
readActivityDefinition(IConfigurationElement configurationElement)295 	private void readActivityDefinition(IConfigurationElement configurationElement) {
296 		ActivityDefinition activityDefinition = Persistence.readActivityDefinition(
297 				new ConfigurationElementMemento(configurationElement), getNamespace(configurationElement));
298 
299 		if (activityDefinition != null) {
300 			// this is not ideal, but core expressions takes an
301 			// IConfigurationElement or a w3c dom Document
302 			IConfigurationElement[] enabledWhen = configurationElement
303 					.getChildren(IWorkbenchRegistryConstants.TAG_ENABLED_WHEN);
304 			if (enabledWhen.length == 1) {
305 				IConfigurationElement[] expElement = enabledWhen[0].getChildren();
306 				if (expElement.length == 1) {
307 					try {
308 						Expression expression = ExpressionConverter.getDefault().perform(expElement[0]);
309 						activityDefinition.setEnabledWhen(expression);
310 					} catch (CoreException e) {
311 						StatusManager.getManager().handle(e, WorkbenchPlugin.PI_WORKBENCH);
312 					}
313 				}
314 			}
315 			activityDefinitions.add(activityDefinition);
316 		}
317 	}
318 
readActivityPatternBindingDefinition(IConfigurationElement configurationElement)319 	private void readActivityPatternBindingDefinition(IConfigurationElement configurationElement) {
320 		ActivityPatternBindingDefinition activityPatternBindingDefinition = Persistence
321 				.readActivityPatternBindingDefinition(new ConfigurationElementMemento(configurationElement),
322 						getNamespace(configurationElement));
323 
324 		if (activityPatternBindingDefinition != null) {
325 			activityPatternBindingDefinitions.add(activityPatternBindingDefinition);
326 		}
327 	}
328 
readCategoryActivityBindingDefinition(IConfigurationElement configurationElement)329 	private void readCategoryActivityBindingDefinition(IConfigurationElement configurationElement) {
330 		CategoryActivityBindingDefinition categoryActivityBindingDefinition = Persistence
331 				.readCategoryActivityBindingDefinition(new ConfigurationElementMemento(configurationElement),
332 						getNamespace(configurationElement));
333 
334 		if (categoryActivityBindingDefinition != null) {
335 			categoryActivityBindingDefinitions.add(categoryActivityBindingDefinition);
336 		}
337 	}
338 
readCategoryDefinition(IConfigurationElement configurationElement)339 	private void readCategoryDefinition(IConfigurationElement configurationElement) {
340 		CategoryDefinition categoryDefinition = Persistence.readCategoryDefinition(
341 				new ConfigurationElementMemento(configurationElement), getNamespace(configurationElement));
342 
343 		if (categoryDefinition != null) {
344 			categoryDefinitions.add(categoryDefinition);
345 		}
346 	}
347 }
348