1 /*******************************************************************************
2  * Copyright (c) 2013, 2018 Remain BV, Industrial-TSI BV and others.
3  *
4  *
5  * This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License 2.0
7  * which accompanies this distribution, and is available at
8  * https://www.eclipse.org/legal/epl-2.0/
9  *
10  * SPDX-License-Identifier: EPL-2.0
11  *
12  * Contributors:
13  * Wim Jongmam <wim.jongman@remainsoftware.com> - initial API and implementation
14  * Steven Spungin <steven@spungin.tv> - Ongoing Maintenance
15  ******************************************************************************/
16 package org.eclipse.e4.tools.emf.ui.internal.imp;
17 
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.List;
22 
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.IConfigurationElement;
25 import org.eclipse.core.runtime.IExtension;
26 import org.eclipse.core.runtime.IExtensionRegistry;
27 import org.eclipse.e4.tools.emf.ui.common.IExtensionLookup;
28 import org.eclipse.e4.ui.model.application.MApplication;
29 import org.eclipse.e4.ui.model.application.MApplicationElement;
30 import org.eclipse.e4.ui.model.application.commands.MCategory;
31 import org.eclipse.e4.ui.model.application.commands.MCommand;
32 import org.eclipse.e4.ui.model.application.commands.MCommandsFactory;
33 import org.eclipse.e4.ui.model.application.commands.MHandler;
34 import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor;
35 import org.eclipse.e4.ui.model.application.ui.advanced.MAdvancedFactory;
36 import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
37 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
38 import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicPackageImpl;
39 import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
40 import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
41 import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
42 import org.eclipse.e4.ui.workbench.UIEvents.ApplicationElement;
43 import org.eclipse.emf.ecore.util.EcoreUtil;
44 import org.osgi.framework.BundleContext;
45 import org.osgi.framework.FrameworkUtil;
46 import org.osgi.framework.InvalidSyntaxException;
47 import org.osgi.framework.ServiceReference;
48 
49 @SuppressWarnings("deprecation")
50 public class RegistryUtil {
51 
52 	private static final String PLATFORM = "platform:"; //$NON-NLS-1$
53 	private static final String EMPTY_STRING = ""; //$NON-NLS-1$
54 	private static final String COMMAND_ID = "commandId"; //$NON-NLS-1$
55 	private static final String CATEGORY_ID = "categoryId"; //$NON-NLS-1$
56 	private static final String COMPATIBILITY_VIEW = "bundleclass://org.eclipse.ui.workbench/org.eclipse.ui.internal.e4.compatibility.CompatibilityView"; //$NON-NLS-1$
57 	private static final String VIEW_MENU = "ViewMenu"; //$NON-NLS-1$
58 	private static final String LAUNCHER = "launcher"; //$NON-NLS-1$
59 	private static final String EDITOR = "editor"; //$NON-NLS-1$
60 	private static final String VIEW = "View"; //$NON-NLS-1$
61 	private static final String CATEGORY_TAG = "categoryTag:"; //$NON-NLS-1$
62 	private static final String CATEGORY = "category"; //$NON-NLS-1$
63 	private static final String CLASS = "class"; //$NON-NLS-1$
64 	private static final String DESCRIPTION = "description"; //$NON-NLS-1$
65 	private static final String ICON = "icon"; //$NON-NLS-1$
66 	private static final String NAME = "name"; //$NON-NLS-1$
67 	private static final String ID = "id"; //$NON-NLS-1$
68 	public final static String HINT_VIEW = "view"; //$NON-NLS-1$
69 	public final static String HINT_EDITOR = EDITOR;
70 	public final static String HINT_COMPAT_VIEW = "compatibilityView"; //$NON-NLS-1$
71 
72 	/**
73 	 *
74 	 * @param t
75 	 * @param application
76 	 * @param elements
77 	 */
getModelElements(Class<? extends MApplicationElement> t, String hint, MApplication application, IConfigurationElement... elements)78 	public static MApplicationElement[] getModelElements(Class<? extends MApplicationElement> t, String hint,
79 			MApplication application, IConfigurationElement... elements) {
80 
81 		Assert.isNotNull(t);
82 		Assert.isNotNull(elements);
83 		Assert.isTrue(elements.length > 0);
84 
85 		if (t.equals(MCommand.class)) {
86 			return getCommands(elements, application);
87 		} else if (t.equals(MCategory.class)) {
88 			return getCategories(elements);
89 		} else if (t.equals(MPerspective.class)) {
90 			return getPerspectives(elements);
91 		} else if (t.equals(MPart.class) && HINT_COMPAT_VIEW.equals(hint)) {
92 			return getViewsAsCompatibilityViews(elements);
93 		} else if (t.equals(MPart.class)) {
94 			return getViews(elements);
95 		} else if (t.equals(MHandler.class)) {
96 			return getHandlers(elements, application);
97 		} else if (t.equals(MPartDescriptor.class) && HINT_EDITOR.equals(hint)) {
98 			return getEditorPartDescriptors(elements);
99 		} else if (t.equals(MPartDescriptor.class) && HINT_VIEW.equals(hint)) {
100 			return getViewPartDescriptors(elements);
101 		} else if (t.equals(MPartDescriptor.class) && HINT_COMPAT_VIEW.equals(hint)) {
102 			return getPartDescriptorsAsCompatibilyViews(elements);
103 		}
104 		return new MApplicationElement[0];
105 	}
106 
getCommands(IConfigurationElement[] elements, MApplication application)107 	private static MCommand[] getCommands(IConfigurationElement[] elements, MApplication application) {
108 
109 		final ArrayList<MCommand> result = new ArrayList<>();
110 
111 		final MCommandsFactory commandsFactory = MCommandsFactory.INSTANCE;
112 
113 		for (final IConfigurationElement element : elements) {
114 
115 			final MCommand command = commandsFactory.createCommand();
116 			command.setCommandName(element.getAttribute(NAME));
117 			command.setDescription(element.getAttribute(DESCRIPTION));
118 			command.setElementId(element.getAttribute(ID));
119 			final String catId = element.getAttribute(CATEGORY_ID);
120 
121 			if (catId != null && catId.trim().length() > 0) {
122 				final List<MCategory> categories = application.getCategories();
123 				for (final MCategory category : categories) {
124 					if (category.getElementId().equals(catId)) {
125 						command.setCategory(category);
126 						break;
127 					}
128 				}
129 			}
130 
131 			result.add(command);
132 		}
133 
134 		return result.toArray(new MCommand[0]);
135 	}
136 
getPerspectives(IConfigurationElement[] elements)137 	private static MPerspective[] getPerspectives(IConfigurationElement[] elements) {
138 
139 		final ArrayList<MPerspective> result = new ArrayList<>();
140 
141 		final MAdvancedFactory factory = MAdvancedFactory.INSTANCE;
142 
143 		for (final IConfigurationElement element : elements) {
144 			final MPerspective perspective = factory.createPerspective();
145 			perspective.setLabel(element.getAttribute(NAME));
146 			perspective.setIconURI(getIconURI(element, ICON));
147 			perspective.setElementId(element.getAttribute(ID));
148 			perspective.setToBeRendered(true);
149 			perspective.setVisible(true);
150 			result.add(perspective);
151 		}
152 
153 		return result.toArray(new MPerspective[0]);
154 	}
155 
getCategories(IConfigurationElement[] elements)156 	private static MCategory[] getCategories(IConfigurationElement[] elements) {
157 
158 		final ArrayList<MCategory> result = new ArrayList<>();
159 
160 		final MCommandsFactory commandsFactory = MCommandsFactory.INSTANCE;
161 
162 		for (final IConfigurationElement element : elements) {
163 
164 			final MCategory category = commandsFactory.createCategory();
165 			category.setDescription(element.getAttribute(DESCRIPTION));
166 			category.setElementId(element.getAttribute(ID));
167 			category.setName(element.getAttribute(NAME));
168 
169 			result.add(category);
170 		}
171 
172 		return result.toArray(new MCategory[0]);
173 	}
174 
getViews(IConfigurationElement[] elements)175 	private static MPart[] getViews(IConfigurationElement[] elements) {
176 
177 		final ArrayList<MPart> result = new ArrayList<>();
178 		for (final IConfigurationElement element : elements) {
179 			final MPart part = (MPart) EcoreUtil.create(BasicPackageImpl.Literals.PART);
180 			part.setElementId(element.getAttribute(ID));
181 			part.setLabel(element.getAttribute(NAME));
182 			part.setIconURI(getIconURI(element, ICON));
183 			part.setContributionURI(getContributionURI(element, CLASS));
184 			part.setToBeRendered(true);
185 			part.setVisible(true);
186 			part.setToolbar(createToolBar(part));
187 			part.getMenus().add(createViewMenu(part));
188 			part.setCloseable(true);
189 			part.getTags().add(VIEW);
190 			if (element.getAttribute(CATEGORY) != null) {
191 				part.getTags().add(CATEGORY_TAG + element.getAttribute(CATEGORY));
192 			}
193 
194 			result.add(part);
195 		}
196 		return result.toArray(new MPart[0]);
197 	}
198 
createToolBar(MPart part)199 	private static MToolBar createToolBar(MPart part) {
200 		final MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
201 		toolBar.setElementId(part.getElementId());
202 		return toolBar;
203 	}
204 
createViewMenu(MPart part)205 	private static MMenu createViewMenu(MPart part) {
206 		final MMenu menu = MMenuFactory.INSTANCE.createMenu();
207 		menu.setElementId(part.getElementId());
208 		menu.getTags().add(VIEW_MENU);
209 		return menu;
210 	}
211 
getViewsAsCompatibilityViews(IConfigurationElement[] elements)212 	private static MPart[] getViewsAsCompatibilityViews(IConfigurationElement[] elements) {
213 		final ArrayList<MPart> result = new ArrayList<>();
214 		final MPart[] parts = getViews(elements);
215 		for (final MPart part : parts) {
216 			part.setContributionURI(COMPATIBILITY_VIEW);
217 			result.add(part);
218 		}
219 		return result.toArray(new MPart[0]);
220 	}
221 
getPartDescriptorsAsCompatibilyViews(IConfigurationElement[] elements)222 	private static MPartDescriptor[] getPartDescriptorsAsCompatibilyViews(IConfigurationElement[] elements) {
223 		final ArrayList<MPartDescriptor> result = new ArrayList<>();
224 		final MPartDescriptor[] parts = getViewPartDescriptors(elements);
225 		for (final MPartDescriptor part : parts) {
226 			part.setContributionURI(COMPATIBILITY_VIEW);
227 			result.add(part);
228 		}
229 		return result.toArray(new MPartDescriptor[0]);
230 	}
231 
getEditorPartDescriptors(IConfigurationElement[] elements)232 	private static MPartDescriptor[] getEditorPartDescriptors(IConfigurationElement[] elements) {
233 
234 		final ArrayList<MPartDescriptor> result = new ArrayList<>();
235 		for (final IConfigurationElement element : elements) {
236 			final MPartDescriptor part = (MPartDescriptor) EcoreUtil
237 					.create(org.eclipse.e4.ui.model.application.descriptor.basic.impl.BasicPackageImpl.Literals.PART_DESCRIPTOR);
238 			part.setElementId(element.getAttribute(ID));
239 			part.setLabel(element.getAttribute(NAME));
240 			part.setIconURI(getIconURI(element, ICON));
241 			if (element.getAttribute(CLASS) != null) {
242 				part.setContributionURI(getContributionURI(element, CLASS));
243 			} else {
244 				part.setContributionURI(getContributionURI(element, LAUNCHER));
245 			}
246 			part.setDirtyable(true);
247 			part.setAllowMultiple(true);
248 
249 			final MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
250 			toolBar.setElementId(part.getElementId());
251 			part.setToolbar(toolBar);
252 
253 			final MMenu menu = MMenuFactory.INSTANCE.createMenu();
254 			menu.setElementId(part.getElementId());
255 			menu.getTags().add(VIEW_MENU);
256 			part.getMenus().add(menu);
257 
258 			part.setCloseable(true);
259 			result.add(part);
260 		}
261 
262 		return result.toArray(new MPartDescriptor[0]);
263 	}
264 
getViewPartDescriptors(IConfigurationElement[] elements)265 	private static MPartDescriptor[] getViewPartDescriptors(IConfigurationElement[] elements) {
266 
267 		final ArrayList<MPartDescriptor> result = new ArrayList<>();
268 		for (final IConfigurationElement element : elements) {
269 			final MPartDescriptor part = (MPartDescriptor) EcoreUtil
270 					.create(org.eclipse.e4.ui.model.application.descriptor.basic.impl.BasicPackageImpl.Literals.PART_DESCRIPTOR);
271 			part.setElementId(element.getAttribute(ID));
272 			part.setLabel(element.getAttribute(NAME));
273 			part.setIconURI(getIconURI(element, ICON));
274 
275 			final MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
276 			toolBar.setElementId(part.getElementId());
277 			part.setToolbar(toolBar);
278 
279 			final MMenu menu = MMenuFactory.INSTANCE.createMenu();
280 			menu.setElementId(part.getElementId());
281 			menu.getTags().add(VIEW_MENU);
282 			part.getMenus().add(menu);
283 
284 			part.setCloseable(true);
285 			result.add(part);
286 		}
287 
288 		return result.toArray(new MPartDescriptor[0]);
289 	}
290 
getHandlers(IConfigurationElement[] elements, MApplication application)291 	private static MHandler[] getHandlers(IConfigurationElement[] elements, MApplication application) {
292 
293 		final ArrayList<MHandler> result = new ArrayList<>();
294 		for (final IConfigurationElement element : elements) {
295 			final MHandler hand = MCommandsFactory.INSTANCE.createHandler();
296 			hand.setElementId(element.getAttribute(ID));
297 			hand.setContributionURI(getContributionURI(element, CLASS));
298 
299 			final String cmdId = element.getAttribute(COMMAND_ID);
300 
301 			if (cmdId != null && cmdId.trim().length() > 0) {
302 				final List<MCommand> categories = application.getCommands();
303 				for (final MCommand command : categories) {
304 					if (command.getElementId().equals(cmdId)) {
305 						hand.setCommand(command);
306 						break;
307 					}
308 				}
309 			}
310 			result.add(hand);
311 		}
312 		return result.toArray(new MHandler[0]);
313 	}
314 
getIconURI(IConfigurationElement element, String attribute)315 	private static String getIconURI(IConfigurationElement element, String attribute) {
316 		if (element.getAttribute(attribute) == null) {
317 			return EMPTY_STRING;
318 		}
319 		// FIXME any other cases?
320 		if (element.getAttribute(attribute).startsWith(PLATFORM)) {
321 			return element.getAttribute(attribute);
322 		}
323 		return "platform:/plugin/" + element.getContributor().getName() + "/" + element.getAttribute(attribute); //$NON-NLS-1$ //$NON-NLS-2$
324 	}
325 
getContributionURI(IConfigurationElement element, String attribute)326 	private static String getContributionURI(IConfigurationElement element, String attribute) {
327 		return "bundleclass://" + element.getContributor().getName() + "/" + element.getAttribute(attribute); //$NON-NLS-1$ //$NON-NLS-2$
328 	}
329 
330 	/**
331 	 * Returns a list of bundle id's that have extension to the passed extension
332 	 * point.
333 	 *
334 	 * @param registry
335 	 * @param extensionPoint
336 	 * @return the bundle ids as an array of Strings
337 	 */
getProvidingBundles(IExtensionRegistry registry, String extensionPoint, boolean isLive)338 	public static String[] getProvidingBundles(IExtensionRegistry registry, String extensionPoint, boolean isLive) {
339 
340 		final IExtensionLookup service = getService(IExtensionLookup.class, null);
341 
342 		if (service == null) {
343 			return new String[] { "No " + IExtensionLookup.class.getName() + " service found." }; //$NON-NLS-1$ //$NON-NLS-2$
344 		}
345 
346 		final ArrayList<String> result = new ArrayList<>();
347 
348 		final IExtension[] extensions = service.findExtensions(extensionPoint, isLive);
349 		for (final IExtension extension : extensions) {
350 			final IConfigurationElement[] elements = extension.getConfigurationElements();
351 			for (final IConfigurationElement element : elements) {
352 				if (!result.contains(element.getContributor().getName())) {
353 					result.add(element.getContributor().getName());
354 				}
355 			}
356 		}
357 
358 		final String[] resultArray = result.toArray(new String[0]);
359 		Arrays.sort(resultArray);
360 		return resultArray;
361 	}
362 
363 	/**
364 	 *
365 	 * @param registry
366 	 * @param struct
367 	 * @return the array of {@link IConfigurationElement} objects that meets the
368 	 *         passed criteria.
369 	 */
getExtensions(IExtensionRegistry registry, RegistryStruct struct, boolean isLive)370 	public static IConfigurationElement[] getExtensions(IExtensionRegistry registry, RegistryStruct struct,
371 			boolean isLive) {
372 
373 		final IExtensionLookup service = getService(IExtensionLookup.class, null);
374 		if (struct == null || service == null) {
375 			return new IConfigurationElement[0];
376 		}
377 
378 		final ArrayList<IConfigurationElement> result = new ArrayList<>();
379 
380 		final IExtension[] extensions = service.findExtensions(struct.getExtensionPoint(), isLive);
381 		for (final IExtension extension : extensions) {
382 			final IConfigurationElement[] elements = extension.getConfigurationElements();
383 			for (final IConfigurationElement element : elements) {
384 				if (element.getContributor().getName().equals(struct.getBundle())) {
385 					if (element.getName().equals(struct.getExtensionPointName())) {
386 						result.add(element);
387 					}
388 				}
389 			}
390 		}
391 
392 		return result.toArray(new IConfigurationElement[0]);
393 	}
394 
395 	/**
396 	 * This will return a structure that contains the registry information we
397 	 * are looking for.
398 	 *
399 	 * @param applicationElement
400 	 * @return the structure that matches the extension registry to the passed {@link ApplicationElement}
401 	 */
getStruct(Class<? extends MApplicationElement> applicationElement, String hint)402 	public static RegistryStruct getStruct(Class<? extends MApplicationElement> applicationElement, String hint) {
403 
404 		if (applicationElement == MCommand.class) {
405 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.commands", "command", NAME); //$NON-NLS-1$ //$NON-NLS-2$
406 		} else if (applicationElement == MCategory.class) {
407 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.commands", CATEGORY, NAME); //$NON-NLS-1$
408 		} else if (applicationElement == MPerspective.class) {
409 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.perspectives", "perspective", NAME); //$NON-NLS-1$ //$NON-NLS-2$
410 		} else if (applicationElement == MPart.class) {
411 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.views", "view", NAME); //$NON-NLS-1$ //$NON-NLS-2$
412 		} else if (applicationElement == MHandler.class) {
413 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.handlers", "handler", COMMAND_ID); //$NON-NLS-1$ //$NON-NLS-2$
414 		} else if (applicationElement == MPart.class) {
415 			return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.views", "view", NAME); //$NON-NLS-1$ //$NON-NLS-2$
416 		}
417 
418 		else if (applicationElement == MPartDescriptor.class) {
419 			if (hint == HINT_EDITOR)
420 			{
421 				return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.editors", EDITOR, NAME); //$NON-NLS-1$
422 			}
423 			if (hint == HINT_VIEW || hint == HINT_COMPAT_VIEW)
424 			{
425 				return new RegistryStruct(EMPTY_STRING, "org.eclipse.ui.views", "view", NAME); //$NON-NLS-1$ //$NON-NLS-2$
426 			}
427 		}
428 
429 		return null;
430 	}
431 
getService(Class<T> clazz, String filter)432 	private static <T> T getService(Class<T> clazz, String filter) {
433 
434 		try {
435 			final BundleContext context = FrameworkUtil.getBundle(RegistryUtil.class).getBundleContext();
436 			Collection<ServiceReference<T>> references;
437 			references = context.getServiceReferences(clazz, filter);
438 			for (final ServiceReference<T> reference : references) {
439 				return context.getService(reference);
440 			}
441 		} catch (final InvalidSyntaxException e) {
442 			// FIXME log
443 		}
444 		return null;
445 	}
446 }
447