KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2005, 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.io.BufferedReader JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStreamReader JavaDoc;
16 import java.net.URL JavaDoc;
17
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.core.runtime.FileLocator;
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.NullProgressMonitor;
22 import org.eclipse.core.runtime.Platform;
23
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.custom.StyleRange;
26 import org.eclipse.swt.custom.StyledText;
27 import org.eclipse.swt.events.VerifyEvent;
28 import org.eclipse.swt.graphics.Color;
29 import org.eclipse.swt.graphics.FontData;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.graphics.RGB;
33 import org.eclipse.swt.widgets.Shell;
34
35 import org.eclipse.jface.internal.text.html.BrowserInformationControl;
36 import org.eclipse.jface.internal.text.html.HTMLPrinter;
37 import org.eclipse.jface.preference.IPreferenceStore;
38 import org.eclipse.jface.preference.PreferenceConverter;
39 import org.eclipse.jface.resource.JFaceResources;
40
41 import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
42 import org.eclipse.jface.text.BadLocationException;
43 import org.eclipse.jface.text.BadPositionCategoryException;
44 import org.eclipse.jface.text.DefaultPositionUpdater;
45 import org.eclipse.jface.text.DocumentCommand;
46 import org.eclipse.jface.text.DocumentEvent;
47 import org.eclipse.jface.text.IDocument;
48 import org.eclipse.jface.text.IInformationControl;
49 import org.eclipse.jface.text.IInformationControlCreator;
50 import org.eclipse.jface.text.IPositionUpdater;
51 import org.eclipse.jface.text.IRegion;
52 import org.eclipse.jface.text.ITextViewer;
53 import org.eclipse.jface.text.ITextViewerExtension2;
54 import org.eclipse.jface.text.ITextViewerExtension5;
55 import org.eclipse.jface.text.Position;
56 import org.eclipse.jface.text.Region;
57 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
58 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
59 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
60 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
61 import org.eclipse.jface.text.contentassist.IContextInformation;
62 import org.eclipse.jface.text.link.ILinkedModeListener;
63 import org.eclipse.jface.text.link.LinkedModeModel;
64 import org.eclipse.jface.text.link.LinkedModeUI;
65 import org.eclipse.jface.text.link.LinkedPosition;
66 import org.eclipse.jface.text.link.LinkedPositionGroup;
67 import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
68 import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
69
70 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
71
72 import org.eclipse.jdt.core.CompletionProposal;
73 import org.eclipse.jdt.core.IJavaElement;
74 import org.eclipse.jdt.core.IJavaProject;
75 import org.eclipse.jdt.core.JavaCore;
76 import org.eclipse.jdt.core.JavaModelException;
77 import org.eclipse.jdt.core.compiler.CharOperation;
78
79 import org.eclipse.jdt.ui.PreferenceConstants;
80 import org.eclipse.jdt.ui.text.IJavaPartitions;
81 import org.eclipse.jdt.ui.text.JavaTextTools;
82 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
83 import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
84
85 import org.eclipse.jdt.internal.ui.JavaPlugin;
86
87 import org.osgi.framework.Bundle;
88
89 /**
90  *
91  * @since 3.2
92  */

93 public abstract class AbstractJavaCompletionProposal implements IJavaCompletionProposal, ICompletionProposalExtension, ICompletionProposalExtension2, ICompletionProposalExtension3, ICompletionProposalExtension5 {
94     
95     
96     /**
97      * The control creator.
98      *
99      * @since 3.3
100      */

101     private static final class ControlCreator extends AbstractReusableInformationControlCreator {
102         /*
103          * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell)
104          */

105         public IInformationControl doCreateInformationControl(Shell parent) {
106             return new BrowserInformationControl(parent, SWT.NO_TRIM | SWT.TOOL, SWT.NONE, null);
107         }
108     }
109
110     
111     /**
112      * A class to simplify tracking a reference position in a document.
113      */

114     static final class ReferenceTracker {
115     
116         /** The reference position category name. */
117         private static final String JavaDoc CATEGORY= "reference_position"; //$NON-NLS-1$
118
/** The position updater of the reference position. */
119         private final IPositionUpdater fPositionUpdater= new DefaultPositionUpdater(CATEGORY);
120         /** The reference position. */
121         private final Position fPosition= new Position(0);
122     
123         /**
124          * Called before document changes occur. It must be followed by a call to postReplace().
125          *
126          * @param document the document on which to track the reference position.
127          * @param offset the offset
128          * @throws BadLocationException if the offset describes an invalid range in this document
129          *
130          */

131         public void preReplace(IDocument document, int offset) throws BadLocationException {
132             fPosition.setOffset(offset);
133             try {
134                 document.addPositionCategory(CATEGORY);
135                 document.addPositionUpdater(fPositionUpdater);
136                 document.addPosition(CATEGORY, fPosition);
137     
138             } catch (BadPositionCategoryException e) {
139                 // should not happen
140
JavaPlugin.log(e);
141             }
142         }
143     
144         /**
145          * Called after the document changed occurred. It must be preceded by a call to preReplace().
146          *
147          * @param document the document on which to track the reference position.
148          * @return offset after the replace
149          */

150         public int postReplace(IDocument document) {
151             try {
152                 document.removePosition(CATEGORY, fPosition);
153                 document.removePositionUpdater(fPositionUpdater);
154                 document.removePositionCategory(CATEGORY);
155     
156             } catch (BadPositionCategoryException e) {
157                 // should not happen
158
JavaPlugin.log(e);
159             }
160             return fPosition.getOffset();
161         }
162     }
163
164     protected static final class ExitPolicy implements IExitPolicy {
165     
166         final char fExitCharacter;
167         private final IDocument fDocument;
168     
169         public ExitPolicy(char exitCharacter, IDocument document) {
170             fExitCharacter= exitCharacter;
171             fDocument= document;
172         }
173     
174         /*
175          * @see org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, org.eclipse.swt.events.VerifyEvent, int, int)
176          */

177         public ExitFlags doExit(LinkedModeModel environment, VerifyEvent event, int offset, int length) {
178     
179             if (event.character == fExitCharacter) {
180                 if (environment.anyPositionContains(offset))
181                     return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
182                 else
183                     return new ExitFlags(ILinkedModeListener.UPDATE_CARET, true);
184             }
185     
186             switch (event.character) {
187                 case ';':
188                     return new ExitFlags(ILinkedModeListener.NONE, true);
189                 case SWT.CR:
190                     // when entering an anonymous class as a parameter, we don't want
191
// to jump after the parenthesis when return is pressed
192
if (offset > 0) {
193                         try {
194                             if (fDocument.getChar(offset - 1) == '{')
195                                 return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
196                         } catch (BadLocationException e) {
197                         }
198                     }
199                     return null;
200                 default:
201                     return null;
202             }
203         }
204     
205     }
206
207     private String JavaDoc fDisplayString;
208     private String JavaDoc fReplacementString;
209     private int fReplacementOffset;
210     private int fReplacementLength;
211     private int fCursorPosition;
212     private Image fImage;
213     private IContextInformation fContextInformation;
214     private ProposalInfo fProposalInfo;
215     private char[] fTriggerCharacters;
216     private String JavaDoc fSortString;
217     private int fRelevance;
218     private boolean fIsInJavadoc;
219     
220     private StyleRange fRememberedStyleRange;
221     private boolean fToggleEating;
222     private ITextViewer fTextViewer;
223     
224     
225     /**
226      * The control creator.
227      *
228      * @since 3.2
229      */

230     private IInformationControlCreator fCreator;
231     /**
232      * The CSS used to format javadoc information.
233      * @since 3.3
234      */

235     private static String JavaDoc fgCSSStyles;
236     
237     /**
238      * The invocation context of this completion proposal. Can be <code>null</code>.
239      */

240     protected final JavaContentAssistInvocationContext fInvocationContext;
241
242     protected AbstractJavaCompletionProposal() {
243         fInvocationContext= null;
244     }
245     
246     protected AbstractJavaCompletionProposal(JavaContentAssistInvocationContext context) {
247         fInvocationContext= context;
248     }
249
250     /*
251      * @see ICompletionProposalExtension#getTriggerCharacters()
252      */

253     public char[] getTriggerCharacters() {
254         return fTriggerCharacters;
255     }
256     
257     /**
258      * Sets the trigger characters.
259      *
260      * @param triggerCharacters The set of characters which can trigger the application of this
261      * completion proposal
262      */

263     public void setTriggerCharacters(char[] triggerCharacters) {
264         fTriggerCharacters= triggerCharacters;
265     }
266
267     /**
268      * Sets the proposal info.
269      *
270      * @param proposalInfo The additional information associated with this proposal or
271      * <code>null</code>
272      */

273     public void setProposalInfo(ProposalInfo proposalInfo) {
274         fProposalInfo= proposalInfo;
275     }
276
277     /**
278      * Returns the additional proposal info, or <code>null</code> if none exists.
279      *
280      * @return the additional proposal info, or <code>null</code> if none exists
281      */

282     protected ProposalInfo getProposalInfo() {
283         return fProposalInfo;
284     }
285
286     /**
287      * Sets the cursor position relative to the insertion offset. By default this is the length of
288      * the completion string (Cursor positioned after the completion)
289      *
290      * @param cursorPosition The cursorPosition to set
291      */

292     public void setCursorPosition(int cursorPosition) {
293         Assert.isTrue(cursorPosition >= 0);
294         fCursorPosition= cursorPosition;
295     }
296     
297     protected int getCursorPosition() {
298         return fCursorPosition;
299     }
300
301     /*
302      * @see ICompletionProposal#apply
303      */

304     public final void apply(IDocument document) {
305         // not used any longer
306
apply(document, (char) 0, getReplacementOffset() + getReplacementLength());
307     }
308     
309     /*
310      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#apply(org.eclipse.jface.text.IDocument, char, int)
311      */

312     public void apply(IDocument document, char trigger, int offset) {
313         
314         if (isSupportingRequiredProposals()) {
315             CompletionProposal coreProposal= ((MemberProposalInfo)getProposalInfo()).fProposal;
316             CompletionProposal[] requiredProposals= coreProposal.getRequiredProposals();
317             for (int i= 0; requiredProposals != null && i < requiredProposals.length; i++) {
318                 int oldLen= document.getLength();
319                 if (requiredProposals[i].getKind() == CompletionProposal.TYPE_REF) {
320                     LazyJavaCompletionProposal proposal= new LazyJavaTypeCompletionProposal(requiredProposals[i], fInvocationContext);
321                     proposal.apply(document);
322                     setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
323                 } else if (requiredProposals[i].getKind() == CompletionProposal.TYPE_IMPORT) {
324                     ImportCompletionProposal proposal= new ImportCompletionProposal(requiredProposals[i], fInvocationContext, coreProposal.getKind());
325                     proposal.setReplacementOffset(getReplacementOffset());
326                     proposal.apply(document);
327                     setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
328                 } else if (requiredProposals[i].getKind() == CompletionProposal.METHOD_IMPORT) {
329                     ImportCompletionProposal proposal= new ImportCompletionProposal(requiredProposals[i], fInvocationContext, coreProposal.getKind());
330                     proposal.setReplacementOffset(getReplacementOffset());
331                     proposal.apply(document);
332                     setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
333                 } else if (requiredProposals[i].getKind() == CompletionProposal.FIELD_IMPORT) {
334                     ImportCompletionProposal proposal= new ImportCompletionProposal(requiredProposals[i], fInvocationContext, coreProposal.getKind());
335                     proposal.setReplacementOffset(getReplacementOffset());
336                     proposal.apply(document);
337                     setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
338                 } else {
339                     /*
340                      * In 3.3 we only support the above required proposals, see
341                      * CompletionProposal#getRequiredProposals()
342                      */

343                      Assert.isTrue(false);
344                 }
345             }
346         }
347         
348         try {
349             // patch replacement length
350
int delta= offset - (getReplacementOffset() + getReplacementLength());
351             if (delta > 0)
352                 setReplacementLength(getReplacementLength() + delta);
353     
354             boolean isSmartTrigger= isSmartTrigger(trigger);
355     
356             String JavaDoc replacement;
357             if (isSmartTrigger || trigger == (char) 0) {
358                 replacement= getReplacementString();
359             } else {
360                 StringBuffer JavaDoc buffer= new StringBuffer JavaDoc(getReplacementString());
361     
362                 // fix for PR #5533. Assumes that no eating takes place.
363
if ((getCursorPosition() > 0 && getCursorPosition() <= buffer.length() && buffer.charAt(getCursorPosition() - 1) != trigger)) {
364                     buffer.insert(getCursorPosition(), trigger);
365                     setCursorPosition(getCursorPosition() + 1);
366                 }
367     
368                 replacement= buffer.toString();
369                 setReplacementString(replacement);
370             }
371     
372             // reference position just at the end of the document change.
373
int referenceOffset= getReplacementOffset() + getReplacementLength();
374             final ReferenceTracker referenceTracker= new ReferenceTracker();
375             referenceTracker.preReplace(document, referenceOffset);
376     
377             replace(document, getReplacementOffset(), getReplacementLength(), replacement);
378     
379             referenceOffset= referenceTracker.postReplace(document);
380             setReplacementOffset(referenceOffset - (replacement == null ? 0 : replacement.length()));
381     
382             // PR 47097
383
if (isSmartTrigger)
384                 handleSmartTrigger(document, trigger, referenceOffset);
385     
386         } catch (BadLocationException x) {
387             // ignore
388
}
389     }
390     
391
392     private boolean isSmartTrigger(char trigger) {
393         return trigger == ';' && JavaPlugin.getDefault().getCombinedPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_SEMICOLON)
394                 || trigger == '{' && JavaPlugin.getDefault().getCombinedPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_OPENING_BRACE);
395     }
396
397     private void handleSmartTrigger(IDocument document, char trigger, int referenceOffset) throws BadLocationException {
398         DocumentCommand cmd= new DocumentCommand() {
399         };
400         
401         cmd.offset= referenceOffset;
402         cmd.length= 0;
403         cmd.text= Character.toString(trigger);
404         cmd.doit= true;
405         cmd.shiftsCaret= true;
406         cmd.caretOffset= getReplacementOffset() + getCursorPosition();
407         
408         SmartSemicolonAutoEditStrategy strategy= new SmartSemicolonAutoEditStrategy(IJavaPartitions.JAVA_PARTITIONING);
409         strategy.customizeDocumentCommand(document, cmd);
410         
411         replace(document, cmd.offset, cmd.length, cmd.text);
412         setCursorPosition(cmd.caretOffset - getReplacementOffset() + cmd.text.length());
413     }
414     
415     protected final void replace(IDocument document, int offset, int length, String JavaDoc string) throws BadLocationException {
416         if (!document.get(offset, length).equals(string))
417             document.replace(offset, length, string);
418     }
419     
420     /*
421      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension1#apply(org.eclipse.jface.text.ITextViewer, char, int, int)
422      */

423     public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
424
425         IDocument document= viewer.getDocument();
426         if (fTextViewer == null)
427             fTextViewer= viewer;
428         
429         // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96059
430
// don't apply the proposal if for some reason we're not valid any longer
431
if (!isInJavadoc() && !validate(document, offset, null)) {
432             setCursorPosition(offset - getReplacementOffset());
433             if (trigger != '\0') {
434                 try {
435                     document.replace(offset, 0, String.valueOf(trigger));
436                     setCursorPosition(getCursorPosition() + 1);
437                     if (trigger == '(' && autocloseBrackets()) {
438                         document.replace(getReplacementOffset() + getCursorPosition(), 0, ")"); //$NON-NLS-1$
439
setUpLinkedMode(document, ')');
440                     }
441                 } catch (BadLocationException x) {
442                     // ignore
443
}
444             }
445             return;
446         }
447
448         // don't eat if not in preferences, XOR with modifier key 1 (Ctrl)
449
// but: if there is a selection, replace it!
450
Point selection= viewer.getSelectedRange();
451         fToggleEating= (stateMask & SWT.MOD1) != 0;
452         int newLength= selection.x + selection.y - getReplacementOffset();
453         if ((insertCompletion() ^ fToggleEating) && newLength >= 0)
454             setReplacementLength(newLength);
455
456         apply(document, trigger, offset);
457         fToggleEating= false;
458     }
459
460     /**
461      * Returns <code>true</code> if the proposal is within javadoc, <code>false</code>
462      * otherwise.
463      *
464      * @return <code>true</code> if the proposal is within javadoc, <code>false</code> otherwise
465      */

466     protected boolean isInJavadoc(){
467         return fIsInJavadoc;
468     }
469     
470     /**
471      * Sets the javadoc attribute.
472      *
473      * @param isInJavadoc <code>true</code> if the proposal is within javadoc
474      */

475     protected void setInJavadoc(boolean isInJavadoc) {
476         fIsInJavadoc= isInJavadoc;
477     }
478
479     /*
480      * @see ICompletionProposal#getSelection
481      */

482     public Point getSelection(IDocument document) {
483         return new Point(getReplacementOffset() + getCursorPosition(), 0);
484     }
485
486     /*
487      * @see ICompletionProposal#getContextInformation()
488      */

489     public IContextInformation getContextInformation() {
490         return fContextInformation;
491     }
492
493     /**
494      * Sets the context information.
495      * @param contextInformation The context information associated with this proposal
496      */

497     public void setContextInformation(IContextInformation contextInformation) {
498         fContextInformation= contextInformation;
499     }
500     
501     /*
502      * @see ICompletionProposal#getDisplayString()
503      */

504     public String JavaDoc getDisplayString() {
505         return fDisplayString;
506     }
507
508     /*
509      * @see ICompletionProposal#getAdditionalProposalInfo()
510      */

511     public String JavaDoc getAdditionalProposalInfo() {
512         Object JavaDoc info= getAdditionalProposalInfo(new NullProgressMonitor());
513         return info == null ? (String JavaDoc) info : info.toString();
514     }
515     
516     /*
517      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension5#getAdditionalProposalInfo(org.eclipse.core.runtime.IProgressMonitor)
518      */

519     public Object JavaDoc getAdditionalProposalInfo(IProgressMonitor monitor) {
520         if (getProposalInfo() != null) {
521             String JavaDoc info= getProposalInfo().getInfo(monitor);
522             if (info != null && info.length() > 0) {
523                 StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
524                 HTMLPrinter.insertPageProlog(buffer, 0, getCSSStyles());
525                 buffer.append(info);
526                 HTMLPrinter.addPageEpilog(buffer);
527                 info= buffer.toString();
528             }
529             return info;
530         }
531         return null;
532     }
533     
534     /**
535      * Returns the style information for displaying HTML (Javadoc) content.
536      *
537      * @return the CSS styles
538      * @since 3.3
539      */

540     protected String JavaDoc getCSSStyles() {
541         if (fgCSSStyles == null) {
542             Bundle bundle= Platform.getBundle(JavaPlugin.getPluginId());
543             URL JavaDoc url= bundle.getEntry("/JavadocHoverStyleSheet.css"); //$NON-NLS-1$
544
if (url != null) {
545                 try {
546                     url= FileLocator.toFileURL(url);
547                     BufferedReader JavaDoc reader= new BufferedReader JavaDoc(new InputStreamReader JavaDoc(url.openStream()));
548                     StringBuffer JavaDoc buffer= new StringBuffer JavaDoc(200);
549                     String JavaDoc line= reader.readLine();
550                     while (line != null) {
551                         buffer.append(line);
552                         buffer.append('\n');
553                         line= reader.readLine();
554                     }
555                     fgCSSStyles= buffer.toString();
556                 } catch (IOException JavaDoc ex) {
557                     JavaPlugin.log(ex);
558                 }
559             }
560         }
561         String JavaDoc css= fgCSSStyles;
562         if (css != null) {
563             FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0];
564             css= HTMLPrinter.convertTopLevelFont(css, fontData);
565         }
566         return css;
567     }
568
569     /*
570      * @see ICompletionProposalExtension#getContextInformationPosition()
571      */

572     public int getContextInformationPosition() {
573         if (getContextInformation() == null)
574             return getReplacementOffset() - 1;
575         return getReplacementOffset() + getCursorPosition();
576     }
577
578     /**
579      * Gets the replacement offset.
580      * @return Returns a int
581      */

582     public int getReplacementOffset() {
583         return fReplacementOffset;
584     }
585
586     /**
587      * Sets the replacement offset.
588      * @param replacementOffset The replacement offset to set
589      */

590     public void setReplacementOffset(int replacementOffset) {
591         Assert.isTrue(replacementOffset >= 0);
592         fReplacementOffset= replacementOffset;
593     }
594
595     /*
596      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getCompletionOffset()
597      */

598     public int getPrefixCompletionStart(IDocument document, int completionOffset) {
599         return getReplacementOffset();
600     }
601
602     /**
603      * Gets the replacement length.
604      * @return Returns a int
605      */

606     public int getReplacementLength() {
607         return fReplacementLength;
608     }
609
610     /**
611      * Sets the replacement length.
612      * @param replacementLength The replacementLength to set
613      */

614     public void setReplacementLength(int replacementLength) {
615         Assert.isTrue(replacementLength >= 0);
616         fReplacementLength= replacementLength;
617     }
618
619     /**
620      * Gets the replacement string.
621      * @return Returns a String
622      */

623     public String JavaDoc getReplacementString() {
624         return fReplacementString;
625     }
626
627     /**
628      * Sets the replacement string.
629      * @param replacementString The replacement string to set
630      */

631     public void setReplacementString(String JavaDoc replacementString) {
632         Assert.isNotNull(replacementString);
633         fReplacementString= replacementString;
634     }
635
636     /*
637      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementText()
638      */

639     public CharSequence JavaDoc getPrefixCompletionText(IDocument document, int completionOffset) {
640         if (!isCamelCaseMatching())
641             return getReplacementString();
642         
643         String JavaDoc prefix= getPrefix(document, completionOffset);
644         return getCamelCaseCompound(prefix, getReplacementString());
645     }
646
647     /*
648      * @see ICompletionProposal#getImage()
649      */

650     public Image getImage() {
651         return fImage;
652     }
653
654     /**
655      * Sets the image.
656      * @param image The image to set
657      */

658     public void setImage(Image image) {
659         fImage= image;
660     }
661
662     /*
663      * @see ICompletionProposalExtension#isValidFor(IDocument, int)
664      */

665     public boolean isValidFor(IDocument document, int offset) {
666         return validate(document, offset, null);
667     }
668
669     /*
670      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument, int, org.eclipse.jface.text.DocumentEvent)
671      */

672     public boolean validate(IDocument document, int offset, DocumentEvent event) {
673
674         if (offset < getReplacementOffset())
675             return false;
676         
677         boolean validated= isValidPrefix(getPrefix(document, offset));
678
679         if (validated && event != null) {
680             // adapt replacement range to document change
681
int delta= (event.fText == null ? 0 : event.fText.length()) - event.fLength;
682             final int newLength= Math.max(getReplacementLength() + delta, 0);
683             setReplacementLength(newLength);
684         }
685
686         return validated;
687     }
688
689     /**
690      * Checks whether <code>prefix</code> is a valid prefix for this proposal. Usually, while code
691      * completion is in progress, the user types and edits the prefix in the document in order to
692      * filter the proposal list. From {@link #validate(IDocument, int, DocumentEvent) }, the
693      * current prefix in the document is extracted and this method is called to find out whether the
694      * proposal is still valid.
695      * <p>
696      * The default implementation checks if <code>prefix</code> is a prefix of the proposal's
697      * {@link #getDisplayString() display string} using the {@link #isPrefix(String, String) }
698      * method.
699      * </p>
700      *
701      * @param prefix the current prefix in the document
702      * @return <code>true</code> if <code>prefix</code> is a valid prefix of this proposal
703      */

704     protected boolean isValidPrefix(String JavaDoc prefix) {
705         /*
706          * See http://dev.eclipse.org/bugs/show_bug.cgi?id=17667
707          * why we do not use the replacement string.
708          * String word= fReplacementString;
709          */

710         return isPrefix(prefix, getDisplayString());
711     }
712
713     /**
714      * Gets the proposal's relevance.
715      * @return Returns a int
716      */

717     public int getRelevance() {
718         return fRelevance;
719     }
720
721     /**
722      * Sets the proposal's relevance.
723      * @param relevance The relevance to set
724      */

725     public void setRelevance(int relevance) {
726         fRelevance= relevance;
727     }
728
729     /**
730      * Returns the text in <code>document</code> from {@link #getReplacementOffset()} to
731      * <code>offset</code>. Returns the empty string if <code>offset</code> is before the
732      * replacement offset or if an exception occurs when accessing the document.
733      *
734      * @param document the document
735      * @param offset the offset
736      * @return the prefix
737      * @since 3.2
738      */

739     protected String JavaDoc getPrefix(IDocument document, int offset) {
740         try {
741             int length= offset - getReplacementOffset();
742             if (length > 0)
743                 return document.get(getReplacementOffset(), length);
744         } catch (BadLocationException x) {
745         }
746         return ""; //$NON-NLS-1$
747
}
748     
749     /**
750      * Case insensitive comparison of <code>prefix</code> with the start of <code>string</code>.
751      *
752      * @param prefix the prefix
753      * @param string the string to look for the prefix
754      * @return <code>true</code> if the string begins with the given prefix and
755      * <code>false</code> if <code>prefix</code> is longer than <code>string</code>
756      * or the string doesn't start with the given prefix
757      * @since 3.2
758      */

759     protected boolean isPrefix(String JavaDoc prefix, String JavaDoc string) {
760         if (prefix == null || string ==null || prefix.length() > string.length())
761             return false;
762         String JavaDoc start= string.substring(0, prefix.length());
763         return start.equalsIgnoreCase(prefix) || isCamelCaseMatching() && CharOperation.camelCaseMatch(prefix.toCharArray(), string.toCharArray());
764     }
765
766     /**
767      * Matches <code>prefix</code> against <code>string</code> and replaces the matched region
768      * by prefix. Case is preserved as much as possible. This method returns <code>string</code> if camel case completion
769      * is disabled. Examples when camel case completion is enabled:
770      * <ul>
771      * <li>getCamelCompound("NuPo", "NullPointerException") -> "NuPointerException"</li>
772      * <li>getCamelCompound("NuPoE", "NullPointerException") -> "NuPoException"</li>
773      * <li>getCamelCompound("hasCod", "hashCode") -> "hasCode"</li>
774      * </ul>
775      *
776      * @param prefix the prefix to match against
777      * @param string the string to match
778      * @return a compound of prefix and any postfix taken from <code>string</code>
779      * @since 3.2
780      */

781     protected final String JavaDoc getCamelCaseCompound(String JavaDoc prefix, String JavaDoc string) {
782         if (prefix.length() > string.length())
783             return string;
784
785         // a normal prefix - no camel case logic at all
786
String JavaDoc start= string.substring(0, prefix.length());
787         if (start.equalsIgnoreCase(prefix))
788             return string;
789
790         final char[] patternChars= prefix.toCharArray();
791         final char[] stringChars= string.toCharArray();
792
793         for (int i= 1; i <= stringChars.length; i++)
794             if (CharOperation.camelCaseMatch(patternChars, 0, patternChars.length, stringChars, 0, i))
795                 return prefix + string.substring(i);
796
797         // Not a camel case match at all.
798
// This should not happen -> stay with the default behavior
799
return string;
800     }
801
802     /**
803      * Returns true if camel case matching is enabled.
804      *
805      * @return <code>true</code> if camel case matching is enabled
806      * @since 3.2
807      */

808     protected boolean isCamelCaseMatching() {
809         IJavaProject project= getProject();
810         String JavaDoc value;
811         if (project == null)
812             value= JavaCore.getOption(JavaCore.CODEASSIST_CAMEL_CASE_MATCH);
813         else
814             value= project.getOption(JavaCore.CODEASSIST_CAMEL_CASE_MATCH, true);
815         
816         return JavaCore.ENABLED.equals(value);
817     }
818     
819     private IJavaProject getProject() {
820         return null;
821     }
822
823
824     private static boolean insertCompletion() {
825         IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
826         return preference.getBoolean(PreferenceConstants.CODEASSIST_INSERT_COMPLETION);
827     }
828
829     private static Color getForegroundColor(StyledText text) {
830
831         IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
832         RGB rgb= PreferenceConverter.getColor(preference, PreferenceConstants.CODEASSIST_REPLACEMENT_FOREGROUND);
833         JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
834         return textTools.getColorManager().getColor(rgb);
835     }
836
837     private static Color getBackgroundColor(StyledText text) {
838
839         IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
840         RGB rgb= PreferenceConverter.getColor(preference, PreferenceConstants.CODEASSIST_REPLACEMENT_BACKGROUND);
841         JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
842         return textTools.getColorManager().getColor(rgb);
843     }
844
845     private void repairPresentation(ITextViewer viewer) {
846         if (fRememberedStyleRange != null) {
847              if (viewer instanceof ITextViewerExtension2) {
848                 // attempts to reduce the redraw area
849
ITextViewerExtension2 viewer2= (ITextViewerExtension2) viewer;
850
851                 if (viewer instanceof ITextViewerExtension5) {
852
853                     ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
854                     IRegion modelRange= extension.widgetRange2ModelRange(new Region(fRememberedStyleRange.start, fRememberedStyleRange.length));
855                     if (modelRange != null)
856                         viewer2.invalidateTextPresentation(modelRange.getOffset(), modelRange.getLength());
857
858                 } else {
859                     viewer2.invalidateTextPresentation(fRememberedStyleRange.start + viewer.getVisibleRegion().getOffset(), fRememberedStyleRange.length);
860                 }
861
862             } else
863                 viewer.invalidateTextPresentation();
864         }
865     }
866
867     private void updateStyle(ITextViewer viewer) {
868
869         StyledText text= viewer.getTextWidget();
870         if (text == null || text.isDisposed())
871             return;
872
873         int widgetCaret= text.getCaretOffset();
874
875         int modelCaret= 0;
876         if (viewer instanceof ITextViewerExtension5) {
877             ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
878             modelCaret= extension.widgetOffset2ModelOffset(widgetCaret);
879         } else {
880             IRegion visibleRegion= viewer.getVisibleRegion();
881             modelCaret= widgetCaret + visibleRegion.getOffset();
882         }
883
884         if (modelCaret >= getReplacementOffset() + getReplacementLength()) {
885             repairPresentation(viewer);
886             return;
887         }
888
889         int offset= widgetCaret;
890         int length= getReplacementOffset() + getReplacementLength() - modelCaret;
891
892         Color foreground= getForegroundColor(text);
893         Color background= getBackgroundColor(text);
894
895         StyleRange range= text.getStyleRangeAtOffset(offset);
896         int fontStyle= range != null ? range.fontStyle : SWT.NORMAL;
897
898         repairPresentation(viewer);
899         fRememberedStyleRange= new StyleRange(offset, length, foreground, background, fontStyle);
900         if (range != null) {
901             fRememberedStyleRange.strikeout= range.strikeout;
902             fRememberedStyleRange.underline= range.underline;
903         }
904
905         // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
906
try {
907             text.setStyleRange(fRememberedStyleRange);
908         } catch (IllegalArgumentException JavaDoc x) {
909             // catching exception as offset + length might be outside of the text widget
910
fRememberedStyleRange= null;
911         }
912     }
913
914     /*
915      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(ITextViewer, boolean)
916      */

917     public void selected(ITextViewer viewer, boolean smartToggle) {
918         if (!insertCompletion() ^ smartToggle)
919             updateStyle(viewer);
920         else {
921             repairPresentation(viewer);
922             fRememberedStyleRange= null;
923         }
924     }
925
926     /*
927      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(ITextViewer)
928      */

929     public void unselected(ITextViewer viewer) {
930         repairPresentation(viewer);
931         fRememberedStyleRange= null;
932     }
933
934     /*
935      * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
936      */

937     public IInformationControlCreator getInformationControlCreator() {
938         Shell shell= JavaPlugin.getActiveWorkbenchShell();
939         if (shell == null || !BrowserInformationControl.isAvailable(shell))
940             return null;
941         
942         if (fCreator == null) {
943             fCreator= new ControlCreator();
944         }
945         return fCreator;
946     }
947
948     public String JavaDoc getSortString() {
949         return fSortString;
950     }
951
952     protected void setSortString(String JavaDoc string) {
953         fSortString= string;
954     }
955
956     protected ITextViewer getTextViewer() {
957         return fTextViewer;
958     }
959
960     protected boolean isToggleEating() {
961         return fToggleEating;
962     }
963
964     /**
965      * Sets up a simple linked mode at {@link #getCursorPosition()} and an exit policy that will
966      * exit the mode when <code>closingCharacter</code> is typed and an exit position at
967      * <code>getCursorPosition() + 1</code>.
968      *
969      * @param document the document
970      * @param closingCharacter the exit character
971      */

972     protected void setUpLinkedMode(IDocument document, char closingCharacter) {
973         if (getTextViewer() != null && autocloseBrackets()) {
974             int offset= getReplacementOffset() + getCursorPosition();
975             int exit= getReplacementOffset() + getReplacementString().length();
976             try {
977                 LinkedPositionGroup group= new LinkedPositionGroup();
978                 group.addPosition(new LinkedPosition(document, offset, 0, LinkedPositionGroup.NO_STOP));
979                 
980                 LinkedModeModel model= new LinkedModeModel();
981                 model.addGroup(group);
982                 model.forceInstall();
983                 
984                 LinkedModeUI ui= new EditorLinkedModeUI(model, getTextViewer());
985                 ui.setSimpleMode(true);
986                 ui.setExitPolicy(new ExitPolicy(closingCharacter, document));
987                 ui.setExitPosition(getTextViewer(), exit, 0, Integer.MAX_VALUE);
988                 ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
989                 ui.enter();
990             } catch (BadLocationException x) {
991                 JavaPlugin.log(x);
992             }
993         }
994     }
995     
996     protected boolean autocloseBrackets() {
997         IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
998         return preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS);
999     }
1000
1001    protected void setDisplayString(String JavaDoc string) {
1002        fDisplayString= string;
1003    }
1004    
1005    /*
1006     * @see java.lang.Object#toString()
1007     */

1008    public String JavaDoc toString() {
1009        return getDisplayString();
1010    }
1011    
1012    /**
1013     * Returns the java element proposed by the receiver, possibly <code>null</code>.
1014     *
1015     * @return the java element proposed by the receiver, possibly <code>null</code>
1016     */

1017    public IJavaElement getJavaElement() {
1018        if (getProposalInfo() != null)
1019            try {
1020                return getProposalInfo().getJavaElement();
1021            } catch (JavaModelException x) {
1022                JavaPlugin.log(x);
1023            }
1024        return null;
1025    }
1026    
1027    /**
1028     * Tells whether required proposals are supported by this proposal.
1029     *
1030     * @return <code>true</code> if required proposals are supported by this proposal
1031     * @see CompletionProposal#getRequiredProposals()
1032     * @since 3.3
1033     */

1034    protected boolean isSupportingRequiredProposals() {
1035        if (fInvocationContext == null)
1036            return false;
1037        
1038        ProposalInfo proposalInfo= getProposalInfo();
1039        if (!(proposalInfo instanceof MemberProposalInfo))
1040            return false;
1041        
1042        CompletionProposal proposal= ((MemberProposalInfo)proposalInfo).fProposal;
1043        return proposal != null && (proposal.getKind() == CompletionProposal.METHOD_REF || proposal.getKind() == CompletionProposal.FIELD_REF);
1044    }
1045    
1046}
1047
Popular Tags