KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.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  * An experimental proposal.
66  */

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

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

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

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

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

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

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

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

232     protected char[] computeTriggerCharacters() {
233         return GENERIC_TYPE_TRIGGERS;
234     }
235
236     /**
237      * Adapt the parameter offsets to any modification of the replacement
238      * string done by <code>apply</code>. For example, applying the proposal
239      * may add an import instead of inserting the fully qualified name.
240      * <p>
241      * This assumes that modifications happen only at the beginning of the
242      * replacement string and do not touch the type arguments list.
243      * </p>
244      *
245      * @param offsets the offsets to modify
246      * @param buffer the original replacement string
247      */

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

275     private TypeArgumentProposal[] computeTypeArgumentProposals() throws JavaModelException {
276         if (fTypeArgumentProposals == null) {
277             
278             IType type= (IType) getJavaElement();
279             if (type == null)
280                 return new TypeArgumentProposal[0];
281             
282             ITypeParameter[] parameters= type.getTypeParameters();
283             if (parameters.length == 0)
284                 return new TypeArgumentProposal[0];
285             
286             TypeArgumentProposal[] arguments= new TypeArgumentProposal[parameters.length];
287             
288             ITypeBinding expectedTypeBinding= getExpectedType();
289             if (expectedTypeBinding != null && expectedTypeBinding.isParameterizedType()) {
290                 // in this case, the type arguments we propose need to be compatible
291
// with the corresponding type parameters to declared type
292

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

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

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

425     private IType[] computeInheritancePath(IType subType, IType superType) throws JavaModelException {
426         if (superType == null)
427             return null;
428
429         // optimization: avoid building the type hierarchy for the identity case
430
if (superType.equals(subType))
431             return new IType[] { subType };
432
433         ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(getProgressMonitor());
434         if (!hierarchy.contains(superType))
435             return null; // no path
436

437         List JavaDoc path= new LinkedList JavaDoc();
438         path.add(superType);
439         do {
440             // any sub type must be on a hierarchy chain from superType to subType
441
superType= hierarchy.getSubtypes(superType)[0];
442             path.add(superType);
443         } while (!superType.equals(subType)); // since the equality case is handled above, we can spare one check
444

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

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

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

540     private int findMatchingTypeArgumentIndex(String JavaDoc signature, String JavaDoc argument) {
541         String JavaDoc[] typeArguments= Signature.getTypeArguments(signature);
542         for (int i= 0; i < typeArguments.length; i++) {
543             if (Signature.getSignatureSimpleName(typeArguments[i]).equals(argument))
544                 return i;
545         }
546         return -1;
547     }
548
549     /**
550      * Returns the super interface signatures of <code>subType</code> if
551      * <code>superType</code> is an interface, otherwise returns the super
552      * type signature.
553      *
554      * @param subType the sub type signature
555      * @param superType the super type signature
556      * @return the super type signatures of <code>subType</code>
557      * @throws JavaModelException if any java model operation fails
558      */

559     private String JavaDoc[] getSuperTypeSignatures(IType subType, IType superType) throws JavaModelException {
560         if (superType.isInterface())
561             return subType.getSuperInterfaceTypeSignatures();
562         else
563             return new String JavaDoc[] {subType.getSuperclassTypeSignature()};
564     }
565
566     /**
567      * Returns the type binding of the expected type as it is contained in the
568      * code completion context.
569      *
570      * @return the binding of the expected type
571      */

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

610     private boolean shouldAppendArguments(IDocument document, int offset, char trigger) {
611         if (trigger != '\0' && trigger != '<')
612             return false;
613         
614         try {
615             IRegion region= document.getLineInformationOfOffset(offset);
616             String JavaDoc line= document.get(region.getOffset(), region.getLength());
617
618             int index= offset - region.getOffset();
619             while (index != line.length() && Character.isUnicodeIdentifierPart(line.charAt(index)))
620                 ++index;
621
622             if (index == line.length())
623                 return true;
624
625             char ch= line.charAt(index);
626             return ch != '<';
627
628         } catch (BadLocationException e) {
629             return true;
630         }
631     }
632
633     private StringBuffer JavaDoc createParameterList(TypeArgumentProposal[] typeArguments, int[] offsets, int[] lengths) {
634         StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
635         buffer.append(getReplacementString());
636
637         FormatterPrefs prefs= getFormatterPrefs();
638         final char LESS= '<';
639         final char GREATER= '>';
640         if (prefs.beforeOpeningBracket)
641             buffer.append(SPACE);
642         buffer.append(LESS);
643         if (prefs.afterOpeningBracket)
644             buffer.append(SPACE);
645         StringBuffer JavaDoc separator= new StringBuffer JavaDoc(3);
646         if (prefs.beforeTypeArgumentComma)
647             separator.append(SPACE);
648         separator.append(COMMA);
649         if (prefs.afterTypeArgumentComma)
650             separator.append(SPACE);
651
652         for (int i= 0; i != typeArguments.length; i++) {
653             if (i != 0)
654                 buffer.append(separator);
655
656             offsets[i]= buffer.length();
657             buffer.append(typeArguments[i]);
658             lengths[i]= buffer.length() - offsets[i];
659         }
660         if (prefs.beforeClosingBracket)
661             buffer.append(SPACE);
662         buffer.append(GREATER);
663
664         return buffer;
665     }
666
667     private void installLinkedMode(IDocument document, int[] offsets, int[] lengths, TypeArgumentProposal[] typeArgumentProposals) {
668         int replacementOffset= getReplacementOffset();
669         String JavaDoc replacementString= getReplacementString();
670
671         try {
672             LinkedModeModel model= new LinkedModeModel();
673             for (int i= 0; i != offsets.length; i++) {
674                 if (typeArgumentProposals[i].isAmbiguous()) {
675                     LinkedPositionGroup group= new LinkedPositionGroup();
676                     group.addPosition(new LinkedPosition(document, replacementOffset + offsets[i], lengths[i], LinkedPositionGroup.NO_STOP));
677                     model.addGroup(group);
678                 }
679             }
680
681             model.forceInstall();
682             JavaEditor editor= getJavaEditor();
683             if (editor != null) {
684                 model.addLinkingListener(new EditorHighlightingSynchronizer(editor));
685             }
686
687             LinkedModeUI ui= new EditorLinkedModeUI(model, getTextViewer());
688             ui.setExitPolicy(new ExitPolicy('>', document));
689             ui.setExitPosition(getTextViewer(), replacementOffset + replacementString.length(), 0, Integer.MAX_VALUE);
690             ui.setDoContextInfo(true);
691             ui.enter();
692
693             fSelectedRegion= ui.getSelectedRegion();
694
695         } catch (BadLocationException e) {
696             JavaPlugin.log(e);
697             openErrorDialog(e);
698         }
699     }
700
701     private boolean hasAmbiguousProposals(TypeArgumentProposal[] typeArgumentProposals) {
702         boolean hasAmbiguousProposals= false;
703         for (int i= 0; i < typeArgumentProposals.length; i++) {
704             if (typeArgumentProposals[i].isAmbiguous()) {
705                 hasAmbiguousProposals= true;
706                 break;
707             }
708         }
709         return hasAmbiguousProposals;
710     }
711
712     /**
713      * Returns the currently active java editor, or <code>null</code> if it
714      * cannot be determined.
715      *
716      * @return the currently active java editor, or <code>null</code>
717      */

718     private JavaEditor getJavaEditor() {
719         IEditorPart part= JavaPlugin.getActivePage().getActiveEditor();
720         if (part instanceof JavaEditor)
721             return (JavaEditor) part;
722         else
723             return null;
724     }
725
726     /*
727      * @see ICompletionProposal#getSelection(IDocument)
728      */

729     public Point getSelection(IDocument document) {
730         if (fSelectedRegion == null)
731             return super.getSelection(document);
732
733         return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
734     }
735
736     private void openErrorDialog(BadLocationException e) {
737         Shell shell= getTextViewer().getTextWidget().getShell();
738         MessageDialog.openError(shell, JavaTextMessages.ExperimentalProposal_error_msg, e.getMessage());
739     }
740
741     /*
742      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation()
743      */

744     protected IContextInformation computeContextInformation() {
745         // only return information if we're already computed
746
// -> avoids creating context information for invalid proposals
747
if (fTypeArgumentProposals != null) {
748             try {
749                 if (hasParameters()) {
750                     TypeArgumentProposal[] proposals= computeTypeArgumentProposals();
751                     if (hasAmbiguousProposals(proposals))
752                         return new ContextInformation(this);
753                 }
754             } catch (JavaModelException e) {
755             }
756         }
757         return super.computeContextInformation();
758     }
759     
760     protected int computeCursorPosition() {
761         if (fSelectedRegion != null)
762             return fSelectedRegion.getOffset() - getReplacementOffset();
763         return super.computeCursorPosition();
764     }
765     
766     private boolean hasParameters() {
767         try {
768             IType type= (IType) getJavaElement();
769             if (type == null)
770                 return false;
771
772             return type.getTypeParameters().length > 0;
773         } catch (JavaModelException e) {
774             return false;
775         }
776     }
777 }
778
Popular Tags