KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > template > java > JavaContext


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.corext.template.java;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.Comparator JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.HashSet JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.NullProgressMonitor;
26 import org.eclipse.core.runtime.Status;
27
28 import org.eclipse.swt.widgets.Shell;
29
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.preference.IPreferenceStore;
32
33 import org.eclipse.jface.text.BadLocationException;
34 import org.eclipse.jface.text.BadPositionCategoryException;
35 import org.eclipse.jface.text.DefaultPositionUpdater;
36 import org.eclipse.jface.text.Document;
37 import org.eclipse.jface.text.IDocument;
38 import org.eclipse.jface.text.IPositionUpdater;
39 import org.eclipse.jface.text.IRegion;
40 import org.eclipse.jface.text.Position;
41 import org.eclipse.jface.text.TextUtilities;
42 import org.eclipse.jface.text.templates.Template;
43 import org.eclipse.jface.text.templates.TemplateBuffer;
44 import org.eclipse.jface.text.templates.TemplateContextType;
45 import org.eclipse.jface.text.templates.TemplateException;
46 import org.eclipse.jface.text.templates.TemplateTranslator;
47 import org.eclipse.jface.text.templates.TemplateVariable;
48 import org.eclipse.jface.text.templates.TemplateVariableType;
49
50 import org.eclipse.jdt.core.Flags;
51 import org.eclipse.jdt.core.ICompilationUnit;
52 import org.eclipse.jdt.core.IJavaElement;
53 import org.eclipse.jdt.core.IJavaProject;
54 import org.eclipse.jdt.core.JavaModelException;
55 import org.eclipse.jdt.core.Signature;
56 import org.eclipse.jdt.core.dom.CompilationUnit;
57 import org.eclipse.jdt.core.dom.SimpleName;
58 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
59 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
60 import org.eclipse.jdt.core.search.IJavaSearchConstants;
61 import org.eclipse.jdt.core.search.IJavaSearchScope;
62 import org.eclipse.jdt.core.search.SearchEngine;
63 import org.eclipse.jdt.core.search.SearchPattern;
64 import org.eclipse.jdt.core.search.TypeNameMatch;
65
66 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
67 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
68 import org.eclipse.jdt.internal.corext.template.java.CompilationUnitCompletion.Variable;
69 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
70 import org.eclipse.jdt.internal.corext.util.Strings;
71 import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector;
72
73 import org.eclipse.jdt.ui.JavaUI;
74 import org.eclipse.jdt.ui.PreferenceConstants;
75
76 import org.eclipse.jdt.internal.ui.JavaPlugin;
77 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
78 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
79 import org.eclipse.jdt.internal.ui.text.correction.SimilarElementsRequestor;
80 import org.eclipse.jdt.internal.ui.text.template.contentassist.MultiVariable;
81 import org.eclipse.jdt.internal.ui.text.template.contentassist.MultiVariableGuess;
82 import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
83
84 /**
85  * A context for java source.
86  */

87 public class JavaContext extends CompilationUnitContext {
88
89     /** A code completion requester for guessing local variable names. */
90     private CompilationUnitCompletion fCompletion;
91     /**
92      * The list of used local names.
93      * @since 3.3
94      */

95     private Set JavaDoc fUsedNames= new HashSet JavaDoc();
96     private Map JavaDoc fVariables= new HashMap JavaDoc();
97     
98     /**
99      * Creates a java template context.
100      *
101      * @param type the context type.
102      * @param document the document.
103      * @param completionOffset the completion offset within the document.
104      * @param completionLength the completion length.
105      * @param compilationUnit the compilation unit (may be <code>null</code>).
106      */

107     public JavaContext(TemplateContextType type, IDocument document, int completionOffset, int completionLength, ICompilationUnit compilationUnit) {
108         super(type, document, completionOffset, completionLength, compilationUnit);
109     }
110     
111     /**
112      * Creates a java template context.
113      *
114      * @param type the context type.
115      * @param document the document.
116      * @param completionPosition the position defining the completion offset and length
117      * @param compilationUnit the compilation unit (may be <code>null</code>).
118      * @since 3.2
119      */

120     public JavaContext(TemplateContextType type, IDocument document, Position completionPosition, ICompilationUnit compilationUnit) {
121         super(type, document, completionPosition, compilationUnit);
122     }
123     
124     /**
125      * Returns the indentation level at the position of code completion.
126      *
127      * @return the indentation level at the position of the code completion
128      */

129     private int getIndentation() {
130         int start= getStart();
131         IDocument document= getDocument();
132         try {
133             IRegion region= document.getLineInformationOfOffset(start);
134             String JavaDoc lineContent= document.get(region.getOffset(), region.getLength());
135             IJavaProject project= getJavaProject();
136             return Strings.computeIndentUnits(lineContent, project);
137         } catch (BadLocationException e) {
138             return 0;
139         }
140     }
141
142     /*
143      * @see TemplateContext#evaluate(Template template)
144      */

145     public TemplateBuffer evaluate(Template template) throws BadLocationException, TemplateException {
146         clear();
147         
148         if (!canEvaluate(template))
149             throw new TemplateException(JavaTemplateMessages.Context_error_cannot_evaluate);
150         
151         TemplateTranslator translator= new TemplateTranslator() {
152             protected TemplateVariable createVariable(TemplateVariableType type, String JavaDoc name, int[] offsets) {
153 // TemplateVariableResolver resolver= getContextType().getResolver(type.getName());
154
// return resolver.createVariable();
155

156                 MultiVariable variable= new JavaVariable(type, name, offsets);
157                 fVariables.put(name, variable);
158                 return variable;
159             }
160         };
161         TemplateBuffer buffer= translator.translate(template);
162
163         getContextType().resolve(buffer, this);
164
165         IPreferenceStore prefs= JavaPlugin.getDefault().getPreferenceStore();
166         boolean useCodeFormatter= prefs.getBoolean(PreferenceConstants.TEMPLATES_USE_CODEFORMATTER);
167
168         IJavaProject project= getJavaProject();
169         JavaFormatter formatter= new JavaFormatter(TextUtilities.getDefaultLineDelimiter(getDocument()), getIndentation(), useCodeFormatter, project);
170         formatter.format(buffer, this);
171         
172         clear();
173
174         return buffer;
175     }
176
177     private void clear() {
178         fUsedNames.clear();
179     }
180     
181     /*
182      * @see TemplateContext#canEvaluate(Template templates)
183      */

184     public boolean canEvaluate(Template template) {
185         if (fForceEvaluation)
186             return true;
187
188         String JavaDoc key= getKey();
189         return
190             template.matches(key, getContextType().getId()) &&
191             key.length() != 0 && template.getName().toLowerCase().startsWith(key.toLowerCase());
192     }
193
194     /*
195      * @see DocumentTemplateContext#getCompletionPosition();
196      */

197     public int getStart() {
198
199         if (fIsManaged && getCompletionLength() > 0)
200             return super.getStart();
201         
202         try {
203             IDocument document= getDocument();
204
205             int start= getCompletionOffset();
206             int end= getCompletionOffset() + getCompletionLength();
207             
208             while (start != 0 && Character.isUnicodeIdentifierPart(document.getChar(start - 1)))
209                 start--;
210             
211             while (start != end && Character.isWhitespace(document.getChar(start)))
212                 start++;
213             
214             if (start == end)
215                 start= getCompletionOffset();
216             
217                 return start;
218
219         } catch (BadLocationException e) {
220             return super.getStart();
221         }
222     }
223
224     /*
225      * @see org.eclipse.jdt.internal.corext.template.DocumentTemplateContext#getEnd()
226      */

227     public int getEnd() {
228         
229         if (fIsManaged || getCompletionLength() == 0)
230             return super.getEnd();
231
232         try {
233             IDocument document= getDocument();
234
235             int start= getCompletionOffset();
236             int end= getCompletionOffset() + getCompletionLength();
237             
238             while (start != end && Character.isWhitespace(document.getChar(end - 1)))
239                 end--;
240             
241             return end;
242
243         } catch (BadLocationException e) {
244             return super.getEnd();
245         }
246     }
247
248     /*
249      * @see org.eclipse.jdt.internal.corext.template.DocumentTemplateContext#getKey()
250      */

251     public String JavaDoc getKey() {
252
253         if (getCompletionLength() == 0)
254             return super.getKey();
255
256         try {
257             IDocument document= getDocument();
258
259             int start= getStart();
260             int end= getCompletionOffset();
261             return start <= end
262                 ? document.get(start, end - start)
263                 : ""; //$NON-NLS-1$
264

265         } catch (BadLocationException e) {
266             return super.getKey();
267         }
268     }
269
270     /**
271      * Returns the character before the start position of the completion.
272      *
273      * @return the character before the start position of the completion
274      */

275     public char getCharacterBeforeStart() {
276         int start= getStart();
277         
278         try {
279             return start == 0
280                 ? ' '
281                 : getDocument().getChar(start - 1);
282
283         } catch (BadLocationException e) {
284             return ' ';
285         }
286     }
287
288     private static void handleException(Shell shell, Exception JavaDoc e) {
289         String JavaDoc title= JavaTemplateMessages.JavaContext_error_title;
290         if (e instanceof CoreException)
291             ExceptionHandler.handle((CoreException)e, shell, title, null);
292         else if (e instanceof InvocationTargetException JavaDoc)
293             ExceptionHandler.handle((InvocationTargetException JavaDoc)e, shell, title, null);
294         else {
295             JavaPlugin.log(e);
296             MessageDialog.openError(shell, title, e.getMessage());
297         }
298     }
299
300     private CompilationUnitCompletion getCompletion() {
301         ICompilationUnit compilationUnit= getCompilationUnit();
302         if (fCompletion == null) {
303             fCompletion= new CompilationUnitCompletion(compilationUnit);
304             
305             if (compilationUnit != null) {
306                 try {
307                     compilationUnit.codeComplete(getStart(), fCompletion);
308                 } catch (JavaModelException e) {
309                     // ignore
310
}
311             }
312         }
313         
314         return fCompletion;
315     }
316
317     /**
318      * Returns the names of local arrays.
319      *
320      * @return the names of local arrays
321      */

322     public Variable[] getArrays() {
323         Variable[] localArrays= getCompletion().findLocalArrays();
324         arrange(localArrays);
325         return localArrays;
326     }
327     
328     /**
329      * Sorts already used locals behind any that are not yet used.
330      *
331      * @param variables the variables to sort
332      * @since 3.3
333      */

334     private void arrange(Variable[] variables) {
335         Arrays.sort(variables, new Comparator JavaDoc() {
336             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
337                 return rank((Variable) o1) - rank((Variable) o2);
338             }
339             
340             private int rank(Variable l) {
341                 return fUsedNames.contains(l.getName()) ? 1 : 0;
342             }
343         });
344     }
345
346     /**
347      * Returns the names of local variables matching <code>type</code>.
348      *
349      * @return the names of local variables matching <code>type</code>
350      * @since 3.3
351      */

352     public Variable[] getLocalVariables(String JavaDoc type) {
353         Variable[] localVariables= getCompletion().findLocalVariables(type);
354         arrange(localVariables);
355         return localVariables;
356     }
357     
358     /**
359      * Returns the names of fields matching <code>type</code>.
360      *
361      * @return the names of fields matching <code>type</code>
362      * @since 3.3
363      */

364     public Variable[] getFields(String JavaDoc type) {
365         Variable[] fields= getCompletion().findFieldVariables(type);
366         arrange(fields);
367         return fields;
368     }
369     
370     /**
371      * Returns the names of local iterables or arrays.
372      *
373      * @return the names of local iterables or arrays
374      */

375     public Variable[] getIterables() {
376         Variable[] iterables= getCompletion().findLocalIterables();
377         arrange(iterables);
378         return iterables;
379     }
380     
381     public void markAsUsed(String JavaDoc name) {
382         fUsedNames.add(name);
383     }
384
385     public String JavaDoc[] suggestVariableNames(String JavaDoc type) throws IllegalArgumentException JavaDoc {
386         String JavaDoc[] excludes= computeExcludes();
387         // TODO erasure, arrays, etc.
388
String JavaDoc[] result= suggestVariableName(type, excludes);
389         return result;
390     }
391     
392     private String JavaDoc[] computeExcludes() {
393         String JavaDoc[] excludes= getCompletion().getLocalVariableNames();
394         if (!fUsedNames.isEmpty()) {
395             String JavaDoc[] allExcludes= new String JavaDoc[fUsedNames.size() + excludes.length];
396             System.arraycopy(excludes, 0, allExcludes, 0, excludes.length);
397             System.arraycopy(fUsedNames.toArray(), 0, allExcludes, 0, fUsedNames.size());
398             excludes= allExcludes;
399         }
400         return excludes;
401     }
402
403     private String JavaDoc[] suggestVariableName(String JavaDoc type, String JavaDoc[] excludes) throws IllegalArgumentException JavaDoc {
404         int dim=0;
405         while (type.endsWith("[]")) {//$NON-NLS-1$
406
dim++;
407             type= type.substring(0, type.length() - 2);
408         }
409         
410         IJavaProject project= getJavaProject();
411         if (project != null)
412             return StubUtility.getVariableNameSuggestions(StubUtility.LOCAL, project, type, dim, Arrays.asList(excludes), true);
413         
414         // fallback if we lack proper context: roll-our own lowercasing
415
return new String JavaDoc[] {Signature.getSimpleName(type).toLowerCase()};
416     }
417     
418     public void addImport(String JavaDoc type) {
419         if (isReadOnly())
420             return;
421         
422         ICompilationUnit cu= getCompilationUnit();
423         if (cu == null)
424             return;
425
426         try {
427             boolean qualified= type.indexOf('.') != -1;
428             if (!qualified) {
429                 IJavaSearchScope searchScope= SearchEngine.createJavaSearchScope(new IJavaElement[] { cu.getJavaProject() });
430                 SimpleName nameNode= null;
431                 TypeNameMatch[] matches= findAllTypes(type, searchScope, nameNode, null, cu);
432                 if (matches.length != 1) // only add import if we have a single match
433
return;
434                 type= matches[0].getFullyQualifiedName();
435             }
436             
437             Position position= new Position(getCompletionOffset(), getCompletionLength());
438             IDocument document= getDocument();
439             final String JavaDoc category= "__template_position_importer" + System.currentTimeMillis(); //$NON-NLS-1$
440
IPositionUpdater updater= new DefaultPositionUpdater(category);
441             document.addPositionCategory(category);
442             document.addPositionUpdater(updater);
443             document.addPosition(position);
444
445             try {
446                 
447                 ImportRewrite rewrite= StubUtility.createImportRewrite(cu, true);
448                 CompilationUnit root= getASTRoot(cu);
449                 ImportRewriteContext context;
450                 if (root == null)
451                     context= null;
452                 else
453                     context= new ContextSensitiveImportRewriteContext(root, getCompletionOffset(), rewrite);
454                 rewrite.addImport(type, context);
455                 JavaModelUtil.applyEdit(cu, rewrite.rewriteImports(null), false, null);
456                 
457                 setCompletionOffset(position.getOffset());
458                 setCompletionLength(position.getLength());
459                 
460             } catch (CoreException e) {
461                 handleException(null, e);
462             } finally {
463                 document.removePosition(position);
464                 document.removePositionUpdater(updater);
465                 document.removePositionCategory(category);
466             }
467             
468         } catch (BadLocationException e) {
469             handleException(null, e);
470         } catch (BadPositionCategoryException e) {
471             handleException(null, e);
472         } catch (JavaModelException e) {
473             handleException(null, e);
474         }
475     }
476     
477     private CompilationUnit getASTRoot(ICompilationUnit compilationUnit) {
478         return JavaPlugin.getDefault().getASTProvider().getAST(compilationUnit, ASTProvider.WAIT_NO, new NullProgressMonitor());
479     }
480     
481     /*
482      * Finds a type by the simple name. From AddImportsOperation
483      */

484     private TypeNameMatch[] findAllTypes(String JavaDoc simpleTypeName, IJavaSearchScope searchScope, SimpleName nameNode, IProgressMonitor monitor, ICompilationUnit cu) throws JavaModelException {
485         boolean is50OrHigher= JavaModelUtil.is50OrHigher(cu.getJavaProject());
486         
487         int typeKinds= SimilarElementsRequestor.ALL_TYPES;
488         if (nameNode != null) {
489             typeKinds= ASTResolving.getPossibleTypeKinds(nameNode, is50OrHigher);
490         }
491         
492         ArrayList JavaDoc typeInfos= new ArrayList JavaDoc();
493         TypeNameMatchCollector requestor= new TypeNameMatchCollector(typeInfos);
494         new SearchEngine().searchAllTypeNames(null, 0, simpleTypeName.toCharArray(), SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, getSearchForConstant(typeKinds), searchScope, requestor, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor);
495
496         ArrayList JavaDoc typeRefsFound= new ArrayList JavaDoc(typeInfos.size());
497         for (int i= 0, len= typeInfos.size(); i < len; i++) {
498             TypeNameMatch curr= (TypeNameMatch) typeInfos.get(i);
499             if (curr.getPackageName().length() > 0) { // do not suggest imports from the default package
500
if (isOfKind(curr, typeKinds, is50OrHigher) && isVisible(curr, cu)) {
501                     typeRefsFound.add(curr);
502                 }
503             }
504         }
505         return (TypeNameMatch[]) typeRefsFound.toArray(new TypeNameMatch[typeRefsFound.size()]);
506     }
507     
508     private int getSearchForConstant(int typeKinds) {
509         final int CLASSES= SimilarElementsRequestor.CLASSES;
510         final int INTERFACES= SimilarElementsRequestor.INTERFACES;
511         final int ENUMS= SimilarElementsRequestor.ENUMS;
512         final int ANNOTATIONS= SimilarElementsRequestor.ANNOTATIONS;
513
514         switch (typeKinds & (CLASSES | INTERFACES | ENUMS | ANNOTATIONS)) {
515             case CLASSES: return IJavaSearchConstants.CLASS;
516             case INTERFACES: return IJavaSearchConstants.INTERFACE;
517             case ENUMS: return IJavaSearchConstants.ENUM;
518             case ANNOTATIONS: return IJavaSearchConstants.ANNOTATION_TYPE;
519             case CLASSES | INTERFACES: return IJavaSearchConstants.CLASS_AND_INTERFACE;
520             case CLASSES | ENUMS: return IJavaSearchConstants.CLASS_AND_ENUM;
521             default: return IJavaSearchConstants.TYPE;
522         }
523     }
524     
525     private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) {
526         int flags= curr.getModifiers();
527         if (Flags.isAnnotation(flags)) {
528             return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0);
529         }
530         if (Flags.isEnum(flags)) {
531             return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ENUMS) != 0);
532         }
533         if (Flags.isInterface(flags)) {
534             return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0;
535         }
536         return (typeKinds & SimilarElementsRequestor.CLASSES) != 0;
537     }
538
539     
540     private boolean isVisible(TypeNameMatch curr, ICompilationUnit cu) {
541         int flags= curr.getModifiers();
542         if (Flags.isPrivate(flags)) {
543             return false;
544         }
545         if (Flags.isPublic(flags) || Flags.isProtected(flags)) {
546             return true;
547         }
548         return curr.getPackageName().equals(cu.getParent().getElementName());
549     }
550     
551     /**
552      * Evaluates a 'java' template in the context of a compilation unit
553      *
554      * @param template the template to be evaluated
555      * @param compilationUnit the compilation unit in which to evaluate the template
556      * @param position the position inside the compilation unit for which to evaluate the template
557      * @return the evaluated template
558      * @throws CoreException in case the template is of an unknown context type
559      * @throws BadLocationException in case the position is invalid in the compilation unit
560      * @throws TemplateException in case the evaluation fails
561      */

562     public static String JavaDoc evaluateTemplate(Template template, ICompilationUnit compilationUnit, int position) throws CoreException, BadLocationException, TemplateException {
563
564         TemplateContextType contextType= JavaPlugin.getDefault().getTemplateContextRegistry().getContextType(JavaContextType.NAME);
565         if (contextType == null)
566             throw new CoreException(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IStatus.ERROR, JavaTemplateMessages.JavaContext_error_message, null));
567
568         IDocument document= new Document();
569         if (compilationUnit != null && compilationUnit.exists())
570             document.set(compilationUnit.getSource());
571
572         JavaContext context= new JavaContext(contextType, document, position, 0, compilationUnit);
573         context.setForceEvaluation(true);
574
575         TemplateBuffer buffer= context.evaluate(template);
576         if (buffer == null)
577             return null;
578         return buffer.getString();
579     }
580
581     TemplateVariable getTemplateVariable(String JavaDoc name) {
582         TemplateVariable variable= (TemplateVariable) fVariables.get(name);
583         if (variable != null && !variable.isResolved())
584             getContextType().resolve(variable, this);
585         return variable;
586     }
587
588     /**
589      * Adds a multi-variable guess dependency.
590      *
591      * @param master the master variable - <code>slave</code> needs to be updated when
592      * <code>master</code> changes
593      * @param slave the dependent variable
594      * @since 3.3
595      */

596     public void addDependency(MultiVariable master, MultiVariable slave) {
597         MultiVariableGuess guess= getMultiVariableGuess();
598         if (guess == null) {
599             guess= new MultiVariableGuess();
600             setMultiVariableGuess(guess);
601         }
602         
603         guess.addDependency(master, slave);
604     }
605
606 }
607
Popular Tags