1 /** 2 * Copyright (c) 2017 Angelo ZERR. 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 * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 13 */ 14 package org.eclipse.ui.internal.texteditor.codemining; 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 22 import org.eclipse.core.runtime.Assert; 23 import org.eclipse.core.runtime.CoreException; 24 import org.eclipse.core.runtime.IConfigurationElement; 25 import org.eclipse.core.runtime.IStatus; 26 import org.eclipse.core.runtime.Status; 27 28 import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; 29 import org.eclipse.jface.text.codemining.ICodeMiningProvider; 30 import org.eclipse.jface.text.source.ISourceViewer; 31 32 import org.eclipse.ui.internal.texteditor.TextEditorPlugin; 33 34 import org.eclipse.ui.texteditor.ITextEditor; 35 36 /** 37 * Describes an extension to the <code>codeMiningProviders</code> extension 38 * point. 39 * <p> 40 * This class is not intended to be subclassed by clients. 41 * </p> 42 * 43 * @since 3.10 44 * @noextend This class is not intended to be subclassed by clients. 45 */ 46 class CodeMiningProviderDescriptor { 47 48 /** Name of the <code>label</code> attribute. */ 49 private static final String LABEL_ATTRIBUTE = "label"; //$NON-NLS-1$ 50 /** Name of the <code>class</code> attribute. */ 51 private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ 52 /** Name of the <code>id</code> attribute. */ 53 private static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ 54 /** Name of the <code>enabledWhen</code> attribute. **/ 55 private static final String ENABLED_WHEN_ATTR = "enabledWhen"; //$NON-NLS-1$ 56 57 /** The configuration element describing this extension. */ 58 private IConfigurationElement fConfiguration; 59 /** The value of the <code>label</code> attribute, if read. */ 60 private String fLabel; 61 /** The value of the <code>id</code> attribute, if read. */ 62 private String fId; 63 /** The expression value of the <code>enabledWhen</code> attribute. */ 64 private final Expression fEnabledWhen; 65 66 /** 67 * Creates a new descriptor for <code>element</code>. 68 * <p> 69 * This method is for internal use only. 70 * </p> 71 * 72 * @param element 73 * the extension point element to be described. 74 * @throws CoreException 75 * when <code>enabledWhen</code> expression is not valid. 76 */ CodeMiningProviderDescriptor(IConfigurationElement element)77 public CodeMiningProviderDescriptor(IConfigurationElement element) throws CoreException { 78 Assert.isLegal(element != null); 79 fConfiguration = element; 80 fEnabledWhen = createEnabledWhen(fConfiguration, getId()); 81 } 82 83 /** 84 * Returns the expression {@link Expression} declared in the 85 * <code>enabledWhen</code> element. 86 * 87 * @param configElement 88 * the configuration element 89 * @param id 90 * the id of the codemining provider. 91 * @return the expression {@link Expression} declared in the enabledWhen 92 * element. 93 * @throws CoreException 94 * when enabledWhen expression is not valid. 95 */ createEnabledWhen(IConfigurationElement configElement, String id)96 private static Expression createEnabledWhen(IConfigurationElement configElement, String id) throws CoreException { 97 final IConfigurationElement[] children = configElement.getChildren(ENABLED_WHEN_ATTR); 98 if (children.length > 0) { 99 IConfigurationElement[] subChildren = children[0].getChildren(); 100 if (subChildren.length != 1) { 101 throw new CoreException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, 102 "One <enabledWhen> element is accepted. Disabling " + id)); //$NON-NLS-1$ 103 } 104 final ElementHandler elementHandler = ElementHandler.getDefault(); 105 final ExpressionConverter converter = ExpressionConverter.getDefault(); 106 return elementHandler.create(converter, subChildren[0]); 107 } 108 return null; 109 } 110 111 /** 112 * Reads (if needed) and returns the label of this extension. 113 * 114 * @return the label for this extension. 115 */ getLabel()116 public String getLabel() { 117 if (fLabel == null) { 118 fLabel = fConfiguration.getAttribute(LABEL_ATTRIBUTE); 119 Assert.isNotNull(fLabel); 120 } 121 return fLabel; 122 } 123 124 /** 125 * Reads (if needed) and returns the id of this extension. 126 * 127 * @return the id for this extension. 128 */ getId()129 public String getId() { 130 if (fId == null) { 131 fId = fConfiguration.getAttribute(ID_ATTRIBUTE); 132 Assert.isNotNull(fId); 133 } 134 return fId; 135 } 136 137 /** 138 * Creates a codemining provider as described in the extension's xml. and null 139 * otherwise. 140 * 141 * @param editor 142 * the text editor 143 * 144 * @return the created codemining provider and null otherwise. 145 */ createCodeMiningProvider(ITextEditor editor)146 protected ICodeMiningProvider createCodeMiningProvider(ITextEditor editor) { 147 try { 148 Object extension = fConfiguration.createExecutableExtension(CLASS_ATTRIBUTE); 149 if (extension instanceof ICodeMiningProvider) { 150 if (extension instanceof AbstractCodeMiningProvider) { 151 ((AbstractCodeMiningProvider) extension).setContext(editor); 152 } 153 return (ICodeMiningProvider) extension; 154 } else { 155 String message = "Invalid extension to codeMiningProviders. Must extends ICodeMiningProvider: " //$NON-NLS-1$ 156 + getId(); 157 TextEditorPlugin.getDefault().getLog() 158 .log(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, message)); 159 return null; 160 } 161 } catch (CoreException e) { 162 TextEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, 163 "Error while creating codeMiningProvider: " + getId(), e)); //$NON-NLS-1$ 164 return null; 165 } 166 } 167 168 /** 169 * Returns true if the given viewer, editor matches the enabledWhen expression 170 * and false otherwise. 171 * 172 * @param viewer 173 * the viewer 174 * @param editor 175 * the editor 176 * @return true if the given viewer, editor matches the enabledWhen expression 177 * and false otherwise. 178 */ matches(ISourceViewer viewer, ITextEditor editor)179 public boolean matches(ISourceViewer viewer, ITextEditor editor) { 180 if (fEnabledWhen == null) { 181 return true; 182 } 183 EvaluationContext context = new EvaluationContext(null, editor); 184 context.setAllowPluginActivation(true); 185 context.addVariable("viewer", viewer); //$NON-NLS-1$ 186 context.addVariable("editor", editor); //$NON-NLS-1$ 187 context.addVariable("editorInput", editor.getEditorInput()); //$NON-NLS-1$ 188 try { 189 return fEnabledWhen.evaluate(context) == EvaluationResult.TRUE; 190 } catch (CoreException e) { 191 TextEditorPlugin.getDefault().getLog().log( 192 new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, "Error while 'enabledWhen' evaluation", e)); //$NON-NLS-1$ 193 return false; 194 } 195 } 196 197 } 198