KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > CompletionProposalComputerDescriptor


1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 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
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.text.java;
12
13 import java.util.Collections JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IConfigurationElement;
22 import org.eclipse.core.runtime.IContributor;
23 import org.eclipse.core.runtime.IExtension;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.InvalidRegistryObjectException;
27 import org.eclipse.core.runtime.PerformanceStats;
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.Status;
30
31 import org.eclipse.jface.text.IDocument;
32
33 import org.eclipse.jdt.internal.corext.util.Messages;
34
35 import org.eclipse.jdt.ui.text.IJavaPartitions;
36 import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
37 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
38
39 import org.eclipse.jdt.internal.ui.JavaPlugin;
40
41 import org.osgi.framework.Bundle;
42
43 /**
44  * The description of an extension to the
45  * <code>org.eclipse.jdt.ui.javaCompletionProposalComputer</code> extension point. Instances are
46  * immutable. Instances can be obtained from a {@link CompletionProposalComputerRegistry}.
47  *
48  * @see CompletionProposalComputerRegistry
49  * @since 3.2
50  */

51 final class CompletionProposalComputerDescriptor {
52     /** The default category id. */
53     private static final String JavaDoc DEFAULT_CATEGORY_ID= "org.eclipse.jdt.ui.defaultProposalCategory"; //$NON-NLS-1$
54
/** The extension schema name of the category id attribute. */
55     private static final String JavaDoc CATEGORY_ID= "categoryId"; //$NON-NLS-1$
56
/** The extension schema name of the partition type attribute. */
57     private static final String JavaDoc TYPE= "type"; //$NON-NLS-1$
58
/** The extension schema name of the class attribute. */
59     private static final String JavaDoc CLASS= "class"; //$NON-NLS-1$
60
/** The extension schema name of the activate attribute. */
61     private static final String JavaDoc ACTIVATE= "activate"; //$NON-NLS-1$
62
/** The extension schema name of the partition child elements. */
63     private static final String JavaDoc PARTITION= "partition"; //$NON-NLS-1$
64
/** Set of Java partition types. */
65     private static final Set JavaDoc PARTITION_SET;
66     /** The name of the performance event used to trace extensions. */
67     private static final String JavaDoc PERFORMANCE_EVENT= JavaPlugin.getPluginId() + "/perf/content_assist/extensions"; //$NON-NLS-1$
68
/**
69      * If <code>true</code>, execution time of extensions is measured and the data forwarded to
70      * core's {@link PerformanceStats} service.
71      */

72     private static final boolean MEASURE_PERFORMANCE= PerformanceStats.isEnabled(PERFORMANCE_EVENT);
73     /**
74      * Independently of the {@link PerformanceStats} service, any operation that takes longer than
75      * {@value} milliseconds will be flagged as an violation. This timeout does not apply to the
76      * first invocation, as it may take longer due to plug-in initialization etc. See also
77      * {@link #fIsReportingDelay}.
78      */

79     private static final long MAX_DELAY= 5000;
80     
81     /* log constants */
82     private static final String JavaDoc COMPUTE_COMPLETION_PROPOSALS= "computeCompletionProposals()"; //$NON-NLS-1$
83
private static final String JavaDoc COMPUTE_CONTEXT_INFORMATION= "computeContextInformation()"; //$NON-NLS-1$
84
private static final String JavaDoc SESSION_STARTED= "sessionStarted()"; //$NON-NLS-1$
85
private static final String JavaDoc SESSION_ENDED= "sessionEnded()"; //$NON-NLS-1$
86

87     static {
88         Set JavaDoc partitions= new HashSet JavaDoc();
89         partitions.add(IDocument.DEFAULT_CONTENT_TYPE);
90         partitions.add(IJavaPartitions.JAVA_DOC);
91         partitions.add(IJavaPartitions.JAVA_MULTI_LINE_COMMENT);
92         partitions.add(IJavaPartitions.JAVA_SINGLE_LINE_COMMENT);
93         partitions.add(IJavaPartitions.JAVA_STRING);
94         partitions.add(IJavaPartitions.JAVA_CHARACTER);
95         
96         PARTITION_SET= Collections.unmodifiableSet(partitions);
97     }
98
99     /** The identifier of the extension. */
100     private final String JavaDoc fId;
101     /** The name of the extension. */
102     private final String JavaDoc fName;
103     /** The class name of the provided <code>IJavaCompletionProposalComputer</code>. */
104     private final String JavaDoc fClass;
105     /** The activate attribute value. */
106     private final boolean fActivate;
107     /** The partition of the extension (element type: {@link String}). */
108     private final Set JavaDoc fPartitions;
109     /** The configuration element of this extension. */
110     private final IConfigurationElement fElement;
111     /** The registry we are registered with. */
112     private final CompletionProposalComputerRegistry fRegistry;
113     /** The computer, if instantiated, <code>null</code> otherwise. */
114     private IJavaCompletionProposalComputer fComputer;
115     /** The ui category. */
116     private final CompletionProposalCategory fCategory;
117     /** The first error message in the most recent operation, or <code>null</code>. */
118     private String JavaDoc fLastError;
119     /**
120      * Tells whether to inform the user when <code>MAX_DELAY</code> has been exceeded.
121      * We start timing execution after the first session because the first may take
122      * longer due to plug-in activation and initialization.
123      */

124     private boolean fIsReportingDelay= false;
125     /** The start of the last operation. */
126     private long fStart;
127
128     /**
129      * Creates a new descriptor.
130      *
131      * @param element the configuration element to read
132      * @param registry the computer registry creating this descriptor
133      */

134     CompletionProposalComputerDescriptor(IConfigurationElement element, CompletionProposalComputerRegistry registry, List JavaDoc categories) throws InvalidRegistryObjectException {
135         Assert.isLegal(registry != null);
136         Assert.isLegal(element != null);
137         
138         fRegistry= registry;
139         fElement= element;
140         IExtension extension= element.getDeclaringExtension();
141         fId= extension.getUniqueIdentifier();
142         checkNotNull(fId, "id"); //$NON-NLS-1$
143

144         String JavaDoc name= extension.getLabel();
145         if (name.length() == 0)
146             fName= fId;
147         else
148             fName= name;
149         
150         Set JavaDoc partitions= new HashSet JavaDoc();
151         IConfigurationElement[] children= element.getChildren(PARTITION);
152         if (children.length == 0) {
153             fPartitions= PARTITION_SET; // add to all partition types if no partition is configured
154
} else {
155             for (int i= 0; i < children.length; i++) {
156                 String JavaDoc type= children[i].getAttribute(TYPE);
157                 checkNotNull(type, TYPE);
158                 partitions.add(type);
159             }
160             fPartitions= Collections.unmodifiableSet(partitions);
161         }
162         
163         String JavaDoc activateAttribute= element.getAttribute(ACTIVATE);
164         fActivate= Boolean.valueOf(activateAttribute).booleanValue();
165
166         fClass= element.getAttribute(CLASS);
167         checkNotNull(fClass, CLASS);
168         
169         String JavaDoc categoryId= element.getAttribute(CATEGORY_ID);
170         if (categoryId == null)
171             categoryId= DEFAULT_CATEGORY_ID;
172         CompletionProposalCategory category= null;
173         for (Iterator JavaDoc it= categories.iterator(); it.hasNext();) {
174             CompletionProposalCategory cat= (CompletionProposalCategory) it.next();
175             if (cat.getId().equals(categoryId)) {
176                 category= cat;
177                 break;
178             }
179         }
180         if (category == null) {
181             // create a category if it does not exist
182
fCategory= new CompletionProposalCategory(categoryId, fName, registry);
183             categories.add(fCategory);
184         } else {
185             fCategory= category;
186         }
187     }
188
189     /**
190      * Checks an element that must be defined according to the extension
191      * point schema. Throws an
192      * <code>InvalidRegistryObjectException</code> if <code>obj</code>
193      * is <code>null</code>.
194      */

195     private void checkNotNull(Object JavaDoc obj, String JavaDoc attribute) throws InvalidRegistryObjectException {
196         if (obj == null) {
197             Object JavaDoc[] args= { getId(), fElement.getContributor().getName(), attribute };
198             String JavaDoc message= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_illegal_attribute_message, args);
199             IStatus status= new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, message, null);
200             JavaPlugin.log(status);
201             throw new InvalidRegistryObjectException();
202         }
203     }
204
205     /**
206      * Returns the identifier of the described extension.
207      *
208      * @return Returns the id
209      */

210     public String JavaDoc getId() {
211         return fId;
212     }
213
214     /**
215      * Returns the name of the described extension.
216      *
217      * @return Returns the name
218      */

219     public String JavaDoc getName() {
220         return fName;
221     }
222     
223     /**
224      * Returns the partition types of the described extension.
225      *
226      * @return the set of partition types (element type: {@link String})
227      */

228     public Set JavaDoc getPartitions() {
229         return fPartitions;
230     }
231     
232     /**
233      * Returns a cached instance of the computer as described in the
234      * extension's xml. The computer is
235      * {@link #createComputer() created} the first time that this method
236      * is called and then cached.
237      *
238      * @return a new instance of the completion proposal computer as
239      * described by this descriptor
240      * @throws CoreException if the creation fails
241      * @throws InvalidRegistryObjectException if the extension is not
242      * valid any longer (e.g. due to plug-in unloading)
243      */

244     private synchronized IJavaCompletionProposalComputer getComputer() throws CoreException, InvalidRegistryObjectException {
245         if (fComputer == null && (fActivate || isPluginLoaded()))
246             fComputer= createComputer();
247         return fComputer;
248     }
249
250     private boolean isPluginLoaded() {
251         Bundle bundle= getBundle();
252         return bundle != null && bundle.getState() == Bundle.ACTIVE;
253     }
254
255     private Bundle getBundle() {
256         String JavaDoc namespace= fElement.getDeclaringExtension().getContributor().getName();
257         Bundle bundle= Platform.getBundle(namespace);
258         return bundle;
259     }
260
261     /**
262      * Returns a new instance of the computer as described in the
263      * extension's xml. Note that the safest way to access the computer
264      * is by using the
265      * {@linkplain #computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor) computeCompletionProposals}
266      * and
267      * {@linkplain #computeContextInformation(ContentAssistInvocationContext, IProgressMonitor) computeContextInformation}
268      * methods. These delegate the functionality to the contributed
269      * computer, but handle instance creation and any exceptions thrown.
270      *
271      * @return a new instance of the completion proposal computer as
272      * described by this descriptor
273      * @throws CoreException if the creation fails
274      * @throws InvalidRegistryObjectException if the extension is not
275      * valid any longer (e.g. due to plug-in unloading)
276      */

277     public IJavaCompletionProposalComputer createComputer() throws CoreException, InvalidRegistryObjectException {
278         return (IJavaCompletionProposalComputer) fElement.createExecutableExtension(CLASS);
279     }
280     
281     /**
282      * Safely computes completion proposals through the described extension. If the extension
283      * is disabled, throws an exception or otherwise does not adhere to the contract described in
284      * {@link IJavaCompletionProposalComputer}, an empty list is returned.
285      *
286      * @param context the invocation context passed on to the extension
287      * @param monitor the progress monitor passed on to the extension
288      * @return the list of computed completion proposals (element type:
289      * {@link org.eclipse.jface.text.contentassist.ICompletionProposal})
290      */

291     public List JavaDoc computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) {
292         if (!isEnabled())
293             return Collections.EMPTY_LIST;
294
295         IStatus status;
296         try {
297             IJavaCompletionProposalComputer computer= getComputer();
298             if (computer == null) // not active yet
299
return Collections.EMPTY_LIST;
300             
301             try {
302                 PerformanceStats stats= startMeter(context, computer);
303                 List JavaDoc proposals= computer.computeCompletionProposals(context, monitor);
304                 stopMeter(stats, COMPUTE_COMPLETION_PROPOSALS);
305                 
306                 if (proposals != null) {
307                     fLastError= computer.getErrorMessage();
308                     return proposals;
309                 }
310             } finally {
311                 fIsReportingDelay= true;
312             }
313             status= createAPIViolationStatus(COMPUTE_COMPLETION_PROPOSALS);
314         } catch (InvalidRegistryObjectException x) {
315             status= createExceptionStatus(x);
316         } catch (CoreException x) {
317             status= createExceptionStatus(x);
318         } catch (RuntimeException JavaDoc x) {
319             status= createExceptionStatus(x);
320         } finally {
321             monitor.done();
322         }
323
324         fRegistry.informUser(this, status);
325
326         return Collections.EMPTY_LIST;
327     }
328
329     /**
330      * Safely computes context information objects through the described extension. If the extension
331      * is disabled, throws an exception or otherwise does not adhere to the contract described in
332      * {@link IJavaCompletionProposalComputer}, an empty list is returned.
333      *
334      * @param context the invocation context passed on to the extension
335      * @param monitor the progress monitor passed on to the extension
336      * @return the list of computed context information objects (element type:
337      * {@link org.eclipse.jface.text.contentassist.IContextInformation})
338      */

339     public List JavaDoc computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) {
340         if (!isEnabled())
341             return Collections.EMPTY_LIST;
342         
343         IStatus status;
344         try {
345             IJavaCompletionProposalComputer computer= getComputer();
346             if (computer == null) // not active yet
347
return Collections.EMPTY_LIST;
348
349             PerformanceStats stats= startMeter(context, computer);
350             List JavaDoc proposals= computer.computeContextInformation(context, monitor);
351             stopMeter(stats, COMPUTE_CONTEXT_INFORMATION);
352             
353             if (proposals != null) {
354                 fLastError= computer.getErrorMessage();
355                 return proposals;
356             }
357             
358             status= createAPIViolationStatus(COMPUTE_CONTEXT_INFORMATION);
359         } catch (InvalidRegistryObjectException x) {
360             status= createExceptionStatus(x);
361         } catch (CoreException x) {
362             status= createExceptionStatus(x);
363         } catch (RuntimeException JavaDoc x) {
364             status= createExceptionStatus(x);
365         } finally {
366             monitor.done();
367         }
368         
369         fRegistry.informUser(this, status);
370         
371         return Collections.EMPTY_LIST;
372     }
373     
374
375     /**
376      * Notifies the described extension of a proposal computation session start.
377      * <p><em>
378      * Note: This method is called every time code assist is invoked and
379      * is <strong>not</strong> filtered by partition type.
380      * </em></p>
381      */

382     public void sessionStarted() {
383         if (!isEnabled())
384             return;
385         
386         IStatus status;
387         try {
388             IJavaCompletionProposalComputer computer= getComputer();
389             if (computer == null) // not active yet
390
return;
391             
392             PerformanceStats stats= startMeter(SESSION_STARTED, computer);
393             computer.sessionStarted();
394             stopMeter(stats, SESSION_ENDED);
395             
396             return;
397         } catch (InvalidRegistryObjectException x) {
398             status= createExceptionStatus(x);
399         } catch (CoreException x) {
400             status= createExceptionStatus(x);
401         } catch (RuntimeException JavaDoc x) {
402             status= createExceptionStatus(x);
403         }
404         
405         fRegistry.informUser(this, status);
406     }
407
408     /**
409      * Notifies the described extension of a proposal computation session end.
410      * <p><em>
411      * Note: This method is called every time code assist is invoked and
412      * is <strong>not</strong> filtered by partition type.
413      * </em></p>
414      */

415     public void sessionEnded() {
416         if (!isEnabled())
417             return;
418
419         IStatus status;
420         try {
421             IJavaCompletionProposalComputer computer= getComputer();
422             if (computer == null) // not active yet
423
return;
424
425             PerformanceStats stats= startMeter(SESSION_ENDED, computer);
426             computer.sessionEnded();
427             stopMeter(stats, SESSION_ENDED);
428
429             return;
430         } catch (InvalidRegistryObjectException x) {
431             status= createExceptionStatus(x);
432         } catch (CoreException x) {
433             status= createExceptionStatus(x);
434         } catch (RuntimeException JavaDoc x) {
435             status= createExceptionStatus(x);
436         }
437
438         fRegistry.informUser(this, status);
439     }
440
441     private PerformanceStats startMeter(Object JavaDoc context, IJavaCompletionProposalComputer computer) {
442         final PerformanceStats stats;
443         if (MEASURE_PERFORMANCE) {
444             stats= PerformanceStats.getStats(PERFORMANCE_EVENT, computer);
445             stats.startRun(context.toString());
446         } else {
447             stats= null;
448         }
449         
450         if (fIsReportingDelay) {
451             fStart= System.currentTimeMillis();
452         }
453         
454         return stats;
455     }
456
457     private void stopMeter(final PerformanceStats stats, String JavaDoc operation) {
458         if (MEASURE_PERFORMANCE) {
459             stats.endRun();
460             if (stats.isFailure()) {
461                 IStatus status= createPerformanceStatus(operation);
462                 fRegistry.informUser(this, status);
463                 return;
464             }
465         }
466         
467         if (fIsReportingDelay) {
468             long current= System.currentTimeMillis();
469             if (current - fStart > MAX_DELAY) {
470                 IStatus status= createPerformanceStatus(operation);
471                 fRegistry.informUser(this, status);
472             }
473         }
474     }
475
476     private IStatus createExceptionStatus(InvalidRegistryObjectException x) {
477         // extension has become invalid - log & disable
478
String JavaDoc blame= createBlameMessage();
479         String JavaDoc reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_invalid;
480         return new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$
481
}
482
483     private IStatus createExceptionStatus(CoreException x) {
484         // unable to instantiate the extension - log & disable
485
String JavaDoc blame= createBlameMessage();
486         String JavaDoc reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_instantiation;
487         return new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$
488
}
489     
490     private IStatus createExceptionStatus(RuntimeException JavaDoc x) {
491         // misbehaving extension - log & disable
492
String JavaDoc blame= createBlameMessage();
493         String JavaDoc reason= JavaTextMessages.CompletionProposalComputerDescriptor_reason_runtime_ex;
494         return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$
495
}
496
497     private IStatus createAPIViolationStatus(String JavaDoc operation) {
498         String JavaDoc blame= createBlameMessage();
499         Object JavaDoc[] args= {operation};
500         String JavaDoc reason= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_reason_API, args);
501         return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, blame + " " + reason, null); //$NON-NLS-1$
502
}
503
504     private IStatus createPerformanceStatus(String JavaDoc operation) {
505         String JavaDoc blame= createBlameMessage();
506         Object JavaDoc[] args= {operation};
507         String JavaDoc reason= Messages.format(JavaTextMessages.CompletionProposalComputerDescriptor_reason_performance, args);
508         return new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, blame + " " + reason, null); //$NON-NLS-1$
509
}
510
511     private String JavaDoc createBlameMessage() {
512         Object JavaDoc[] args= { getName(), fElement.getDeclaringExtension().getContributor().getName() };
513         String JavaDoc disable= Messages.format( JavaTextMessages.CompletionProposalComputerDescriptor_blame_message, args);
514         return disable;
515     }
516     
517     /**
518      * Returns the enablement state of the described extension.
519      *
520      * @return the enablement state of the described extension
521      */

522     private boolean isEnabled() {
523         return fCategory.isEnabled();
524     }
525     
526     CompletionProposalCategory getCategory() {
527         return fCategory;
528     }
529
530     /**
531      * Returns the error message from the described extension.
532      *
533      * @return the error message from the described extension
534      */

535     public String JavaDoc getErrorMessage() {
536         return fLastError;
537     }
538
539     /**
540      * Returns the contributor of the described extension.
541      *
542      * @return the contributor of the described extension
543      */

544     IContributor getContributor() {
545         try {
546             return fElement.getContributor();
547         } catch (InvalidRegistryObjectException e) {
548             return null;
549         }
550     }
551     
552 }
553
Popular Tags