1 /*******************************************************************************
2 * Copyright (c) 2005, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Marcel Bruch <bruch@cs.tu-darmstadt.de> - [content assist] Allow to re-sort proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=350991
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.ui.text.java;
14 import java.util.Collections;
15 import java.util.List;
17 import org.eclipse.core.runtime.Assert;
18 import org.eclipse.core.runtime.CoreException;
19 import org.eclipse.core.runtime.IConfigurationElement;
20 import org.eclipse.core.runtime.IStatus;
21 import org.eclipse.core.runtime.InvalidRegistryObjectException;
22 import org.eclipse.core.runtime.PerformanceStats;
23 import org.eclipse.core.runtime.Status;
25 import org.eclipse.jface.text.contentassist.ICompletionProposal;
27 import org.eclipse.jdt.internal.corext.util.Messages;
29 import org.eclipse.jdt.ui.text.java.AbstractProposalSorter;
30 import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
32 import org.eclipse.jdt.internal.ui.JavaPlugin;
35 * The description of an extension to the
36 * <code>org.eclipse.jdt.ui.javaCompletionProposalSorters</code> extension point. Instances are
41 public final class ProposalSorterHandle {
42 /** The extension schema name of the id attribute. */
43 private static final String ID= "id"; //$NON-NLS-1$
44 /** The extension schema name of the name attribute. */
45 private static final String NAME= "name"; //$NON-NLS-1$
46 /** The extension schema name of the class attribute. */
47 private static final String CLASS= "class"; //$NON-NLS-1$
48 /** The name of the performance event used to trace extensions. */
49 private static final String PERFORMANCE_EVENT= JavaPlugin.getPluginId() + "/perf/content_assist_sorters/extensions"; //$NON-NLS-1$
51 * If <code>true</code>, execution time of extensions is measured and extensions may be
52 * disabled if execution takes too long.
54 private static final boolean MEASURE_PERFORMANCE= PerformanceStats.isEnabled(PERFORMANCE_EVENT);
55 /** The one and only operation name. */
56 private static final String SORT= "sort"; //$NON-NLS-1$
58 /** The identifier of the extension. */
59 private final String fId;
60 /** The name of the extension. */
61 private final String fName;
62 /** The class name of the provided <code>AbstractProposalSorter</code>. */
63 private final String fClass;
64 /** The configuration element of this extension. */
65 private final IConfigurationElement fElement;
66 /** The computer, if instantiated, <code>null</code> otherwise. */
67 private AbstractProposalSorter fSorter;
70 * Creates a new descriptor.
72 * @param element the configuration element to read
73 * @throws InvalidRegistryObjectException if the configuration element is not valid any longer
74 * @throws CoreException if the configuration does not contain mandatory attributes
76 ProposalSorterHandle(IConfigurationElement element) throws InvalidRegistryObjectException, CoreException {
77 Assert.isLegal(element != null);
80 fId= element.getAttribute(ID);
81 checkNotNull(fId, ID);
83 String name= element.getAttribute(NAME);
89 fClass= element.getAttribute(CLASS);
90 checkNotNull(fClass, CLASS);
94 * Checks that the given attribute value is not <code>null</code>.
96 * @param value the value to check if not null
97 * @param attribute the attribute
98 * @throws InvalidRegistryObjectException if the registry element is no longer valid
99 * @throws CoreException if <code>value</code> is <code>null</code>
101 private void checkNotNull(Object value, String attribute) throws InvalidRegistryObjectException, CoreException {
103 Object[] args= { getId(), fElement.getContributor().getName(), attribute };
104 String message= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_illegal_attribute_message, args);
105 IStatus status= new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, message, null);
106 throw new CoreException(status);
111 * Returns the identifier of the described extension.
113 * @return Returns the id
115 public String getId() {
120 * Returns the name of the described extension.
122 * @return Returns the name
124 public String getName() {
129 * Returns a cached instance of the sorter as described in the extension's xml. The sorter is
130 * {@link #createSorter() created} the first time that this method is called and then cached.
132 * @return a new instance of the proposal sorter as described by this descriptor
133 * @throws CoreException if the creation fails
134 * @throws InvalidRegistryObjectException if the extension is not valid any longer (e.g. due to
137 synchronized AbstractProposalSorter getSorter() throws CoreException, InvalidRegistryObjectException {
139 fSorter= createSorter();
144 * Returns a new instance of the sorter as described in the
147 * @return a new instance of the completion proposal computer as
148 * described by this descriptor
149 * @throws CoreException if the creation fails
150 * @throws InvalidRegistryObjectException if the extension is not
151 * valid any longer (e.g. due to plug-in unloading)
153 private AbstractProposalSorter createSorter() throws CoreException, InvalidRegistryObjectException {
154 return (AbstractProposalSorter) fElement.createExecutableExtension(CLASS);
158 * Safely computes completion proposals through the described extension. If the extension throws
159 * an exception or otherwise does not adhere to the contract described in
160 * {@link AbstractProposalSorter}, the list is returned as is.
162 * @param context the invocation context passed on to the extension
163 * @param proposals the list of computed completion proposals to be sorted (element type:
164 * {@link org.eclipse.jface.text.contentassist.ICompletionProposal}), must be writable
166 public void sortProposals(ContentAssistInvocationContext context, List<ICompletionProposal> proposals) {
169 AbstractProposalSorter sorter= getSorter();
171 PerformanceStats stats= startMeter(SORT, sorter);
173 sorter.beginSorting(context);
174 Collections.sort(proposals, sorter);
177 status= stopMeter(stats, SORT);
183 status= createAPIViolationStatus(SORT);
185 } catch (InvalidRegistryObjectException x) {
186 status= createExceptionStatus(x);
187 } catch (CoreException x) {
188 status= createExceptionStatus(x);
189 } catch (RuntimeException x) {
190 status= createExceptionStatus(x);
193 JavaPlugin.log(status);
197 private IStatus stopMeter(final PerformanceStats stats, String operation) {
198 if (MEASURE_PERFORMANCE) {
200 if (stats.isFailure())
201 return createPerformanceStatus(operation);
206 private PerformanceStats startMeter(String context, AbstractProposalSorter sorter) {
207 final PerformanceStats stats;
208 if (MEASURE_PERFORMANCE) {
209 stats= PerformanceStats.getStats(PERFORMANCE_EVENT, sorter);
210 stats.startRun(context);
217 Status createExceptionStatus(InvalidRegistryObjectException x) {
218 // extension has become invalid - log & disable
219 String disable= createBlameMessage();
220 String reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_invalid;
221 return new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, disable + " " + reason, x); //$NON-NLS-1$
224 Status createExceptionStatus(CoreException x) {
225 // unable to instantiate the extension - log & disable
226 String disable= createBlameMessage();
227 String reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_instantiation;
228 return new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, disable + " " + reason, x); //$NON-NLS-1$
231 Status createExceptionStatus(RuntimeException x) {
232 // misbehaving extension - log & disable
233 String disable= createBlameMessage();
234 String reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_runtime_ex;
235 return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, disable + " " + reason, x); //$NON-NLS-1$
238 private Status createAPIViolationStatus(String operation) {
239 String disable= createBlameMessage();
240 Object[] args= {operation};
241 String reason= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_reason_API, args);
242 return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, disable + " " + reason, null); //$NON-NLS-1$
245 private Status createPerformanceStatus(String operation) {
246 String disable= createBlameMessage();
247 Object[] args= {operation};
248 String reason= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_reason_performance, args);
249 return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, disable + " " + reason, null); //$NON-NLS-1$
252 private String createBlameMessage() {
253 Object[] args= { getName(), getId() };
254 String disable= Messages.format(JavaTextMessages.ProposalSorterHandle_blame, args);
259 * Returns the error message from the described extension, <code>null</code> for no error.
261 * @return the error message from the described extension, <code>null</code> for no error
263 public String getErrorMessage() {