1 /*******************************************************************************
2  * Copyright (c) 2017 Red Hat Inc. 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  * - Mickael Istria (Red Hat Inc.)
13  *******************************************************************************/
14 package org.eclipse.ui.internal.genericeditor;
15 
16 import org.eclipse.core.expressions.ElementHandler;
17 import org.eclipse.core.expressions.EvaluationContext;
18 import org.eclipse.core.expressions.EvaluationResult;
19 import org.eclipse.core.expressions.Expression;
20 import org.eclipse.core.expressions.ExpressionConverter;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IConfigurationElement;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Platform;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.content.IContentType;
27 import org.eclipse.jface.text.source.ISourceViewer;
28 import org.eclipse.ui.texteditor.ITextEditor;
29 
30 /**
31  * This class wraps and proxies an instance of T provided through extensions and loads it lazily when it can contribute to the editor, then delegates all operations to actual instance.
32  *
33  * @param <T>
34  *            the actual type to proxy, typically the one defined on the extension point.
35  */
36 public class GenericContentTypeRelatedExtension<T> {
37 	private static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$
38 	private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$
39 	static final String CONTENT_TYPE_ATTRIBUTE = "contentType"; //$NON-NLS-1$
40 	private static final String ENABLED_WHEN_ATTRIBUTE = "enabledWhen"; //$NON-NLS-1$
41 
42 	public final IConfigurationElement extension;
43 	public final IContentType targetContentType;
44 	public final Expression enabledWhen;
45 
GenericContentTypeRelatedExtension(IConfigurationElement element)46 	public GenericContentTypeRelatedExtension(IConfigurationElement element) throws Exception {
47 		this.extension = element;
48 		this.targetContentType = Platform.getContentTypeManager().getContentType(element.getAttribute(CONTENT_TYPE_ATTRIBUTE));
49 		this.enabledWhen = buildEnabledWhen(element);
50 	}
51 
createDelegate()52 	@SuppressWarnings("unchecked") public T createDelegate() {
53 		try {
54 			return (T) extension.createExecutableExtension(CLASS_ATTRIBUTE);
55 		} catch (CoreException e) {
56 			GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, e.getMessage(), e));
57 		}
58 		return null;
59 	}
60 
61 	/**
62 	 * Returns the expression {@link Expression} declared in the <code>enabledWhen</code> element.
63 	 *
64 	 * @param configElement
65 	 *            the configuration element
66 	 * @return the expression {@link Expression} declared in the enabledWhen element.
67 	 * @throws CoreException
68 	 *             when enabledWhen expression is not valid.
69 	 */
buildEnabledWhen(IConfigurationElement configElement)70 	private static Expression buildEnabledWhen(IConfigurationElement configElement) throws CoreException {
71 		final IConfigurationElement[] children = configElement.getChildren(ENABLED_WHEN_ATTRIBUTE);
72 		if (children.length > 0) {
73 			IConfigurationElement[] subChildren = children[0].getChildren();
74 			if (subChildren.length != 1) {
75 				throw new CoreException(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, "One <enabledWhen> element is accepted. Disabling " //$NON-NLS-1$
76 						+ configElement.getAttribute(ID_ATTRIBUTE)));
77 			}
78 			final ElementHandler elementHandler = ElementHandler.getDefault();
79 			final ExpressionConverter converter = ExpressionConverter.getDefault();
80 			return elementHandler.create(converter, subChildren[0]);
81 		}
82 		return null;
83 	}
84 
85 	/**
86 	 * Returns true if the given viewer, editor matches the enabledWhen expression and false otherwise.
87 	 *
88 	 * @param viewer
89 	 *            the viewer
90 	 * @param editor
91 	 *            the editor
92 	 * @return true if the given viewer, editor matches the enabledWhen expression and false otherwise.
93 	 */
matches(ISourceViewer viewer, ITextEditor editor)94 	public boolean matches(ISourceViewer viewer, ITextEditor editor) {
95 		if (enabledWhen == null) {
96 			return true;
97 		}
98 		EvaluationContext context = new EvaluationContext(null, editor != null ? editor : viewer);
99 		context.setAllowPluginActivation(true);
100 		context.addVariable("viewer", viewer); //$NON-NLS-1$
101 		if (viewer.getDocument() != null) {
102 			context.addVariable("document", viewer.getDocument()); //$NON-NLS-1$
103 		}
104 		if (editor != null) {
105 			context.addVariable("editor", editor); //$NON-NLS-1$
106 			context.addVariable("editorInput", editor.getEditorInput()); //$NON-NLS-1$
107 		}
108 		try {
109 			return enabledWhen.evaluate(context) == EvaluationResult.TRUE;
110 		} catch (CoreException e) {
111 			GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, "Error while 'enabledWhen' evaluation", e)); //$NON-NLS-1$
112 			return false;
113 		}
114 	}
115 }
116