KickJava   Java API By Example, From Geeks To Geeks.

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


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.ui.text.java;
12
13 import java.util.HashMap JavaDoc;
14 import java.util.LinkedList JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.eclipse.core.runtime.CoreException;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.NullProgressMonitor;
21 import org.eclipse.core.runtime.Status;
22
23 import org.eclipse.swt.graphics.Image;
24 import org.eclipse.swt.graphics.Point;
25 import org.eclipse.swt.widgets.Shell;
26
27 import org.eclipse.jface.dialogs.MessageDialog;
28
29 import org.eclipse.jface.text.BadLocationException;
30 import org.eclipse.jface.text.IDocument;
31 import org.eclipse.jface.text.IRegion;
32 import org.eclipse.jface.text.Region;
33 import org.eclipse.jface.text.contentassist.IContextInformation;
34 import org.eclipse.jface.text.contentassist.IContextInformationExtension;
35 import org.eclipse.jface.text.link.LinkedModeModel;
36 import org.eclipse.jface.text.link.LinkedModeUI;
37 import org.eclipse.jface.text.link.LinkedPosition;
38 import org.eclipse.jface.text.link.LinkedPositionGroup;
39
40 import org.eclipse.ui.IEditorPart;
41 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
42
43 import org.eclipse.jdt.core.CompletionProposal;
44 import org.eclipse.jdt.core.ICompilationUnit;
45 import org.eclipse.jdt.core.IType;
46 import org.eclipse.jdt.core.ITypeHierarchy;
47 import org.eclipse.jdt.core.ITypeParameter;
48 import org.eclipse.jdt.core.JavaModelException;
49 import org.eclipse.jdt.core.Signature;
50 import org.eclipse.jdt.core.dom.AST;
51 import org.eclipse.jdt.core.dom.ASTParser;
52 import org.eclipse.jdt.core.dom.ASTRequestor;
53 import org.eclipse.jdt.core.dom.IBinding;
54 import org.eclipse.jdt.core.dom.ITypeBinding;
55
56 import org.eclipse.jdt.internal.corext.template.java.SignatureUtil;
57
58 import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
59
60 import org.eclipse.jdt.internal.ui.JavaPlugin;
61 import org.eclipse.jdt.internal.ui.javaeditor.EditorHighlightingSynchronizer;
62 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
63
64 /**
65  * Proposal for generic types.
66  * <p>
67  * Only used when compliance is set to 5.0 or higher.
68  * </p>
69  */

70 public final class LazyGenericTypeProposal extends LazyJavaTypeCompletionProposal {
71     /** Triggers for types. Do not modify. */
72     private final static char[] GENERIC_TYPE_TRIGGERS= new char[] { '.', '\t', '[', '(', '<', ' ' };
73
74     /**
75      * Short-lived context information object for generic types. Currently, these
76      * are only created after inserting a type proposal, as core doesn't give us
77      * the correct type proposal from within SomeType<|>.
78      */

79     private static class ContextInformation implements IContextInformation, IContextInformationExtension {
80         private final String JavaDoc fInformationDisplayString;
81         private final String JavaDoc fContextDisplayString;
82         private final Image fImage;
83         private final int fPosition;
84         
85         ContextInformation(LazyGenericTypeProposal proposal) {
86             // don't cache the proposal as content assistant
87
// might hang on to the context info
88
fContextDisplayString= proposal.getDisplayString();
89             fInformationDisplayString= computeContextString(proposal);
90             fImage= proposal.getImage();
91             fPosition= proposal.getReplacementOffset() + proposal.getReplacementString().indexOf('<') + 1;
92         }
93         
94         /*
95          * @see org.eclipse.jface.text.contentassist.IContextInformation#getContextDisplayString()
96          */

97         public String JavaDoc getContextDisplayString() {
98             return fContextDisplayString;
99         }
100
101         /*
102          * @see org.eclipse.jface.text.contentassist.IContextInformation#getImage()
103          */

104         public Image getImage() {
105             return fImage;
106         }
107
108         /*
109          * @see org.eclipse.jface.text.contentassist.IContextInformation#getInformationDisplayString()
110          */

111         public String JavaDoc getInformationDisplayString() {
112             return fInformationDisplayString;
113         }
114
115         private String JavaDoc computeContextString(LazyGenericTypeProposal proposal) {
116             try {
117                 TypeArgumentProposal[] proposals= proposal.computeTypeArgumentProposals();
118                 if (proposals.length == 0)
119                     return null;
120                 
121                 StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
122                 for (int i= 0; i < proposals.length; i++) {
123                     buf.append(proposals[i].getDisplayName());
124                     if (i < proposals.length - 1)
125                         buf.append(", "); //$NON-NLS-1$
126
}
127                 return buf.toString();
128                 
129             } catch (JavaModelException e) {
130                 return null;
131             }
132         }
133
134         /*
135          * @see org.eclipse.jface.text.contentassist.IContextInformationExtension#getContextInformationPosition()
136          */

137         public int getContextInformationPosition() {
138             return fPosition;
139         }
140         
141         /*
142          * @see java.lang.Object#equals(java.lang.Object)
143          */

144         public boolean equals(Object JavaDoc obj) {
145             if (obj instanceof ContextInformation) {
146                 ContextInformation ci= (ContextInformation) obj;
147                 return getContextInformationPosition() == ci.getContextInformationPosition() && getInformationDisplayString().equals(ci.getInformationDisplayString());
148             }
149             return false;
150         }
151     }
152
153     private static final class TypeArgumentProposal {
154         private final boolean fIsAmbiguous;
155         private final String JavaDoc fProposal;
156         private final String JavaDoc fTypeDisplayName;
157
158         TypeArgumentProposal(String JavaDoc proposal, boolean ambiguous, String JavaDoc typeDisplayName) {
159             fIsAmbiguous= ambiguous;
160             fProposal= proposal;
161             fTypeDisplayName= typeDisplayName;
162         }
163
164         public String JavaDoc getDisplayName() {
165             return fTypeDisplayName;
166         }
167
168         boolean isAmbiguous() {
169             return fIsAmbiguous;
170         }
171
172         String JavaDoc getProposals() {
173             return fProposal;
174         }
175
176         public String JavaDoc toString() {
177             return fProposal;
178         }
179     }
180
181     private IRegion fSelectedRegion; // initialized by apply()
182
private TypeArgumentProposal[] fTypeArgumentProposals;
183
184     public LazyGenericTypeProposal(CompletionProposal typeProposal, JavaContentAssistInvocationContext context) {
185         super(typeProposal, context);
186     }
187
188     /*
189      * @see ICompletionProposalExtension#apply(IDocument, char)
190      */

191     public void apply(IDocument document, char trigger, int offset) {
192
193         if (shouldAppendArguments(document, offset, trigger)) {
194             try {
195                 TypeArgumentProposal[] typeArgumentProposals= computeTypeArgumentProposals();
196                 if (typeArgumentProposals.length > 0) {
197
198                     int[] offsets= new int[typeArgumentProposals.length];
199                     int[] lengths= new int[typeArgumentProposals.length];
200                     StringBuffer JavaDoc buffer= createParameterList(typeArgumentProposals, offsets, lengths);
201
202                     // set the generic type as replacement string
203
boolean insertClosingParenthesis= trigger == '(' && autocloseBrackets();
204                     if (insertClosingParenthesis)
205                         updateReplacementWithParentheses(buffer);
206                     super.setReplacementString(buffer.toString());
207
208                     // add import & remove package, update replacement offset
209
super.apply(document, '\0', offset);
210
211                     if (getTextViewer() != null) {
212                         if (hasAmbiguousProposals(typeArgumentProposals)) {
213                             adaptOffsets(offsets, buffer);
214                             installLinkedMode(document, offsets, lengths, typeArgumentProposals, insertClosingParenthesis);
215                         } else {
216                             if (insertClosingParenthesis)
217                                 setUpLinkedMode(document, ')');
218                             else
219                                 fSelectedRegion= new Region(getReplacementOffset() + getReplacementString().length(), 0);
220                         }
221                     }
222
223                     return;
224                 }
225             } catch (JavaModelException e) {
226                 // log and continue
227
JavaPlugin.log(e);
228             }
229         }
230
231         // default is to use the super implementation
232
// reasons:
233
// - not a parameterized type,
234
// - already followed by <type arguments>
235
// - proposal type does not inherit from expected type
236
super.apply(document, trigger, offset);
237     }
238     
239     /*
240      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal#computeTriggerCharacters()
241      */

242     protected char[] computeTriggerCharacters() {
243         return GENERIC_TYPE_TRIGGERS;
244     }
245
246     /**
247      * Adapt the parameter offsets to any modification of the replacement
248      * string done by <code>apply</code>. For example, applying the proposal
249      * may add an import instead of inserting the fully qualified name.
250      * <p>
251      * This assumes that modifications happen only at the beginning of the
252      * replacement string and do not touch the type arguments list.
253      * </p>
254      *
255      * @param offsets the offsets to modify
256      * @param buffer the original replacement string
257      */

258     private void adaptOffsets(int[] offsets, StringBuffer JavaDoc buffer) {
259         String JavaDoc replacementString= getReplacementString();
260         int delta= buffer.length() - replacementString.length(); // due to using an import instead of package
261
for (int i= 0; i < offsets.length; i++) {
262             offsets[i]-= delta;
263         }
264     }
265
266     /**
267      * Computes the type argument proposals for this type proposals. If there is
268      * an expected type binding that is a super type of the proposed type, the
269      * wildcard type arguments of the proposed type that can be mapped through
270      * to type the arguments of the expected type binding are bound accordingly.
271      * <p>
272      * For type arguments that cannot be mapped to arguments in the expected
273      * type, or if there is no expected type, the upper bound of the type
274      * argument is proposed.
275      * </p>
276      * <p>
277      * The argument proposals have their <code>isAmbiguos</code> flag set to
278      * <code>false</code> if the argument can be mapped to a non-wildcard type
279      * argument in the expected type, otherwise the proposal is ambiguous.
280      * </p>
281      *
282      * @return the type argument proposals for the proposed type
283      * @throws JavaModelException if accessing the java model fails
284      */

285     private TypeArgumentProposal[] computeTypeArgumentProposals() throws JavaModelException {
286         if (fTypeArgumentProposals == null) {
287             
288             IType type= (IType) getJavaElement();
289             if (type == null)
290                 return new TypeArgumentProposal[0];
291             
292             ITypeParameter[] parameters= type.getTypeParameters();
293             if (parameters.length == 0)
294                 return new TypeArgumentProposal[0];
295             
296             TypeArgumentProposal[] arguments= new TypeArgumentProposal[parameters.length];
297             
298             ITypeBinding expectedTypeBinding= getExpectedType();
299             if (expectedTypeBinding != null && expectedTypeBinding.isParameterizedType()) {
300                 // in this case, the type arguments we propose need to be compatible
301
// with the corresponding type parameters to declared type
302

303                 IType expectedType= (IType) expectedTypeBinding.getJavaElement();
304                 
305                 IType[] path= computeInheritancePath(type, expectedType);
306                 if (path == null)
307                     // proposed type does not inherit from expected type
308
// the user might be looking for an inner type of proposed type
309
// to instantiate -> do not add any type arguments
310
return new TypeArgumentProposal[0];
311                 
312                 int[] indices= new int[parameters.length];
313                 for (int paramIdx= 0; paramIdx < parameters.length; paramIdx++) {
314                     indices[paramIdx]= mapTypeParameterIndex(path, path.length - 1, paramIdx);
315                 }
316                 
317                 // for type arguments that are mapped through to the expected type's
318
// parameters, take the arguments of the expected type
319
ITypeBinding[] typeArguments= expectedTypeBinding.getTypeArguments();
320                 for (int paramIdx= 0; paramIdx < parameters.length; paramIdx++) {
321                     if (indices[paramIdx] != -1) {
322                         // type argument is mapped through
323
ITypeBinding binding= typeArguments[indices[paramIdx]];
324                         arguments[paramIdx]= computeTypeProposal(binding, parameters[paramIdx]);
325                     }
326                 }
327             }
328             
329             // for type arguments that are not mapped through to the expected type,
330
// take the lower bound of the type parameter
331
for (int i= 0; i < arguments.length; i++) {
332                 if (arguments[i] == null) {
333                     arguments[i]= computeTypeProposal(parameters[i]);
334                 }
335             }
336             fTypeArgumentProposals= arguments;
337         }
338         return fTypeArgumentProposals;
339     }
340
341     /**
342      * Returns a type argument proposal for a given type parameter. The proposal is:
343      * <ul>
344      * <li>the type bound for type parameters with a single bound</li>
345      * <li>the type parameter name for all other (unbounded or more than one bound) type parameters</li>
346      * </ul>
347      * Type argument proposals for type parameters are always ambiguous.
348      *
349      * @param parameter the type parameter of the inserted type
350      * @return a type argument proposal for <code>parameter</code>
351      * @throws JavaModelException
352      */

353     private TypeArgumentProposal computeTypeProposal(ITypeParameter parameter) throws JavaModelException {
354         String JavaDoc[] bounds= parameter.getBounds();
355         String JavaDoc elementName= parameter.getElementName();
356         String JavaDoc displayName= computeTypeParameterDisplayName(parameter, bounds);
357         if (bounds.length == 1 && !"java.lang.Object".equals(bounds[0])) //$NON-NLS-1$
358
return new TypeArgumentProposal(Signature.getSimpleName(bounds[0]), true, displayName);
359         else
360             return new TypeArgumentProposal(elementName, true, displayName);
361     }
362
363     private String JavaDoc computeTypeParameterDisplayName(ITypeParameter parameter, String JavaDoc[] bounds) {
364         if (bounds.length == 0 || bounds.length == 1 && "java.lang.Object".equals(bounds[0])) //$NON-NLS-1$
365
return parameter.getElementName();
366         StringBuffer JavaDoc buf= new StringBuffer JavaDoc(parameter.getElementName());
367         buf.append(" extends "); //$NON-NLS-1$
368
for (int i= 0; i < bounds.length; i++) {
369             buf.append(Signature.getSimpleName(bounds[i]));
370             if (i < bounds.length - 1)
371                 buf.append(" & "); //$NON-NLS-1$
372
}
373         return buf.toString();
374     }
375
376     /**
377      * Returns a type argument proposal for a given type binding. The proposal is:
378      * <ul>
379      * <li>the simple type name for normal types or type variables (unambigous proposal)</li>
380      * <li>for wildcard types (ambigous proposals):
381      * <ul>
382      * <li>the upper bound for wildcards with an upper bound</li>
383      * <li>the {@linkplain #computeTypeProposal(ITypeParameter) parameter proposal} for unbounded
384      * wildcards or wildcards with a lower bound</li>
385      * </ul>
386      * </li>
387      * </ul>
388      *
389      * @param binding the type argument binding in the expected type
390      * @param parameter the type parameter of the inserted type
391      * @return a type argument proposal for <code>binding</code>
392      * @throws JavaModelException
393      * @see #computeTypeProposal(ITypeParameter)
394      */

395     private TypeArgumentProposal computeTypeProposal(ITypeBinding binding, ITypeParameter parameter) throws JavaModelException {
396         final String JavaDoc name= binding.getName();
397         if (binding.isWildcardType()) {
398
399             if (binding.isUpperbound()) {
400                 // replace the wildcard ? with the type parameter name to get "E extends Bound" instead of "? extends Bound"
401
String JavaDoc contextName= name.replaceFirst("\\?", parameter.getElementName()); //$NON-NLS-1$
402
// upper bound - the upper bound is the bound itself
403
return new TypeArgumentProposal(binding.getBound().getName(), true, contextName);
404             }
405             
406             // no or upper bound - use the type parameter of the inserted type, as it may be more
407
// restrictive (eg. List<?> list= new SerializableList<Serializable>())
408
return computeTypeProposal(parameter);
409         }
410
411         // not a wildcard but a type or type variable - this is unambigously the right thing to insert
412
return new TypeArgumentProposal(name, false, name);
413     }
414
415     /**
416      * Computes one inheritance path from <code>superType</code> to
417      * <code>subType</code> or <code>null</code> if <code>subType</code>
418      * does not inherit from <code>superType</code>. Note that there may be
419      * more than one inheritance path - this method simply returns one.
420      * <p>
421      * The returned array contains <code>superType</code> at its first index,
422      * and <code>subType</code> at its last index. If <code>subType</code>
423      * equals <code>superType</code>, an array of length 1 is returned
424      * containing that type.
425      * </p>
426      *
427      * @param subType the sub type
428      * @param superType the super type
429      * @return an inheritance path from <code>superType</code> to
430      * <code>subType</code>, or <code>null</code> if
431      * <code>subType</code> does not inherit from
432      * <code>superType</code>
433      * @throws JavaModelException
434      */

435     private IType[] computeInheritancePath(IType subType, IType superType) throws JavaModelException {
436         if (superType == null)
437             return null;
438
439         // optimization: avoid building the type hierarchy for the identity case
440
if (superType.equals(subType))
441             return new IType[] { subType };
442
443         ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(getProgressMonitor());
444         if (!hierarchy.contains(superType))
445             return null; // no path
446

447         List JavaDoc path= new LinkedList JavaDoc();
448         path.add(superType);
449         do {
450             // any sub type must be on a hierarchy chain from superType to subType
451
superType= hierarchy.getSubtypes(superType)[0];
452             path.add(superType);
453         } while (!superType.equals(subType)); // since the equality case is handled above, we can spare one check
454

455         return (IType[]) path.toArray(new IType[path.size()]);
456     }
457
458     private NullProgressMonitor getProgressMonitor() {
459         return new NullProgressMonitor();
460     }
461
462     /**
463      * For the type parameter at <code>paramIndex</code> in the type at
464      * <code>path[pathIndex]</code>, this method computes the corresponding
465      * type parameter index in the type at <code>path[0]</code>. If the type
466      * parameter does not map to a type parameter of the super type,
467      * <code>-1</code> is returned.
468      *
469      * @param path the type inheritance path, a non-empty array of consecutive
470      * sub types
471      * @param pathIndex an index into <code>path</code> specifying the type to
472      * start with
473      * @param paramIndex the index of the type parameter to map -
474      * <code>path[pathIndex]</code> must have a type parameter at that
475      * index, lest an <code>ArrayIndexOutOfBoundsException</code> is
476      * thrown
477      * @return the index of the type parameter in <code>path[0]</code>
478      * corresponding to the type parameter at <code>paramIndex</code>
479      * in <code>path[pathIndex]</code>, or -1 if there is no
480      * corresponding type parameter
481      * @throws JavaModelException
482      * @throws ArrayIndexOutOfBoundsException if <code>path[pathIndex]</code>
483      * has &lt;= <code>paramIndex</code> parameters
484      */

485     private int mapTypeParameterIndex(IType[] path, int pathIndex, int paramIndex) throws JavaModelException, ArrayIndexOutOfBoundsException JavaDoc {
486         if (pathIndex == 0)
487             // break condition: we've reached the top of the hierarchy
488
return paramIndex;
489
490         IType subType= path[pathIndex];
491         IType superType= path[pathIndex - 1];
492
493         String JavaDoc superSignature= findMatchingSuperTypeSignature(subType, superType);
494         ITypeParameter param= subType.getTypeParameters()[paramIndex];
495         int index= findMatchingTypeArgumentIndex(superSignature, param.getElementName());
496         if (index == -1) {
497             // not mapped through
498
return -1;
499         }
500
501         return mapTypeParameterIndex(path, pathIndex - 1, index);
502     }
503
504     /**
505      * Finds and returns the super type signature in the
506      * <code>extends</code> or <code>implements</code> clause of
507      * <code>subType</code> that corresponds to <code>superType</code>.
508      *
509      * @param subType a direct and true sub type of <code>superType</code>
510      * @param superType a direct super type (super class or interface) of
511      * <code>subType</code>
512      * @return the super type signature of <code>subType</code> referring
513      * to <code>superType</code>
514      * @throws JavaModelException if extracting the super type signatures
515      * fails, or if <code>subType</code> contains no super type
516      * signature to <code>superType</code>
517      */

518     private String JavaDoc findMatchingSuperTypeSignature(IType subType, IType superType) throws JavaModelException {
519         String JavaDoc[] signatures= getSuperTypeSignatures(subType, superType);
520         for (int i= 0; i < signatures.length; i++) {
521             String JavaDoc signature= signatures[i];
522             String JavaDoc qualified= SignatureUtil.qualifySignature(signature, subType);
523             String JavaDoc subFQN= SignatureUtil.stripSignatureToFQN(qualified);
524
525             String JavaDoc superFQN= superType.getFullyQualifiedName();
526             if (subFQN.equals(superFQN)) {
527                 return signature;
528             }
529
530             // TODO handle local types
531
}
532
533         throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "Illegal hierarchy", null))); //$NON-NLS-1$
534
}
535
536     /**
537      * Finds and returns the index of the type argument named
538      * <code>argument</code> in the given super type signature.
539      * <p>
540      * If <code>signature</code> does not contain a corresponding type
541      * argument, or if <code>signature</code> has no type parameters (i.e. is
542      * a reference to a non-parameterized type or a raw type), -1 is returned.
543      * </p>
544      *
545      * @param signature the super type signature from a type's
546      * <code>extends</code> or <code>implements</code> clause
547      * @param argument the name of the type argument to find
548      * @return the index of the given type argument, or -1 if there is none
549      */

550     private int findMatchingTypeArgumentIndex(String JavaDoc signature, String JavaDoc argument) {
551         String JavaDoc[] typeArguments= Signature.getTypeArguments(signature);
552         for (int i= 0; i < typeArguments.length; i++) {
553             if (Signature.getSignatureSimpleName(typeArguments[i]).equals(argument))
554                 return i;
555         }
556         return -1;
557     }
558
559     /**
560      * Returns the super interface signatures of <code>subType</code> if
561      * <code>superType</code> is an interface, otherwise returns the super
562      * type signature.
563      *
564      * @param subType the sub type signature
565      * @param superType the super type signature
566      * @return the super type signatures of <code>subType</code>
567      * @throws JavaModelException if any java model operation fails
568      */

569     private String JavaDoc[] getSuperTypeSignatures(IType subType, IType superType) throws JavaModelException {
570         if (superType.isInterface())
571             return subType.getSuperInterfaceTypeSignatures();
572         else
573             return new String JavaDoc[] {subType.getSuperclassTypeSignature()};
574     }
575
576     /**
577      * Returns the type binding of the expected type as it is contained in the
578      * code completion context.
579      *
580      * @return the binding of the expected type
581      */

582     private ITypeBinding getExpectedType() {
583         char[][] chKeys= fInvocationContext.getCoreContext().getExpectedTypesKeys();
584         if (chKeys == null || chKeys.length == 0)
585             return null;
586
587         String JavaDoc[] keys= new String JavaDoc[chKeys.length];
588         for (int i= 0; i < keys.length; i++) {
589             keys[i]= String.valueOf(chKeys[0]);
590         }
591
592         final ASTParser parser= ASTParser.newParser(AST.JLS3);
593         parser.setProject(fCompilationUnit.getJavaProject());
594         parser.setResolveBindings(true);
595
596         final Map JavaDoc bindings= new HashMap JavaDoc();
597         ASTRequestor requestor= new ASTRequestor() {
598             public void acceptBinding(String JavaDoc bindingKey, IBinding binding) {
599                 bindings.put(bindingKey, binding);
600             }
601         };
602         parser.createASTs(new ICompilationUnit[0], keys, requestor, null);
603
604         if (bindings.size() > 0)
605             return (ITypeBinding) bindings.get(keys[0]);
606
607         return null;
608     }
609
610     /**
611      * Returns <code>true</code> if type arguments should be appended when
612      * applying this proposal, <code>false</code> if not (for example if the
613      * document already contains a type argument list after the insertion point.
614      *
615      * @param document the document
616      * @param offset the insertion offset
617      * @param trigger the trigger character
618      * @return <code>true</code> if arguments should be appended
619      */

620     private boolean shouldAppendArguments(IDocument document, int offset, char trigger) {
621         /*
622          * No argument list if there were any special triggers (for example a period to qualify an
623          * inner type).
624          */

625         if (trigger != '\0' && trigger != '<' && trigger != '(')
626             return false;
627         
628         /* No argument list if the completion is empty (already within the argument list). */
629         char[] completion= fProposal.getCompletion();
630         if (completion.length == 0)
631             return false;
632
633         /* No argument list if there already is a generic signature behind the name. */
634         try {
635             IRegion region= document.getLineInformationOfOffset(offset);
636             String JavaDoc line= document.get(region.getOffset(), region.getLength());
637
638             int index= offset - region.getOffset();
639             while (index != line.length() && Character.isUnicodeIdentifierPart(line.charAt(index)))
640                 ++index;
641
642             if (index == line.length())
643                 return true;
644
645             char ch= line.charAt(index);
646             return ch != '<';
647
648         } catch (BadLocationException e) {
649             return true;
650         }
651     }
652
653     private StringBuffer JavaDoc createParameterList(TypeArgumentProposal[] typeArguments, int[] offsets, int[] lengths) {
654         StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
655         buffer.append(getReplacementString());
656
657         FormatterPrefs prefs= getFormatterPrefs();
658         final char LESS= '<';
659         final char GREATER= '>';
660         if (prefs.beforeOpeningBracket)
661             buffer.append(SPACE);
662         buffer.append(LESS);
663         if (prefs.afterOpeningBracket)
664             buffer.append(SPACE);
665         StringBuffer JavaDoc separator= new StringBuffer JavaDoc(3);
666         if (prefs.beforeTypeArgumentComma)
667             separator.append(SPACE);
668         separator.append(COMMA);
669         if (prefs.afterTypeArgumentComma)
670             separator.append(SPACE);
671
672         for (int i= 0; i != typeArguments.length; i++) {
673             if (i != 0)
674                 buffer.append(separator);
675
676             offsets[i]= buffer.length();
677             buffer.append(typeArguments[i]);
678             lengths[i]= buffer.length() - offsets[i];
679         }
680         if (prefs.beforeClosingBracket)
681             buffer.append(SPACE);
682         buffer.append(GREATER);
683
684         return buffer;
685     }
686
687     private void installLinkedMode(IDocument document, int[] offsets, int[] lengths, TypeArgumentProposal[] typeArgumentProposals, boolean withParentheses) {
688         int replacementOffset= getReplacementOffset();
689         String JavaDoc replacementString= getReplacementString();
690
691         try {
692             LinkedModeModel model= new LinkedModeModel();
693             for (int i= 0; i != offsets.length; i++) {
694                 if (typeArgumentProposals[i].isAmbiguous()) {
695                     LinkedPositionGroup group= new LinkedPositionGroup();
696                     group.addPosition(new LinkedPosition(document, replacementOffset + offsets[i], lengths[i]));
697                     model.addGroup(group);
698                 }
699             }
700             if (withParentheses) {
701                 LinkedPositionGroup group= new LinkedPositionGroup();
702                 group.addPosition(new LinkedPosition(document, replacementOffset + getCursorPosition(), 0));
703                 model.addGroup(group);
704             }
705
706             model.forceInstall();
707             JavaEditor editor= getJavaEditor();
708             if (editor != null) {
709                 model.addLinkingListener(new EditorHighlightingSynchronizer(editor));
710             }
711
712             LinkedModeUI ui= new EditorLinkedModeUI(model, getTextViewer());
713             ui.setExitPolicy(new ExitPolicy(withParentheses ? ')' : '>', document));
714             ui.setExitPosition(getTextViewer(), replacementOffset + replacementString.length(), 0, Integer.MAX_VALUE);
715             ui.setDoContextInfo(true);
716             ui.enter();
717
718             fSelectedRegion= ui.getSelectedRegion();
719
720         } catch (BadLocationException e) {
721             JavaPlugin.log(e);
722             openErrorDialog(e);
723         }
724     }
725
726     private boolean hasAmbiguousProposals(TypeArgumentProposal[] typeArgumentProposals) {
727         boolean hasAmbiguousProposals= false;
728         for (int i= 0; i < typeArgumentProposals.length; i++) {
729             if (typeArgumentProposals[i].isAmbiguous()) {
730                 hasAmbiguousProposals= true;
731                 break;
732             }
733         }
734         return hasAmbiguousProposals;
735     }
736
737     /**
738      * Returns the currently active java editor, or <code>null</code> if it
739      * cannot be determined.
740      *
741      * @return the currently active java editor, or <code>null</code>
742      */

743     private JavaEditor getJavaEditor() {
744         IEditorPart part= JavaPlugin.getActivePage().getActiveEditor();
745         if (part instanceof JavaEditor)
746             return (JavaEditor) part;
747         else
748             return null;
749     }
750
751     /*
752      * @see ICompletionProposal#getSelection(IDocument)
753      */

754     public Point getSelection(IDocument document) {
755         if (fSelectedRegion == null)
756             return super.getSelection(document);
757
758         return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
759     }
760
761     private void openErrorDialog(BadLocationException e) {
762         Shell shell= getTextViewer().getTextWidget().getShell();
763         MessageDialog.openError(shell, JavaTextMessages.FilledArgumentNamesMethodProposal_error_msg, e.getMessage());
764     }
765
766     /*
767      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation()
768      */

769     protected IContextInformation computeContextInformation() {
770         try {
771             if (hasParameters()) {
772                 TypeArgumentProposal[] proposals= computeTypeArgumentProposals();
773                 if (hasAmbiguousProposals(proposals))
774                     return new ContextInformation(this);
775             }
776         } catch (JavaModelException e) {
777         }
778         return super.computeContextInformation();
779     }
780     
781     protected int computeCursorPosition() {
782         if (fSelectedRegion != null)
783             return fSelectedRegion.getOffset() - getReplacementOffset();
784         return super.computeCursorPosition();
785     }
786     
787     private boolean hasParameters() {
788         try {
789             IType type= (IType) getJavaElement();
790             if (type == null)
791                 return false;
792
793             return type.getTypeParameters().length > 0;
794         } catch (JavaModelException e) {
795             return false;
796         }
797     }
798 }
799
Popular Tags