KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > javaeditor > CompilationUnitEditor


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
12 package org.eclipse.jdt.internal.ui.javaeditor;
13
14
15 import java.util.HashMap JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.Stack JavaDoc;
18
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.core.runtime.ListenerList;
23 import org.eclipse.core.runtime.Platform;
24
25 import org.eclipse.core.resources.IFile;
26
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.custom.StyledText;
29 import org.eclipse.swt.custom.VerifyKeyListener;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.events.VerifyEvent;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.layout.GridData;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Link;
38 import org.eclipse.swt.widgets.Shell;
39
40 import org.eclipse.jface.action.IAction;
41 import org.eclipse.jface.action.IMenuManager;
42 import org.eclipse.jface.dialogs.ErrorDialog;
43 import org.eclipse.jface.dialogs.MessageDialog;
44 import org.eclipse.jface.preference.IPreferenceStore;
45 import org.eclipse.jface.util.PropertyChangeEvent;
46
47 import org.eclipse.jface.text.BadLocationException;
48 import org.eclipse.jface.text.BadPositionCategoryException;
49 import org.eclipse.jface.text.DefaultLineTracker;
50 import org.eclipse.jface.text.DocumentCommand;
51 import org.eclipse.jface.text.DocumentEvent;
52 import org.eclipse.jface.text.IDocument;
53 import org.eclipse.jface.text.IDocumentExtension;
54 import org.eclipse.jface.text.IDocumentListener;
55 import org.eclipse.jface.text.IPositionUpdater;
56 import org.eclipse.jface.text.IRegion;
57 import org.eclipse.jface.text.ITextOperationTarget;
58 import org.eclipse.jface.text.ITextViewerExtension;
59 import org.eclipse.jface.text.ITextViewerExtension7;
60 import org.eclipse.jface.text.ITypedRegion;
61 import org.eclipse.jface.text.IWidgetTokenKeeper;
62 import org.eclipse.jface.text.Position;
63 import org.eclipse.jface.text.TabsToSpacesConverter;
64 import org.eclipse.jface.text.TextUtilities;
65 import org.eclipse.jface.text.contentassist.ContentAssistant;
66 import org.eclipse.jface.text.contentassist.IContentAssistant;
67 import org.eclipse.jface.text.formatter.FormattingContextProperties;
68 import org.eclipse.jface.text.formatter.IFormattingContext;
69 import org.eclipse.jface.text.link.ILinkedModeListener;
70 import org.eclipse.jface.text.link.LinkedModeModel;
71 import org.eclipse.jface.text.link.LinkedModeUI;
72 import org.eclipse.jface.text.link.LinkedPosition;
73 import org.eclipse.jface.text.link.LinkedPositionGroup;
74 import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
75 import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
76 import org.eclipse.jface.text.source.IOverviewRuler;
77 import org.eclipse.jface.text.source.ISourceViewer;
78 import org.eclipse.jface.text.source.IVerticalRuler;
79 import org.eclipse.jface.text.source.SourceViewerConfiguration;
80
81 import org.eclipse.ui.IEditorInput;
82 import org.eclipse.ui.IEditorPart;
83 import org.eclipse.ui.IFileEditorInput;
84 import org.eclipse.ui.IWorkbenchPage;
85 import org.eclipse.ui.IWorkbenchWindow;
86 import org.eclipse.ui.PlatformUI;
87 import org.eclipse.ui.actions.ActionContext;
88 import org.eclipse.ui.actions.ActionGroup;
89 import org.eclipse.ui.dialogs.PreferencesUtil;
90 import org.eclipse.ui.texteditor.ContentAssistAction;
91 import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
92 import org.eclipse.ui.texteditor.IDocumentProvider;
93 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
94 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
95 import org.eclipse.ui.texteditor.ResourceAction;
96 import org.eclipse.ui.texteditor.TextOperationAction;
97 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
98
99 import org.eclipse.jdt.core.ICompilationUnit;
100 import org.eclipse.jdt.core.IJavaElement;
101 import org.eclipse.jdt.core.IJavaProject;
102 import org.eclipse.jdt.core.IMember;
103 import org.eclipse.jdt.core.ISourceRange;
104 import org.eclipse.jdt.core.ISourceReference;
105 import org.eclipse.jdt.core.JavaCore;
106 import org.eclipse.jdt.core.JavaModelException;
107 import org.eclipse.jdt.core.dom.CompilationUnit;
108 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
109
110 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
111
112 import org.eclipse.jdt.ui.IWorkingCopyManager;
113 import org.eclipse.jdt.ui.JavaUI;
114 import org.eclipse.jdt.ui.PreferenceConstants;
115 import org.eclipse.jdt.ui.actions.GenerateActionGroup;
116 import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
117 import org.eclipse.jdt.ui.actions.RefactorActionGroup;
118 import org.eclipse.jdt.ui.text.IJavaPartitions;
119
120 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
121 import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
122 import org.eclipse.jdt.internal.ui.JavaPlugin;
123 import org.eclipse.jdt.internal.ui.actions.AddBlockCommentAction;
124 import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
125 import org.eclipse.jdt.internal.ui.actions.IndentAction;
126 import org.eclipse.jdt.internal.ui.actions.RemoveBlockCommentAction;
127 import org.eclipse.jdt.internal.ui.actions.SurroundWithActionGroup;
128 import org.eclipse.jdt.internal.ui.compare.LocalHistoryActionGroup;
129 import org.eclipse.jdt.internal.ui.text.ContentAssistPreference;
130 import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
131 import org.eclipse.jdt.internal.ui.text.SmartBackspaceManager;
132 import org.eclipse.jdt.internal.ui.text.Symbols;
133 import org.eclipse.jdt.internal.ui.text.comment.CommentFormattingContext;
134 import org.eclipse.jdt.internal.ui.text.correction.CorrectionCommandInstaller;
135 import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener;
136
137
138
139 /**
140  * Java specific text editor.
141  */

142 public class CompilationUnitEditor extends JavaEditor implements IJavaReconcilingListener {
143     private static final boolean CODE_ASSIST_DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jdt.ui/debug/ResultCollector")); //$NON-NLS-1$//$NON-NLS-2$
144

145     /**
146      * Text operation code for requesting common prefix completion.
147      */

148     public static final int CONTENTASSIST_COMPLETE_PREFIX= 60;
149
150
151     interface ITextConverter {
152         void customizeDocumentCommand(IDocument document, DocumentCommand command);
153     }
154
155     class AdaptedSourceViewer extends JavaSourceViewer {
156
157         public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles, IPreferenceStore store) {
158             super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store);
159         }
160
161         public IContentAssistant getContentAssistant() {
162             return fContentAssistant;
163         }
164
165         /*
166          * @see ITextOperationTarget#doOperation(int)
167          */

168         public void doOperation(int operation) {
169
170             if (getTextWidget() == null)
171                 return;
172
173             switch (operation) {
174                 case CONTENTASSIST_PROPOSALS:
175                     long time= CODE_ASSIST_DEBUG ? System.currentTimeMillis() : 0;
176                     String JavaDoc msg= fContentAssistant.showPossibleCompletions();
177                     if (CODE_ASSIST_DEBUG) {
178                         long delta= System.currentTimeMillis() - time;
179                         System.err.println("Code Assist (total): " + delta); //$NON-NLS-1$
180
}
181                     setStatusLineErrorMessage(msg);
182                     return;
183                 case QUICK_ASSIST:
184                     /*
185                      * XXX: We can get rid of this once the SourceViewer has a way to update the status line
186                      * https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787
187                      */

188                     msg= fQuickAssistAssistant.showPossibleQuickAssists();
189                     setStatusLineErrorMessage(msg);
190                     return;
191             }
192
193             super.doOperation(operation);
194         }
195
196         /*
197          * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
198          */

199         public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
200             if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed())
201                 return false;
202             return super.requestWidgetToken(requester);
203         }
204
205         /*
206          * @see IWidgetTokenOwnerExtension#requestWidgetToken(IWidgetTokenKeeper, int)
207          * @since 3.0
208          */

209         public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
210             if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed())
211                 return false;
212             return super.requestWidgetToken(requester, priority);
213         }
214
215         /*
216          * @see org.eclipse.jface.text.source.SourceViewer#createFormattingContext()
217          * @since 3.0
218          */

219         public IFormattingContext createFormattingContext() {
220             IFormattingContext context= new CommentFormattingContext();
221
222             Map JavaDoc preferences;
223             IJavaElement inputJavaElement= getInputJavaElement();
224             IJavaProject javaProject= inputJavaElement != null ? inputJavaElement.getJavaProject() : null;
225             if (javaProject == null)
226                 preferences= new HashMap JavaDoc(JavaCore.getOptions());
227             else
228                 preferences= new HashMap JavaDoc(javaProject.getOptions(true));
229
230             context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, preferences);
231
232             return context;
233         }
234     }
235
236     
237     private class ExitPolicy implements IExitPolicy {
238
239         final char fExitCharacter;
240         final char fEscapeCharacter;
241         final Stack JavaDoc fStack;
242         final int fSize;
243
244         public ExitPolicy(char exitCharacter, char escapeCharacter, Stack JavaDoc stack) {
245             fExitCharacter= exitCharacter;
246             fEscapeCharacter= escapeCharacter;
247             fStack= stack;
248             fSize= fStack.size();
249         }
250
251         /*
252          * @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)
253          */

254         public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) {
255
256             if (fSize == fStack.size() && !isMasked(offset)) {
257                 if (event.character == fExitCharacter) {
258                     BracketLevel level= (BracketLevel) fStack.peek();
259                     if (level.fFirstPosition.offset > offset || level.fSecondPosition.offset < offset)
260                         return null;
261                     if (level.fSecondPosition.offset == offset && length == 0)
262                         // don't enter the character if if its the closing peer
263
return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
264                 }
265                 // when entering an anonymous class between the parenthesis', we don't want
266
// to jump after the closing parenthesis when return is pressed
267
if (event.character == SWT.CR && offset > 0) {
268                     IDocument document= getSourceViewer().getDocument();
269                     try {
270                         if (document.getChar(offset - 1) == '{')
271                             return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
272                     } catch (BadLocationException e) {
273                     }
274                 }
275             }
276             return null;
277         }
278
279         private boolean isMasked(int offset) {
280             IDocument document= getSourceViewer().getDocument();
281             try {
282                 return fEscapeCharacter == document.getChar(offset - 1);
283             } catch (BadLocationException e) {
284             }
285             return false;
286         }
287     }
288
289     private static class BracketLevel {
290         int fOffset;
291         int fLength;
292         LinkedModeUI fUI;
293         Position fFirstPosition;
294         Position fSecondPosition;
295     }
296
297     /**
298      * Position updater that takes any changes at the borders of a position to not belong to the position.
299      *
300      * @since 3.0
301      */

302     private static class ExclusivePositionUpdater implements IPositionUpdater {
303
304         /** The position category. */
305         private final String JavaDoc fCategory;
306
307         /**
308          * Creates a new updater for the given <code>category</code>.
309          *
310          * @param category the new category.
311          */

312         public ExclusivePositionUpdater(String JavaDoc category) {
313             fCategory= category;
314         }
315
316         /*
317          * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
318          */

319         public void update(DocumentEvent event) {
320
321             int eventOffset= event.getOffset();
322             int eventOldLength= event.getLength();
323             int eventNewLength= event.getText() == null ? 0 : event.getText().length();
324             int deltaLength= eventNewLength - eventOldLength;
325
326             try {
327                 Position[] positions= event.getDocument().getPositions(fCategory);
328
329                 for (int i= 0; i != positions.length; i++) {
330
331                     Position position= positions[i];
332
333                     if (position.isDeleted())
334                         continue;
335
336                     int offset= position.getOffset();
337                     int length= position.getLength();
338                     int end= offset + length;
339
340                     if (offset >= eventOffset + eventOldLength)
341                         // position comes
342
// after change - shift
343
position.setOffset(offset + deltaLength);
344                     else if (end <= eventOffset) {
345                         // position comes way before change -
346
// leave alone
347
} else if (offset <= eventOffset && end >= eventOffset + eventOldLength) {
348                         // event completely internal to the position - adjust length
349
position.setLength(length + deltaLength);
350                     } else if (offset < eventOffset) {
351                         // event extends over end of position - adjust length
352
int newEnd= eventOffset;
353                         position.setLength(newEnd - offset);
354                     } else if (end > eventOffset + eventOldLength) {
355                         // event extends from before position into it - adjust offset
356
// and length
357
// offset becomes end of event, length adjusted accordingly
358
int newOffset= eventOffset + eventNewLength;
359                         position.setOffset(newOffset);
360                         position.setLength(end - newOffset);
361                     } else {
362                         // event consumes the position - delete it
363
position.delete();
364                     }
365                 }
366             } catch (BadPositionCategoryException e) {
367                 // ignore and return
368
}
369         }
370
371         /**
372          * Returns the position category.
373          *
374          * @return the position category
375          */

376         public String JavaDoc getCategory() {
377             return fCategory;
378         }
379
380     }
381
382     private class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
383
384         private boolean fCloseBrackets= true;
385         private boolean fCloseStrings= true;
386         private boolean fCloseAngularBrackets= true;
387         private final String JavaDoc CATEGORY= toString();
388         private IPositionUpdater fUpdater= new ExclusivePositionUpdater(CATEGORY);
389         private Stack JavaDoc fBracketLevelStack= new Stack JavaDoc();
390
391         public void setCloseBracketsEnabled(boolean enabled) {
392             fCloseBrackets= enabled;
393         }
394
395         public void setCloseStringsEnabled(boolean enabled) {
396             fCloseStrings= enabled;
397         }
398
399         public void setCloseAngularBracketsEnabled(boolean enabled) {
400             fCloseAngularBrackets= enabled;
401         }
402
403         private boolean isAngularIntroducer(String JavaDoc identifier) {
404             return identifier.length() > 0
405                     && (Character.isUpperCase(identifier.charAt(0))
406                             || identifier.startsWith("final") //$NON-NLS-1$
407
|| identifier.startsWith("public") //$NON-NLS-1$
408
|| identifier.startsWith("public") //$NON-NLS-1$
409
|| identifier.startsWith("protected") //$NON-NLS-1$
410
|| identifier.startsWith("private")); //$NON-NLS-1$
411
}
412
413         /*
414          * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
415          */

416         public void verifyKey(VerifyEvent event) {
417
418             // early pruning to slow down normal typing as little as possible
419
if (!event.doit || getInsertMode() != SMART_INSERT)
420                 return;
421             switch (event.character) {
422                 case '(':
423                 case '<':
424                 case '[':
425                 case '\'':
426                 case '\"':
427                     break;
428                 default:
429                     return;
430             }
431
432             final ISourceViewer sourceViewer= getSourceViewer();
433             IDocument document= sourceViewer.getDocument();
434
435             final Point selection= sourceViewer.getSelectedRange();
436             final int offset= selection.x;
437             final int length= selection.y;
438
439             try {
440                 IRegion startLine= document.getLineInformationOfOffset(offset);
441                 IRegion endLine= document.getLineInformationOfOffset(offset + length);
442
443                 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
444                 int nextToken= scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength());
445                 String JavaDoc next= nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim();
446                 int prevToken= scanner.previousToken(offset - 1, startLine.getOffset());
447                 int prevTokenOffset= scanner.getPosition() + 1;
448                 String JavaDoc previous= prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim();
449
450                 switch (event.character) {
451                     case '(':
452                         if (!fCloseBrackets
453                                 || nextToken == Symbols.TokenLPAREN
454                                 || nextToken == Symbols.TokenIDENT
455                                 || next != null && next.length() > 1)
456                             return;
457                         break;
458
459                     case '<':
460                         if (!(fCloseAngularBrackets && fCloseBrackets)
461                                 || nextToken == Symbols.TokenLESSTHAN
462                                 || prevToken != Symbols.TokenLBRACE
463                                         && prevToken != Symbols.TokenRBRACE
464                                         && prevToken != Symbols.TokenSEMICOLON
465                                         && prevToken != Symbols.TokenSYNCHRONIZED
466                                         && prevToken != Symbols.TokenSTATIC
467                                         && (prevToken != Symbols.TokenIDENT || !isAngularIntroducer(previous))
468                                         && prevToken != Symbols.TokenEOF)
469                             return;
470                         break;
471
472                     case '[':
473                         if (!fCloseBrackets
474                                 || nextToken == Symbols.TokenIDENT
475                                 || next != null && next.length() > 1)
476                             return;
477                         break;
478
479                     case '\'':
480                     case '"':
481                         if (!fCloseStrings
482                                 || nextToken == Symbols.TokenIDENT
483                                 || prevToken == Symbols.TokenIDENT
484                                 || next != null && next.length() > 1
485                                 || previous != null && previous.length() > 1)
486                             return;
487                         break;
488
489                     default:
490                         return;
491                 }
492
493                 ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, true);
494                 if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()))
495                     return;
496
497                 if (!validateEditorInputState())
498                     return;
499
500                 final char character= event.character;
501                 final char closingCharacter= getPeerCharacter(character);
502                 final StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
503                 buffer.append(character);
504                 buffer.append(closingCharacter);
505
506                 document.replace(offset, length, buffer.toString());
507
508
509                 BracketLevel level= new BracketLevel();
510                 fBracketLevelStack.push(level);
511
512                 LinkedPositionGroup group= new LinkedPositionGroup();
513                 group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP));
514
515                 LinkedModeModel model= new LinkedModeModel();
516                 model.addLinkingListener(this);
517                 model.addGroup(group);
518                 model.forceInstall();
519
520                 level.fOffset= offset;
521                 level.fLength= 2;
522
523                 // set up position tracking for our magic peers
524
if (fBracketLevelStack.size() == 1) {
525                     document.addPositionCategory(CATEGORY);
526                     document.addPositionUpdater(fUpdater);
527                 }
528                 level.fFirstPosition= new Position(offset, 1);
529                 level.fSecondPosition= new Position(offset + 1, 1);
530                 document.addPosition(CATEGORY, level.fFirstPosition);
531                 document.addPosition(CATEGORY, level.fSecondPosition);
532
533                 level.fUI= new EditorLinkedModeUI(model, sourceViewer);
534                 level.fUI.setSimpleMode(true);
535                 level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
536                 level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
537                 level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
538                 level.fUI.enter();
539
540
541                 IRegion newSelection= level.fUI.getSelectedRegion();
542                 sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength());
543
544                 event.doit= false;
545
546             } catch (BadLocationException e) {
547                 JavaPlugin.log(e);
548             } catch (BadPositionCategoryException e) {
549                 JavaPlugin.log(e);
550             }
551         }
552
553         /*
554          * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int)
555          */

556         public void left(LinkedModeModel environment, int flags) {
557
558             final BracketLevel level= (BracketLevel) fBracketLevelStack.pop();
559
560             if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION)
561                 return;
562
563             // remove brackets
564
final ISourceViewer sourceViewer= getSourceViewer();
565             final IDocument document= sourceViewer.getDocument();
566             if (document instanceof IDocumentExtension) {
567                 IDocumentExtension extension= (IDocumentExtension) document;
568                 extension.registerPostNotificationReplace(null, new IDocumentExtension.IReplace() {
569
570                     public void perform(IDocument d, IDocumentListener owner) {
571                         if ((level.fFirstPosition.isDeleted || level.fFirstPosition.length == 0)
572                                 && !level.fSecondPosition.isDeleted
573                                 && level.fSecondPosition.offset == level.fFirstPosition.offset)
574                         {
575                             try {
576                                 document.replace(level.fSecondPosition.offset,
577                                                  level.fSecondPosition.length,
578                                                  ""); //$NON-NLS-1$
579
} catch (BadLocationException e) {
580                                 JavaPlugin.log(e);
581                             }
582                         }
583
584                         if (fBracketLevelStack.size() == 0) {
585                             document.removePositionUpdater(fUpdater);
586                             try {
587                                 document.removePositionCategory(CATEGORY);
588                             } catch (BadPositionCategoryException e) {
589                                 JavaPlugin.log(e);
590                             }
591                         }
592                     }
593                 });
594             }
595
596
597         }
598
599         /*
600          * @see org.eclipse.jface.text.link.ILinkedModeListener#suspend(org.eclipse.jface.text.link.LinkedModeModel)
601          */

602         public void suspend(LinkedModeModel environment) {
603         }
604
605         /*
606          * @see org.eclipse.jface.text.link.ILinkedModeListener#resume(org.eclipse.jface.text.link.LinkedModeModel, int)
607          */

608         public void resume(LinkedModeModel environment, int flags) {
609         }
610     }
611
612     /**
613      * Remembers data related to the current selection to be able to
614      * restore it later.
615      *
616      * @since 3.0
617      */

618     private class RememberedSelection {
619         /** The remembered selection start. */
620         private RememberedOffset fStartOffset= new RememberedOffset();
621         /** The remembered selection end. */
622         private RememberedOffset fEndOffset= new RememberedOffset();
623
624         /**
625          * Remember current selection.
626          */

627         public void remember() {
628             /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257
629              * This method may be called inside an asynchronous call posted
630              * to the UI thread, so protect against intermediate disposal
631              * of the editor.
632              */

633             ISourceViewer viewer= getSourceViewer();
634             if (viewer != null) {
635                 Point selection= viewer.getSelectedRange();
636                 int startOffset= selection.x;
637                 int endOffset= startOffset + selection.y;
638
639                 fStartOffset.setOffset(startOffset);
640                 fEndOffset.setOffset(endOffset);
641             }
642         }
643
644         /**
645          * Restore remembered selection.
646          */

647         public void restore() {
648             /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257
649              * This method may be called inside an asynchronous call posted
650              * to the UI thread, so protect against intermediate disposal
651              * of the editor.
652              */

653             if (getSourceViewer() == null)
654                 return;
655
656             try {
657
658                 int startOffset, endOffset;
659                 int revealStartOffset, revealEndOffset;
660                 if (showsHighlightRangeOnly()) {
661                     IJavaElement newStartElement= fStartOffset.getElement();
662                     startOffset= fStartOffset.getRememberedOffset(newStartElement);
663                     revealStartOffset= fStartOffset.getRevealOffset(newStartElement, startOffset);
664                     if (revealStartOffset == -1)
665                         startOffset= -1;
666
667                     IJavaElement newEndElement= fEndOffset.getElement();
668                     endOffset= fEndOffset.getRememberedOffset(newEndElement);
669                     revealEndOffset= fEndOffset.getRevealOffset(newEndElement, endOffset);
670                     if (revealEndOffset == -1)
671                         endOffset= -1;
672                 } else {
673                     startOffset= fStartOffset.getOffset();
674                     revealStartOffset= startOffset;
675                     endOffset= fEndOffset.getOffset();
676                     revealEndOffset= endOffset;
677                 }
678
679                 if (startOffset == -1) {
680                     startOffset= endOffset; // fallback to caret offset
681
revealStartOffset= revealEndOffset;
682                 }
683
684                 if (endOffset == -1) {
685                     endOffset= startOffset; // fallback to other offset
686
revealEndOffset= revealStartOffset;
687                 }
688
689                 IJavaElement element;
690                 if (endOffset == -1) {
691                      // fallback to element selection
692
element= fEndOffset.getElement();
693                     if (element == null)
694                         element= fStartOffset.getElement();
695                     if (element != null)
696                         setSelection(element);
697                     return;
698                 }
699
700                 if (isValidSelection(revealStartOffset, revealEndOffset - revealStartOffset) && isValidSelection(startOffset, endOffset - startOffset))
701                     selectAndReveal(startOffset, endOffset - startOffset, revealStartOffset, revealEndOffset - revealStartOffset);
702             } finally {
703                 fStartOffset.clear();
704                 fEndOffset.clear();
705             }
706         }
707
708         private boolean isValidSelection(int offset, int length) {
709             IDocumentProvider provider= getDocumentProvider();
710             if (provider != null) {
711                 IDocument document= provider.getDocument(getEditorInput());
712                 if (document != null) {
713                     int end= offset + length;
714                     int documentLength= document.getLength();
715                     return 0 <= offset && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
716                 }
717             }
718             return false;
719         }
720
721     }
722
723     /**
724      * Remembers additional data for a given
725      * offset to be able restore it later.
726      *
727      * @since 3.0
728      */

729     private class RememberedOffset {
730         /** Remembered line for the given offset */
731         private int fLine;
732         /** Remembered column for the given offset*/
733         private int fColumn;
734         /** Remembered Java element for the given offset*/
735         private IJavaElement fElement;
736         /** Remembered Java element line for the given offset*/
737         private int fElementLine;
738
739         /**
740          * Store visual properties of the given offset.
741          *
742          * @param offset Offset in the document
743          */

744         public void setOffset(int offset) {
745             try {
746                 IDocument document= getSourceViewer().getDocument();
747                 fLine= document.getLineOfOffset(offset);
748                 fColumn= offset - document.getLineOffset(fLine);
749                 fElement= getElementAt(offset, true);
750                 fElementLine= getElementLine(document, fElement);
751             } catch (BadLocationException e) {
752                 // should not happen
753
JavaPlugin.log(e);
754                 clear();
755             } catch (JavaModelException e) {
756                 // should not happen
757
JavaPlugin.log(e.getStatus());
758                 clear();
759             }
760         }
761         
762         /**
763          * Computes the element line of a java element (the start of the element, or the line with
764          * the element's name range).
765          *
766          * @param document the displayed document for line information
767          * @param element the java element, may be <code>null</code>
768          * @return the element's start line, or -1
769          * @throws BadLocationException
770          * @throws JavaModelException
771          * @since 3.2
772          */

773         private int getElementLine(IDocument document, IJavaElement element) throws BadLocationException, JavaModelException {
774             if (element instanceof IMember) {
775                 ISourceRange range= ((IMember) element).getNameRange();
776                 if (range != null)
777                     return document.getLineOfOffset(range.getOffset());
778             }
779             int elementOffset= getOffset(element);
780             if (elementOffset != -1)
781                 return document.getLineOfOffset(elementOffset);
782             return -1;
783         }
784
785         /**
786          * Return offset recomputed from stored visual properties.
787          *
788          * @return Offset in the document
789          */

790         public int getOffset() {
791             IJavaElement newElement= getElement();
792
793             int offset= getRememberedOffset(newElement);
794
795             if (offset == -1 || newElement != null && !containsOffset(newElement, offset) && (offset == 0 || !containsOffset(newElement, offset - 1)))
796                 return -1;
797
798             return offset;
799         }
800
801         /**
802          * Return offset recomputed from stored visual properties.
803          *
804          * @param newElement Enclosing element
805          * @return Offset in the document
806          */

807         public int getRememberedOffset(IJavaElement newElement) {
808             try {
809                 IDocument document= getSourceViewer().getDocument();
810                 int newElementLine= getElementLine(document, newElement);
811                 int newLine= fLine;
812                 if (newElementLine != -1 && fElementLine != -1)
813                     newLine += newElementLine - fElementLine;
814
815                 if (newLine < 0 || newLine >= document.getNumberOfLines())
816                     return -1;
817                 int maxColumn= document.getLineLength(newLine);
818                 String JavaDoc lineDelimiter= document.getLineDelimiter(newLine);
819                 if (lineDelimiter != null)
820                     maxColumn= maxColumn - lineDelimiter.length();
821                 int offset;
822                 if (fColumn > maxColumn)
823                     offset= document.getLineOffset(newLine) + maxColumn;
824                 else
825                     offset= document.getLineOffset(newLine) + fColumn;
826
827                 return offset;
828             } catch (BadLocationException e) {
829                 // should not happen
830
JavaPlugin.log(e);
831                 return -1;
832             } catch (JavaModelException e) {
833                 // should not happen
834
JavaPlugin.log(e.getStatus());
835                 return -1;
836             }
837         }
838
839         /**
840          * Returns the offset used to reveal the given element based on the given selection offset.
841          * @param element the element
842          * @param offset the selection offset
843          * @return the offset to reveal the given element based on the given selection offset
844          */

845         public int getRevealOffset(IJavaElement element, int offset) {
846             if (element == null || offset == -1)
847                 return -1;
848
849             if (containsOffset(element, offset)) {
850                 if (offset > 0) {
851                     IJavaElement alternateElement= getElementAt(offset, false);
852                     if (element.getHandleIdentifier().equals(alternateElement.getParent().getHandleIdentifier()))
853                         return offset - 1; // Solves test case 2 from https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
854
}
855                 return offset;
856             } else if (offset > 0 && containsOffset(element, offset - 1))
857                 return offset - 1; // Solves test case 1 from https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
858

859             return -1;
860         }
861
862         /**
863          * Return Java element recomputed from stored visual properties.
864          *
865          * @return Java element
866          */

867         public IJavaElement getElement() {
868             if (fElement == null)
869                 return null;
870
871             return findElement(fElement);
872         }
873
874         /**
875          * Clears the stored position
876          */

877         public void clear() {
878             fLine= -1;
879             fColumn= -1;
880             fElement= null;
881             fElementLine= -1;
882         }
883
884         /**
885          * Does the given Java element contain the given offset?
886          * @param element Java element
887          * @param offset Offset
888          * @return <code>true</code> iff the Java element contains the offset
889          */

890         private boolean containsOffset(IJavaElement element, int offset) {
891             int elementOffset= getOffset(element);
892             int elementLength= getLength(element);
893             return (elementOffset > -1 && elementLength > -1) ? (offset >= elementOffset && offset < elementOffset + elementLength) : false;
894         }
895         /**
896          * Returns the offset of the given Java element.
897          *
898          * @param element Java element
899          * @return Offset of the given Java element
900          */

901         private int getOffset(IJavaElement element) {
902             if (element instanceof ISourceReference) {
903                 ISourceReference sr= (ISourceReference) element;
904                 try {
905                     ISourceRange srcRange= sr.getSourceRange();
906                     if (srcRange != null)
907                         return srcRange.getOffset();
908                 } catch (JavaModelException e) {
909                 }
910             }
911             return -1;
912         }
913
914         /**
915          * Returns the length of the given Java element.
916          *
917          * @param element Java element
918          * @return Length of the given Java element
919          */

920         private int getLength(IJavaElement element) {
921             if (element instanceof ISourceReference) {
922                 ISourceReference sr= (ISourceReference) element;
923                 try {
924                     ISourceRange srcRange= sr.getSourceRange();
925                     if (srcRange != null)
926                         return srcRange.getLength();
927                 } catch (JavaModelException e) {
928                 }
929             }
930             return -1;
931         }
932
933         /**
934          * Returns the updated java element for the old java element.
935          *
936          * @param element Old Java element
937          * @return Updated Java element
938          */

939         private IJavaElement findElement(IJavaElement element) {
940
941             if (element == null)
942                 return null;
943
944             IWorkingCopyManager manager= JavaPlugin.getDefault().getWorkingCopyManager();
945             ICompilationUnit unit= manager.getWorkingCopy(getEditorInput());
946
947             if (unit != null) {
948                 try {
949                     JavaModelUtil.reconcile(unit);
950                     IJavaElement[] findings= unit.findElements(element);
951                     if (findings != null && findings.length > 0)
952                         return findings[0];
953
954                 } catch (JavaModelException x) {
955                     JavaPlugin.log(x.getStatus());
956                     // nothing found, be tolerant and go on
957
}
958             }
959
960             return null;
961         }
962
963     }
964
965     /** Preference key for code formatter tab size */
966     private final static String JavaDoc CODE_FORMATTER_TAB_SIZE= DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE;
967     /** Preference key for inserting spaces rather than tabs */
968     private final static String JavaDoc SPACES_FOR_TABS= DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR;
969     /** Preference key for automatically closing strings */
970     private final static String JavaDoc CLOSE_STRINGS= PreferenceConstants.EDITOR_CLOSE_STRINGS;
971     /** Preference key for automatically closing brackets and parenthesis */
972     private final static String JavaDoc CLOSE_BRACKETS= PreferenceConstants.EDITOR_CLOSE_BRACKETS;
973
974
975     /** The editor's save policy */
976     protected ISavePolicy fSavePolicy;
977     /** Listener to annotation model changes that updates the error tick in the tab image */
978     private JavaEditorErrorTickUpdater fJavaEditorErrorTickUpdater;
979     /**
980      * The remembered selection.
981      * @since 3.0
982      */

983     private RememberedSelection fRememberedSelection= new RememberedSelection();
984     /** The bracket inserter. */
985     private BracketInserter fBracketInserter= new BracketInserter();
986
987     /** The standard action groups added to the menu */
988     private GenerateActionGroup fGenerateActionGroup;
989     private CompositeActionGroup fContextMenuGroup;
990     
991     private CorrectionCommandInstaller fCorrectionCommands;
992
993     /**
994      * Reconciling listeners.
995      * @since 3.0
996      */

997     private ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY);
998
999     /**
1000     * Mutex for the reconciler. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898
1001     * for a description of the problem.
1002     * <p>
1003     * XXX remove once the underlying problem (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved.
1004     * </p>
1005     */

1006    private final Object JavaDoc fReconcilerLock= new Object JavaDoc();
1007
1008
1009
1010
1011
1012    /**
1013     * Creates a new compilation unit editor.
1014     */

1015    public CompilationUnitEditor() {
1016        super();
1017        setDocumentProvider(JavaPlugin.getDefault().getCompilationUnitDocumentProvider());
1018        setEditorContextMenuId("#CompilationUnitEditorContext"); //$NON-NLS-1$
1019
setRulerContextMenuId("#CompilationUnitRulerContext"); //$NON-NLS-1$
1020
setOutlinerContextMenuId("#CompilationUnitOutlinerContext"); //$NON-NLS-1$
1021
// don't set help contextId, we install our own help context
1022
fSavePolicy= null;
1023
1024        fJavaEditorErrorTickUpdater= new JavaEditorErrorTickUpdater(this);
1025        fCorrectionCommands= null;
1026    }
1027
1028    /*
1029     * @see AbstractTextEditor#createActions()
1030     */

1031    protected void createActions() {
1032
1033        super.createActions();
1034
1035        IAction action= new ContentAssistAction(JavaEditorMessages.getBundleForConstructedKeys(), "ContentAssistProposal.", this); //$NON-NLS-1$
1036
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
1037        setAction("ContentAssistProposal", action); //$NON-NLS-1$
1038
markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
1039
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.CONTENT_ASSIST_ACTION);
1040
1041        action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
1042
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
1043        setAction("ContentAssistContextInformation", action); //$NON-NLS-1$
1044
markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$
1045
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.PARAMETER_HINTS_ACTION);
1046
1047        action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$
1048
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.COMMENT);
1049        setAction("Comment", action); //$NON-NLS-1$
1050
markAsStateDependentAction("Comment", true); //$NON-NLS-1$
1051
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.COMMENT_ACTION);
1052
1053        action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$
1054
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.UNCOMMENT);
1055        setAction("Uncomment", action); //$NON-NLS-1$
1056
markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
1057
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.UNCOMMENT_ACTION);
1058
1059        action= new ToggleCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "ToggleComment.", this); //$NON-NLS-1$
1060
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.TOGGLE_COMMENT);
1061        setAction("ToggleComment", action); //$NON-NLS-1$
1062
markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$
1063
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.TOGGLE_COMMENT_ACTION);
1064        configureToggleCommentAction();
1065
1066        action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
1067
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.FORMAT);
1068        setAction("Format", action); //$NON-NLS-1$
1069
markAsStateDependentAction("Format", true); //$NON-NLS-1$
1070
markAsSelectionDependentAction("Format", true); //$NON-NLS-1$
1071
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.FORMAT_ACTION);
1072
1073        action= new AddBlockCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "AddBlockComment.", this); //$NON-NLS-1$
1074
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.ADD_BLOCK_COMMENT);
1075        setAction("AddBlockComment", action); //$NON-NLS-1$
1076
markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$
1077
markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$
1078
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
1079
1080        action= new RemoveBlockCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "RemoveBlockComment.", this); //$NON-NLS-1$
1081
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT);
1082        setAction("RemoveBlockComment", action); //$NON-NLS-1$
1083
markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
1084
markAsSelectionDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
1085
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION);
1086
1087        action= new IndentAction(JavaEditorMessages.getBundleForConstructedKeys(), "Indent.", this, false); //$NON-NLS-1$
1088
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.INDENT);
1089        setAction("Indent", action); //$NON-NLS-1$
1090
markAsStateDependentAction("Indent", true); //$NON-NLS-1$
1091
markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$
1092
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.INDENT_ACTION);
1093
1094        action= new IndentAction(JavaEditorMessages.getBundleForConstructedKeys(), "Indent.", this, true); //$NON-NLS-1$
1095
setAction("IndentOnTab", action); //$NON-NLS-1$
1096
markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$
1097
markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$
1098

1099        // override the text editor actions with indenting move line actions
1100
JavaMoveLinesAction[] moveLinesActions= JavaMoveLinesAction.createMoveCopyActionSet(JavaEditorMessages.getBundleForConstructedKeys(), this);
1101        ResourceAction rAction= moveLinesActions[0];
1102        rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
1103        rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
1104        setAction(ITextEditorActionConstants.MOVE_LINE_UP, rAction);
1105
1106        rAction= moveLinesActions[1];
1107        rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
1108        rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
1109        setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, rAction);
1110
1111        rAction= moveLinesActions[2];
1112        rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
1113        rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
1114        setAction(ITextEditorActionConstants.COPY_LINE_UP, rAction);
1115
1116        rAction= moveLinesActions[3];
1117        rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
1118        rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
1119        setAction(ITextEditorActionConstants.COPY_LINE_DOWN, rAction);
1120
1121        if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) {
1122            // don't replace Shift Right - have to make sure their enablement is mutually exclusive
1123
// removeActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT);
1124
setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$
1125
}
1126
1127        fGenerateActionGroup= new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
1128        ActionGroup rg= new RefactorActionGroup(this, ITextEditorActionConstants.GROUP_EDIT, false);
1129        ActionGroup surroundWith= new SurroundWithActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
1130        
1131        fActionGroups.addGroup(surroundWith);
1132        fActionGroups.addGroup(rg);
1133        fActionGroups.addGroup(fGenerateActionGroup);
1134
1135        // We have to keep the context menu group separate to have better control over positioning
1136
fContextMenuGroup= new CompositeActionGroup(new ActionGroup[] {
1137            fGenerateActionGroup,
1138            rg,
1139            surroundWith,
1140            new LocalHistoryActionGroup(this, ITextEditorActionConstants.GROUP_EDIT)});
1141        
1142        fCorrectionCommands= new CorrectionCommandInstaller(); // allow shortcuts for quick fix/assist
1143
fCorrectionCommands.registerCommands(this);
1144    }
1145
1146    /*
1147     * @see JavaEditor#getElementAt(int)
1148     */

1149    protected IJavaElement getElementAt(int offset) {
1150        return getElementAt(offset, true);
1151    }
1152
1153    /**
1154     * Returns the most narrow element including the given offset. If <code>reconcile</code>
1155     * is <code>true</code> the editor's input element is reconciled in advance. If it is
1156     * <code>false</code> this method only returns a result if the editor's input element
1157     * does not need to be reconciled.
1158     *
1159     * @param offset the offset included by the retrieved element
1160     * @param reconcile <code>true</code> if working copy should be reconciled
1161     * @return the most narrow element which includes the given offset
1162     */

1163    protected IJavaElement getElementAt(int offset, boolean reconcile) {
1164        ICompilationUnit unit= (ICompilationUnit)getInputJavaElement();
1165
1166        if (unit != null) {
1167            try {
1168                if (reconcile) {
1169                    JavaModelUtil.reconcile(unit);
1170                    return unit.getElementAt(offset);
1171                } else if (unit.isConsistent())
1172                    return unit.getElementAt(offset);
1173
1174            } catch (JavaModelException x) {
1175                if (!x.isDoesNotExist())
1176                JavaPlugin.log(x.getStatus());
1177                // nothing found, be tolerant and go on
1178
}
1179        }
1180
1181        return null;
1182    }
1183
1184    /*
1185     * @see JavaEditor#getCorrespondingElement(IJavaElement)
1186     */

1187    protected IJavaElement getCorrespondingElement(IJavaElement element) {
1188        // XXX: With new working copy story: original == working copy.
1189
// Note that the previous code could result in a reconcile as side effect. Should check if that
1190
// is still required.
1191
return element;
1192    }
1193
1194    /*
1195     * @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager)
1196     */

1197    public void editorContextMenuAboutToShow(IMenuManager menu) {
1198        super.editorContextMenuAboutToShow(menu);
1199
1200        ActionContext context= new ActionContext(getSelectionProvider().getSelection());
1201        fContextMenuGroup.setContext(context);
1202        fContextMenuGroup.fillContextMenu(menu);
1203        fContextMenuGroup.setContext(null);
1204    }
1205
1206    /*
1207     * @see org.eclipse.ui.texteditor.AbstractTextEditor#performSave(boolean, org.eclipse.core.runtime.IProgressMonitor)
1208     */

1209    protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
1210        IDocumentProvider p= getDocumentProvider();
1211        if (p instanceof ICompilationUnitDocumentProvider) {
1212            ICompilationUnitDocumentProvider cp= (ICompilationUnitDocumentProvider) p;
1213            cp.setSavePolicy(fSavePolicy);
1214        }
1215        try {
1216            super.performSave(overwrite, progressMonitor);
1217        } finally {
1218            if (p instanceof ICompilationUnitDocumentProvider) {
1219                ICompilationUnitDocumentProvider cp= (ICompilationUnitDocumentProvider) p;
1220                cp.setSavePolicy(null);
1221            }
1222        }
1223    }
1224
1225    /*
1226     * @see AbstractTextEditor#doSave(IProgressMonitor)
1227     */

1228    public void doSave(IProgressMonitor progressMonitor) {
1229
1230        IDocumentProvider p= getDocumentProvider();
1231        if (p == null) {
1232            // editor has been closed
1233
return;
1234        }
1235
1236        if (p.isDeleted(getEditorInput())) {
1237
1238            if (isSaveAsAllowed()) {
1239
1240                /*
1241                 * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
1242                 * Changed Behavior to make sure that if called inside a regular save (because
1243                 * of deletion of input element) there is a way to report back to the caller.
1244                 */

1245                 performSaveAs(progressMonitor);
1246
1247            } else {
1248
1249                /*
1250                 * 1GF5YOX: ITPJUI:ALL - Save of delete file claims it's still there
1251                 * Missing resources.
1252                 */

1253                Shell shell= getSite().getShell();
1254                MessageDialog.openError(shell, JavaEditorMessages.CompilationUnitEditor_error_saving_title1, JavaEditorMessages.CompilationUnitEditor_error_saving_message1);
1255            }
1256
1257        } else {
1258
1259            setStatusLineErrorMessage(null);
1260
1261            updateState(getEditorInput());
1262            validateState(getEditorInput());
1263
1264            IWorkingCopyManager manager= JavaPlugin.getDefault().getWorkingCopyManager();
1265            ICompilationUnit unit= manager.getWorkingCopy(getEditorInput());
1266
1267            if (unit != null) {
1268                synchronized (unit) {
1269                    performSave(false, progressMonitor);
1270                }
1271            } else
1272                performSave(false, progressMonitor);
1273        }
1274    }
1275    
1276    /*
1277     * @see org.eclipse.ui.texteditor.AbstractTextEditor#openSaveErrorDialog(java.lang.String, java.lang.String, org.eclipse.core.runtime.CoreException)
1278     * @since 3.3
1279     */

1280    protected void openSaveErrorDialog(String JavaDoc title, String JavaDoc message, CoreException exception) {
1281        IStatus status= exception.getStatus();
1282        if (JavaUI.ID_PLUGIN.equals(status.getPlugin()) && status.getCode() == IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION) {
1283            int mask= IStatus.OK | IStatus.INFO | IStatus.WARNING | IStatus.ERROR;
1284            ErrorDialog dialog = new ErrorDialog(getSite().getShell(), title, message, status, mask) {
1285                protected Control createDialogArea(Composite parent) {
1286                    parent= (Composite)super.createDialogArea(parent);
1287                    Link link= new Link(parent, SWT.NONE);
1288                    link.setText(JavaEditorMessages.CompilationUnitEditor_error_saving_saveParticipant);
1289                    link.addSelectionListener(new SelectionAdapter() {
1290                        public void widgetSelected(SelectionEvent e) {
1291                            PreferencesUtil.createPreferenceDialogOn(getShell(), "org.eclipse.jdt.ui.preferences.SaveParticipantPreferencePage", null, null).open(); //$NON-NLS-1$
1292
}
1293                    });
1294                    GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
1295                    link.setLayoutData(gridData);
1296                    return parent;
1297                }
1298            };
1299            dialog.open();
1300        } else
1301            super.openSaveErrorDialog(title, message, exception);
1302    }
1303
1304    public boolean isSaveAsAllowed() {
1305        return true;
1306    }
1307
1308    /*
1309     * @see AbstractTextEditor#doSetInput(IEditorInput)
1310     */

1311    protected void doSetInput(IEditorInput input) throws CoreException {
1312        super.doSetInput(input);
1313        configureToggleCommentAction();
1314        if (fJavaEditorErrorTickUpdater != null)
1315            fJavaEditorErrorTickUpdater.updateEditorImage(getInputJavaElement());
1316    }
1317
1318    /*
1319     * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#installOverrideIndicator(boolean)
1320     * @since 3.0
1321     */

1322    protected void installOverrideIndicator(boolean provideAST) {
1323        super.installOverrideIndicator(provideAST);
1324
1325        if (fOverrideIndicatorManager == null)
1326            return;
1327
1328        addReconcileListener(fOverrideIndicatorManager);
1329    }
1330
1331    /*
1332     * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#uninstallOverrideIndicator()
1333     * @since 3.0
1334     */

1335    protected void uninstallOverrideIndicator() {
1336        if (fOverrideIndicatorManager != null)
1337            removeReconcileListener(fOverrideIndicatorManager);
1338        super.uninstallOverrideIndicator();
1339    }
1340
1341    /**
1342     * Configures the toggle comment action
1343     *
1344     * @since 3.0
1345     */

1346    private void configureToggleCommentAction() {
1347        IAction action= getAction("ToggleComment"); //$NON-NLS-1$
1348
if (action instanceof ToggleCommentAction) {
1349            ISourceViewer sourceViewer= getSourceViewer();
1350            SourceViewerConfiguration configuration= getSourceViewerConfiguration();
1351            ((ToggleCommentAction)action).configure(sourceViewer, configuration);
1352        }
1353    }
1354
1355    /*
1356     * @see org.eclipse.ui.texteditor.AbstractTextEditor#installTabsToSpacesConverter()
1357     * @since 3.3
1358     */

1359    protected void installTabsToSpacesConverter() {
1360        ISourceViewer sourceViewer= getSourceViewer();
1361        SourceViewerConfiguration config= getSourceViewerConfiguration();
1362        if (config != null && sourceViewer instanceof ITextViewerExtension7) {
1363            int tabWidth= config.getTabWidth(sourceViewer);
1364            TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
1365            tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
1366            IDocumentProvider provider= getDocumentProvider();
1367            if (provider instanceof ICompilationUnitDocumentProvider) {
1368                ICompilationUnitDocumentProvider cup= (ICompilationUnitDocumentProvider) provider;
1369                tabToSpacesConverter.setLineTracker(cup.createLineTracker(getEditorInput()));
1370            } else
1371                tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
1372            ((ITextViewerExtension7)sourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
1373            updateIndentPrefixes();
1374        }
1375    }
1376
1377    /*
1378     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isTabsToSpacesConversionEnabled()
1379     * @since 3.3
1380     */

1381    protected boolean isTabsToSpacesConversionEnabled() {
1382        IJavaElement element= getInputJavaElement();
1383        IJavaProject project= element == null ? null : element.getJavaProject();
1384        String JavaDoc option;
1385        if (project == null)
1386            option= JavaCore.getOption(SPACES_FOR_TABS);
1387        else
1388            option= project.getOption(SPACES_FOR_TABS, true);
1389        return JavaCore.SPACE.equals(option);
1390    }
1391
1392    public void dispose() {
1393
1394        ISourceViewer sourceViewer= getSourceViewer();
1395        if (sourceViewer instanceof ITextViewerExtension)
1396            ((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter);
1397
1398        if (fJavaEditorErrorTickUpdater != null) {
1399            fJavaEditorErrorTickUpdater.dispose();
1400            fJavaEditorErrorTickUpdater= null;
1401        }
1402
1403        if (fCorrectionCommands != null) {
1404            fCorrectionCommands.deregisterCommands();
1405            fCorrectionCommands= null;
1406        }
1407
1408        super.dispose();
1409    }
1410
1411    /*
1412     * @see AbstractTextEditor#createPartControl(Composite)
1413     */

1414    public void createPartControl(Composite parent) {
1415
1416        super.createPartControl(parent);
1417
1418        IPreferenceStore preferenceStore= getPreferenceStore();
1419        boolean closeBrackets= preferenceStore.getBoolean(CLOSE_BRACKETS);
1420        boolean closeStrings= preferenceStore.getBoolean(CLOSE_STRINGS);
1421        boolean closeAngularBrackets= JavaCore.VERSION_1_5.compareTo(preferenceStore.getString(JavaCore.COMPILER_SOURCE)) <= 0;
1422
1423        fBracketInserter.setCloseBracketsEnabled(closeBrackets);
1424        fBracketInserter.setCloseStringsEnabled(closeStrings);
1425        fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
1426
1427        ISourceViewer sourceViewer= getSourceViewer();
1428        if (sourceViewer instanceof ITextViewerExtension)
1429            ((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter);
1430
1431        if (isMarkingOccurrences())
1432            installOccurrencesFinder(false);
1433    }
1434
1435    private static char getEscapeCharacter(char character) {
1436        switch (character) {
1437            case '"':
1438            case '\'':
1439                return '\\';
1440            default:
1441                return 0;
1442        }
1443    }
1444
1445    private static char getPeerCharacter(char character) {
1446        switch (character) {
1447            case '(':
1448                return ')';
1449
1450            case ')':
1451                return '(';
1452
1453            case '<':
1454                return '>';
1455
1456            case '>':
1457                return '<';
1458
1459            case '[':
1460                return ']';
1461
1462            case ']':
1463                return '[';
1464
1465            case '"':
1466                return character;
1467
1468            case '\'':
1469                return character;
1470
1471            default:
1472                throw new IllegalArgumentException JavaDoc();
1473        }
1474    }
1475
1476    /*
1477     * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
1478     */

1479    protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
1480
1481        try {
1482
1483            AdaptedSourceViewer asv= (AdaptedSourceViewer) getSourceViewer();
1484            if (asv != null) {
1485
1486                String JavaDoc p= event.getProperty();
1487
1488                if (CLOSE_BRACKETS.equals(p)) {
1489                    fBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(p));
1490                    return;
1491                }
1492
1493                if (CLOSE_STRINGS.equals(p)) {
1494                    fBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(p));
1495                    return;
1496                }
1497
1498                if (JavaCore.COMPILER_SOURCE.equals(p)) {
1499                    boolean closeAngularBrackets= JavaCore.VERSION_1_5.compareTo(getPreferenceStore().getString(p)) <= 0;
1500                    fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
1501                }
1502
1503                if (SPACES_FOR_TABS.equals(p)) {
1504                    if (isTabsToSpacesConversionEnabled())
1505                        installTabsToSpacesConverter();
1506                    else
1507                        uninstallTabsToSpacesConverter();
1508                    return;
1509                }
1510
1511                if (PreferenceConstants.EDITOR_SMART_TAB.equals(p)) {
1512                    if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) {
1513                        setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$
1514
} else {
1515                        removeActionActivationCode("IndentOnTab"); //$NON-NLS-1$
1516
}
1517                }
1518
1519                IContentAssistant c= asv.getContentAssistant();
1520                if (c instanceof ContentAssistant)
1521                    ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event);
1522
1523                if (CODE_FORMATTER_TAB_SIZE.equals(p) && isTabsToSpacesConversionEnabled()) {
1524                    uninstallTabsToSpacesConverter();
1525                    installTabsToSpacesConverter();
1526                }
1527            }
1528
1529        } finally {
1530            super.handlePreferenceStoreChanged(event);
1531        }
1532    }
1533
1534    /*
1535     * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#createJavaSourceViewer(org.eclipse.swt.widgets.Composite, org.eclipse.jface.text.source.IVerticalRuler, org.eclipse.jface.text.source.IOverviewRuler, boolean, int)
1536     */

1537    protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles, IPreferenceStore store) {
1538        return new AdaptedSourceViewer(parent, verticalRuler, overviewRuler, isOverviewRulerVisible, styles, store);
1539    }
1540
1541    /*
1542     * @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#aboutToBeReconciled()
1543     * @since 3.0
1544     */

1545    public void aboutToBeReconciled() {
1546
1547        // Notify AST provider
1548
JavaPlugin.getDefault().getASTProvider().aboutToBeReconciled(getInputJavaElement());
1549
1550        // Notify listeners
1551
Object JavaDoc[] listeners = fReconcilingListeners.getListeners();
1552        for (int i = 0, length= listeners.length; i < length; ++i)
1553            ((IJavaReconcilingListener)listeners[i]).aboutToBeReconciled();
1554    }
1555
1556    /*
1557     * @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#reconciled(CompilationUnit, boolean, IProgressMonitor)
1558     * @since 3.0
1559     */

1560    public void reconciled(CompilationUnit ast, boolean forced, IProgressMonitor progressMonitor) {
1561        
1562        // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58245
1563
JavaPlugin javaPlugin= JavaPlugin.getDefault();
1564        if (javaPlugin == null)
1565            return;
1566        
1567        // Always notify AST provider
1568
javaPlugin.getASTProvider().reconciled(ast, getInputJavaElement(), progressMonitor);
1569
1570        // Notify listeners
1571
Object JavaDoc[] listeners = fReconcilingListeners.getListeners();
1572        for (int i = 0, length= listeners.length; i < length; ++i)
1573            ((IJavaReconcilingListener)listeners[i]).reconciled(ast, forced, progressMonitor);
1574
1575        // Update Java Outline page selection
1576
if (!forced && !progressMonitor.isCanceled()) {
1577            Shell shell= getSite().getShell();
1578            if (shell != null && !shell.isDisposed()) {
1579                shell.getDisplay().asyncExec(new Runnable JavaDoc() {
1580                    public void run() {
1581                        selectionChanged();
1582                    }
1583                });
1584            }
1585        }
1586    }
1587
1588    /**
1589     * Tells whether this is the active editor in the active page.
1590     *
1591     * @return <code>true</code> if this is the active editor in the active page
1592     * @see IWorkbenchPage#getActiveEditor
1593     */

1594    protected final boolean isActiveEditor() {
1595        IWorkbenchWindow window= getSite().getWorkbenchWindow();
1596        IWorkbenchPage page= window.getActivePage();
1597        if (page == null)
1598            return false;
1599        IEditorPart activeEditor= page.getActiveEditor();
1600        return activeEditor != null && activeEditor.equals(this);
1601    }
1602
1603    /**
1604     * Adds the given listener.
1605     * Has no effect if an identical listener was not already registered.
1606     *
1607     * @param listener The reconcile listener to be added
1608     * @since 3.0
1609     */

1610    final void addReconcileListener(IJavaReconcilingListener listener) {
1611        synchronized (fReconcilingListeners) {
1612            fReconcilingListeners.add(listener);
1613        }
1614    }
1615
1616    /**
1617     * Removes the given listener.
1618     * Has no effect if an identical listener was not already registered.
1619     *
1620     * @param listener the reconcile listener to be removed
1621     * @since 3.0
1622     */

1623    final void removeReconcileListener(IJavaReconcilingListener listener) {
1624        synchronized (fReconcilingListeners) {
1625            fReconcilingListeners.remove(listener);
1626        }
1627    }
1628
1629    protected void updateStateDependentActions() {
1630        super.updateStateDependentActions();
1631        fGenerateActionGroup.editorStateChanged();
1632    }
1633
1634    /*
1635     * @see AbstractTextEditor#rememberSelection()
1636     */

1637    protected void rememberSelection() {
1638        fRememberedSelection.remember();
1639    }
1640
1641    /*
1642     * @see AbstractTextEditor#restoreSelection()
1643     */

1644    protected void restoreSelection() {
1645        fRememberedSelection.restore();
1646    }
1647
1648    /*
1649     * @see AbstractTextEditor#canHandleMove(IEditorInput, IEditorInput)
1650     */

1651    protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
1652
1653        String JavaDoc oldExtension= ""; //$NON-NLS-1$
1654
if (originalElement instanceof IFileEditorInput) {
1655            IFile file= ((IFileEditorInput) originalElement).getFile();
1656            if (file != null) {
1657                String JavaDoc ext= file.getFileExtension();
1658                if (ext != null)
1659                    oldExtension= ext;
1660            }
1661        }
1662
1663        String JavaDoc newExtension= ""; //$NON-NLS-1$
1664
if (movedElement instanceof IFileEditorInput) {
1665            IFile file= ((IFileEditorInput) movedElement).getFile();
1666            if (file != null)
1667                newExtension= file.getFileExtension();
1668        }
1669
1670        return oldExtension.equals(newExtension);
1671    }
1672
1673    /*
1674     * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#getAdapter(java.lang.Class)
1675     */

1676    public Object JavaDoc getAdapter(Class JavaDoc required) {
1677        if (SmartBackspaceManager.class.equals(required)) {
1678            if (getSourceViewer() instanceof JavaSourceViewer) {
1679                return ((JavaSourceViewer) getSourceViewer()).getBackspaceManager();
1680            }
1681        }
1682
1683        return super.getAdapter(required);
1684    }
1685
1686    /**
1687     * Returns the mutex for the reconciler. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898
1688     * for a description of the problem.
1689     * <p>
1690     * XXX remove once the underlying problem (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved.
1691     * </p>
1692     * @return the lock reconcilers may use to synchronize on
1693     */

1694    public Object JavaDoc getReconcilerLock() {
1695        return fReconcilerLock;
1696    }
1697
1698
1699    /*
1700     * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#createNavigationActions()
1701     */

1702    protected void createNavigationActions() {
1703        super.createNavigationActions();
1704
1705        final StyledText textWidget= getSourceViewer().getTextWidget();
1706
1707        IAction action= new DeletePreviousSubWordAction();
1708        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD);
1709        setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action);
1710        textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL);
1711        markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, true);
1712
1713        action= new DeleteNextSubWordAction();
1714        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD);
1715        setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action);
1716        textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL);
1717        markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, true);
1718    }
1719
1720}
1721
Popular Tags