]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/javaeditor/CompilationUnitDocumentProvider.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / internal / ui / javaeditor / CompilationUnitDocumentProvider.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2000, 2012 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 *******************************************************************************/
11package org.eclipse.jdt.internal.ui.javaeditor;
12
13import java.io.BufferedReader;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.InputStreamReader;
17import java.io.Reader;
18import java.net.URI;
19import java.util.ArrayList;
20import java.util.HashMap;
21import java.util.Iterator;
22import java.util.List;
23import java.util.Map;
24
25import org.eclipse.swt.SWT;
26import org.eclipse.swt.graphics.GC;
27import org.eclipse.swt.graphics.Image;
28import org.eclipse.swt.graphics.Rectangle;
29import org.eclipse.swt.widgets.Canvas;
30import org.eclipse.swt.widgets.Display;
31
32import org.eclipse.core.filesystem.EFS;
33import org.eclipse.core.filesystem.IFileStore;
34import org.eclipse.core.filesystem.URIUtil;
35
36import org.eclipse.core.runtime.Assert;
37import org.eclipse.core.runtime.CoreException;
38import org.eclipse.core.runtime.IPath;
39import org.eclipse.core.runtime.IProgressMonitor;
40import org.eclipse.core.runtime.ISafeRunnable;
41import org.eclipse.core.runtime.IStatus;
42import org.eclipse.core.runtime.ListenerList;
43import org.eclipse.core.runtime.MultiStatus;
44import org.eclipse.core.runtime.NullProgressMonitor;
45import org.eclipse.core.runtime.SafeRunner;
46import org.eclipse.core.runtime.Status;
47import org.eclipse.core.runtime.SubProgressMonitor;
48import org.eclipse.core.runtime.jobs.ISchedulingRule;
49
50import org.eclipse.core.resources.IEncodedStorage;
51import org.eclipse.core.resources.IFile;
52import org.eclipse.core.resources.IFileState;
53import org.eclipse.core.resources.IMarker;
54import org.eclipse.core.resources.IResource;
55import org.eclipse.core.resources.IStorage;
56import org.eclipse.core.resources.ResourcesPlugin;
57
58import org.eclipse.core.filebuffers.IAnnotationModelFactory;
59
60import org.eclipse.jface.preference.IPreferenceStore;
61import org.eclipse.jface.util.IPropertyChangeListener;
62import org.eclipse.jface.util.PropertyChangeEvent;
63
64import org.eclipse.jface.text.BadLocationException;
65import org.eclipse.jface.text.DefaultLineTracker;
66import org.eclipse.jface.text.IDocument;
67import org.eclipse.jface.text.ILineTracker;
68import org.eclipse.jface.text.IRegion;
69import org.eclipse.jface.text.Position;
70import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation;
71import org.eclipse.jface.text.source.Annotation;
72import org.eclipse.jface.text.source.AnnotationModel;
73import org.eclipse.jface.text.source.AnnotationModelEvent;
74import org.eclipse.jface.text.source.IAnnotationAccessExtension;
75import org.eclipse.jface.text.source.IAnnotationModel;
76import org.eclipse.jface.text.source.IAnnotationModelListener;
77import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
78import org.eclipse.jface.text.source.IAnnotationPresentation;
79import org.eclipse.jface.text.source.ImageUtilities;
80
81import org.eclipse.ui.IEditorInput;
82import org.eclipse.ui.IFileEditorInput;
83import org.eclipse.ui.ISharedImages;
84import org.eclipse.ui.IStorageEditorInput;
85import org.eclipse.ui.IURIEditorInput;
86import org.eclipse.ui.PlatformUI;
87import org.eclipse.ui.ide.IDE.SharedImages;
88
89import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
90import org.eclipse.ui.texteditor.AnnotationPreference;
91import org.eclipse.ui.texteditor.AnnotationPreferenceLookup;
92import org.eclipse.ui.texteditor.IDocumentProvider;
93import org.eclipse.ui.texteditor.MarkerAnnotation;
94import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
95
96import org.eclipse.ui.editors.text.EditorsUI;
97import org.eclipse.ui.editors.text.ForwardingDocumentProvider;
98import org.eclipse.ui.editors.text.TextFileDocumentProvider;
99
100import org.eclipse.jdt.core.IBuffer;
101import org.eclipse.jdt.core.IClasspathEntry;
102import org.eclipse.jdt.core.ICompilationUnit;
103import org.eclipse.jdt.core.IJavaModel;
104import org.eclipse.jdt.core.IJavaProject;
105import org.eclipse.jdt.core.IProblemRequestor;
106import org.eclipse.jdt.core.JavaCore;
107import org.eclipse.jdt.core.JavaModelException;
108import org.eclipse.jdt.core.WorkingCopyOwner;
109import org.eclipse.jdt.core.compiler.CategorizedProblem;
110import org.eclipse.jdt.core.compiler.IProblem;
111
112import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
113import org.eclipse.jdt.internal.corext.util.Messages;
114
115import org.eclipse.jdt.launching.JavaRuntime;
116
117import org.eclipse.jdt.ui.JavaUI;
118import org.eclipse.jdt.ui.PreferenceConstants;
119import org.eclipse.jdt.ui.text.IJavaPartitions;
120
121import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
122import org.eclipse.jdt.internal.ui.JavaPlugin;
123import org.eclipse.jdt.internal.ui.JavaPluginImages;
124import org.eclipse.jdt.internal.ui.javaeditor.saveparticipant.IPostSaveListener;
125import org.eclipse.jdt.internal.ui.javaeditor.saveparticipant.SaveParticipantRegistry;
126import org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor;
127import org.eclipse.jdt.internal.ui.text.java.IProblemRequestorExtension;
128import org.eclipse.jdt.internal.ui.text.spelling.JavaSpellingReconcileStrategy;
129
130
131public class CompilationUnitDocumentProvider extends TextFileDocumentProvider implements ICompilationUnitDocumentProvider, IAnnotationModelFactory {
132
133 /**
134 * Bundle of all required informations to allow working copy management.
135 */
136 static protected class CompilationUnitInfo extends FileInfo {
137 public ICompilationUnit fCopy;
138 }
139
140 /**
141 * Annotation representing an <code>IProblem</code>.
142 */
143 static public class ProblemAnnotation extends Annotation implements IJavaAnnotation, IAnnotationPresentation, IQuickFixableAnnotation {
144
145 public static final String SPELLING_ANNOTATION_TYPE= "org.eclipse.ui.workbench.texteditor.spelling"; //$NON-NLS-1$
146
147 //XXX: To be fully correct these constants should be non-static
148 /**
149 * The layer in which task problem annotations are located.
150 */
151 private static final int TASK_LAYER;
152 /**
153 * The layer in which info problem annotations are located.
154 */
155 private static final int INFO_LAYER;
156 /**
157 * The layer in which warning problem annotations representing are located.
158 */
159 private static final int WARNING_LAYER;
160 /**
161 * The layer in which error problem annotations representing are located.
162 */
163 private static final int ERROR_LAYER;
164
165 static {
166 AnnotationPreferenceLookup lookup= EditorsUI.getAnnotationPreferenceLookup();
167 TASK_LAYER= computeLayer("org.eclipse.ui.workbench.texteditor.task", lookup); //$NON-NLS-1$
168 INFO_LAYER= computeLayer("org.eclipse.jdt.ui.info", lookup); //$NON-NLS-1$
169 WARNING_LAYER= computeLayer("org.eclipse.jdt.ui.warning", lookup); //$NON-NLS-1$
170 ERROR_LAYER= computeLayer("org.eclipse.jdt.ui.error", lookup); //$NON-NLS-1$
171 }
172
173 private static int computeLayer(String annotationType, AnnotationPreferenceLookup lookup) {
174 Annotation annotation= new Annotation(annotationType, false, null);
175 AnnotationPreference preference= lookup.getAnnotationPreference(annotation);
176 if (preference != null)
177 return preference.getPresentationLayer() + 1;
178 else
179 return IAnnotationAccessExtension.DEFAULT_LAYER + 1;
180 }
181
182 private static Image fgQuickFixImage;
183 private static Image fgQuickFixErrorImage;
184 private static Image fgTaskImage;
185 private static Image fgInfoImage;
186 private static Image fgWarningImage;
187 private static Image fgErrorImage;
188 private static boolean fgImagesInitialized= false;
189
190 private ICompilationUnit fCompilationUnit;
191 private List<IJavaAnnotation> fOverlaids;
192 private IProblem fProblem;
193 private Image fImage;
194 private boolean fImageInitialized= false;
195 private int fLayer= IAnnotationAccessExtension.DEFAULT_LAYER;
196 private boolean fIsQuickFixable;
197 private boolean fIsQuickFixableStateSet= false;
198
199
200 public ProblemAnnotation(IProblem problem, ICompilationUnit cu) {
201
202 fProblem= problem;
203 fCompilationUnit= cu;
204
205 if (JavaSpellingReconcileStrategy.SPELLING_PROBLEM_ID == fProblem.getID()) {
206 setType(SPELLING_ANNOTATION_TYPE);
207 fLayer= WARNING_LAYER;
208 } else if (IProblem.Task == fProblem.getID()) {
209 setType(JavaMarkerAnnotation.TASK_ANNOTATION_TYPE);
210 fLayer= TASK_LAYER;
211 } else if (fProblem.isWarning()) {
212 setType(JavaMarkerAnnotation.WARNING_ANNOTATION_TYPE);
213 fLayer= WARNING_LAYER;
214 } else if (fProblem.isError()) {
215 setType(JavaMarkerAnnotation.ERROR_ANNOTATION_TYPE);
216 fLayer= ERROR_LAYER;
217 } else {
218 setType(JavaMarkerAnnotation.INFO_ANNOTATION_TYPE);
219 fLayer= INFO_LAYER;
220 }
221 }
222
223 /*
224 * @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
225 */
226 public int getLayer() {
227 return fLayer;
228 }
229
230 private void initializeImage() {
231 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18936
232 if (!fImageInitialized) {
233 initializeImages();
234 if (!isQuickFixableStateSet())
235 setQuickFixable(isProblem() && indicateQuixFixableProblems() && JavaCorrectionProcessor.hasCorrections(this)); // no light bulb for tasks
236 if (isQuickFixable()) {
237 if (JavaMarkerAnnotation.ERROR_ANNOTATION_TYPE.equals(getType()))
238 fImage= fgQuickFixErrorImage;
239 else
240 fImage= fgQuickFixImage;
241 } else {
242 String type= getType();
243 if (JavaMarkerAnnotation.TASK_ANNOTATION_TYPE.equals(type))
244 fImage= fgTaskImage;
245 else if (JavaMarkerAnnotation.INFO_ANNOTATION_TYPE.equals(type))
246 fImage= fgInfoImage;
247 else if (JavaMarkerAnnotation.WARNING_ANNOTATION_TYPE.equals(type))
248 fImage= fgWarningImage;
249 else if (JavaMarkerAnnotation.ERROR_ANNOTATION_TYPE.equals(type))
250 fImage= fgErrorImage;
251 }
252 fImageInitialized= true;
253 }
254 }
255
256 private void initializeImages() {
257 if (fgImagesInitialized)
258 return;
259
260 fgQuickFixImage= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_PROBLEM);
261 fgQuickFixErrorImage= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_ERROR);
262
263 ISharedImages sharedImages= PlatformUI.getWorkbench().getSharedImages();
264 fgTaskImage= sharedImages.getImage(SharedImages.IMG_OBJS_TASK_TSK);
265 fgInfoImage= sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
266 fgWarningImage= sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
267 fgErrorImage= sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
268
269 fgImagesInitialized= true;
270 }
271
272 private boolean indicateQuixFixableProblems() {
273 return PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_CORRECTION_INDICATION);
274 }
275
276 /*
277 * @see Annotation#paint
278 */
279 public void paint(GC gc, Canvas canvas, Rectangle r) {
280 initializeImage();
281 if (fImage != null)
282 ImageUtilities.drawImage(fImage, gc, canvas, r, SWT.CENTER, SWT.TOP);
283 }
284
285 /*
286 * @see IJavaAnnotation#getMessage()
287 */
288 @Override
289 public String getText() {
290 return fProblem.getMessage();
291 }
292
293 /*
294 * @see IJavaAnnotation#getArguments()
295 */
296 public String[] getArguments() {
297 return isProblem() ? fProblem.getArguments() : null;
298 }
299
300 /*
301 * @see IJavaAnnotation#getId()
302 */
303 public int getId() {
304 return fProblem.getID();
305 }
306
307 /*
308 * @see IJavaAnnotation#isProblem()
309 */
310 public boolean isProblem() {
311 String type= getType();
312 return JavaMarkerAnnotation.WARNING_ANNOTATION_TYPE.equals(type) ||
313 JavaMarkerAnnotation.ERROR_ANNOTATION_TYPE.equals(type) ||
314 SPELLING_ANNOTATION_TYPE.equals(type);
315 }
316
317 /*
318 * @see IJavaAnnotation#hasOverlay()
319 */
320 public boolean hasOverlay() {
321 return false;
322 }
323
324 /*
325 * @see org.eclipse.jdt.internal.ui.javaeditor.IJavaAnnotation#getOverlay()
326 */
327 public IJavaAnnotation getOverlay() {
328 return null;
329 }
330
331 /*
332 * @see IJavaAnnotation#addOverlaid(IJavaAnnotation)
333 */
334 public void addOverlaid(IJavaAnnotation annotation) {
335 if (fOverlaids == null)
336 fOverlaids= new ArrayList<IJavaAnnotation>(1);
337 fOverlaids.add(annotation);
338 }
339
340 /*
341 * @see IJavaAnnotation#removeOverlaid(IJavaAnnotation)
342 */
343 public void removeOverlaid(IJavaAnnotation annotation) {
344 if (fOverlaids != null) {
345 fOverlaids.remove(annotation);
346 if (fOverlaids.size() == 0)
347 fOverlaids= null;
348 }
349 }
350
351 /*
352 * @see IJavaAnnotation#getOverlaidIterator()
353 */
354 public Iterator<IJavaAnnotation> getOverlaidIterator() {
355 if (fOverlaids != null)
356 return fOverlaids.iterator();
357 return null;
358 }
359
360 /*
361 * @see org.eclipse.jdt.internal.ui.javaeditor.IJavaAnnotation#getCompilationUnit()
362 */
363 public ICompilationUnit getCompilationUnit() {
364 return fCompilationUnit;
365 }
366
367 /*
368 * @see org.eclipse.jdt.internal.ui.javaeditor.IJavaAnnotation#getMarkerType()
369 */
370 public String getMarkerType() {
371 if (fProblem instanceof CategorizedProblem)
372 return ((CategorizedProblem) fProblem).getMarkerType();
373 return null;
374 }
375
376 /*
377 * @see org.eclipse.jface.text.quickassist.IQuickFixableAnnotation#setQuickFixable(boolean)
378 * @since 3.2
379 */
380 public void setQuickFixable(boolean state) {
381 fIsQuickFixable= state;
382 fIsQuickFixableStateSet= true;
383 }
384
385 /*
386 * @see org.eclipse.jface.text.quickassist.IQuickFixableAnnotation#isQuickFixableStateSet()
387 * @since 3.2
388 */
389 public boolean isQuickFixableStateSet() {
390 return fIsQuickFixableStateSet;
391 }
392
393 /*
394 * @see org.eclipse.jface.text.quickassist.IQuickFixableAnnotation#isQuickFixable()
395 * @since 3.2
396 */
397 public boolean isQuickFixable() {
398 Assert.isTrue(isQuickFixableStateSet());
399 return fIsQuickFixable;
400 }
401 }
402
403 /**
404 * Internal structure for mapping positions to some value.
405 * The reason for this specific structure is that positions can
406 * change over time. Thus a lookup is based on value and not
407 * on hash value.
408 */
409 protected static class ReverseMap {
410
411 static class Entry {
412 Position fPosition;
413 Object fValue;
414 }
415
416 private List<Entry> fList= new ArrayList<Entry>(2);
417 private int fAnchor= 0;
418
419 public ReverseMap() {
420 }
421
422 public Object get(Position position) {
423
424 Entry entry;
425
426 // behind anchor
427 int length= fList.size();
428 for (int i= fAnchor; i < length; i++) {
429 entry= fList.get(i);
430 if (entry.fPosition.equals(position)) {
431 fAnchor= i;
432 return entry.fValue;
433 }
434 }
435
436 // before anchor
437 for (int i= 0; i < fAnchor; i++) {
438 entry= fList.get(i);
439 if (entry.fPosition.equals(position)) {
440 fAnchor= i;
441 return entry.fValue;
442 }
443 }
444
445 return null;
446 }
447
448 private int getIndex(Position position) {
449 Entry entry;
450 int length= fList.size();
451 for (int i= 0; i < length; i++) {
452 entry= fList.get(i);
453 if (entry.fPosition.equals(position))
454 return i;
455 }
456 return -1;
457 }
458
459 public void put(Position position, Object value) {
460 int index= getIndex(position);
461 if (index == -1) {
462 Entry entry= new Entry();
463 entry.fPosition= position;
464 entry.fValue= value;
465 fList.add(entry);
466 } else {
467 Entry entry= fList.get(index);
468 entry.fValue= value;
469 }
470 }
471
472 public void remove(Position position) {
473 int index= getIndex(position);
474 if (index > -1)
475 fList.remove(index);
476 }
477
478 public void clear() {
479 fList.clear();
480 }
481 }
482
483 /**
484 * Annotation model dealing with java marker annotations and temporary problems.
485 * Also acts as problem requester for its compilation unit. Initially inactive. Must explicitly be
486 * activated.
487 */
488 protected static class CompilationUnitAnnotationModel extends ResourceMarkerAnnotationModel implements IProblemRequestor, IProblemRequestorExtension {
489
490 private static class ProblemRequestorState {
491 boolean fInsideReportingSequence= false;
492 List<IProblem> fReportedProblems;
493 }
494
495 private ThreadLocal<ProblemRequestorState> fProblemRequestorState= new ThreadLocal<ProblemRequestorState>();
496 private int fStateCount= 0;
497
498 private ICompilationUnit fCompilationUnit;
499 private final List<ProblemAnnotation> fGeneratedAnnotations= new ArrayList<ProblemAnnotation>();
500 private IProgressMonitor fProgressMonitor;
501 private boolean fIsActive= false;
502 private boolean fIsHandlingTemporaryProblems;
503
504 private ReverseMap fReverseMap= new ReverseMap();
505 private List<JavaMarkerAnnotation> fPreviouslyOverlaid= null;
506 private List<JavaMarkerAnnotation> fCurrentlyOverlaid= new ArrayList<JavaMarkerAnnotation>();
507 private Thread fActiveThread;
508
509
510 public CompilationUnitAnnotationModel(IResource resource) {
511 super(resource);
512 }
513
514 public void setCompilationUnit(ICompilationUnit unit) {
515 fCompilationUnit= unit;
516 }
517
518 @Override
519 protected MarkerAnnotation createMarkerAnnotation(IMarker marker) {
520 if (JavaMarkerAnnotation.isJavaAnnotation(marker))
521 return new JavaMarkerAnnotation(marker);
522 return super.createMarkerAnnotation(marker);
523 }
524
525 /*
526 * @see org.eclipse.jface.text.source.AnnotationModel#createAnnotationModelEvent()
527 */
528 @Override
529 protected AnnotationModelEvent createAnnotationModelEvent() {
530 return new CompilationUnitAnnotationModelEvent(this, getResource());
531 }
532
533 protected Position createPositionFromProblem(IProblem problem) {
534 int start= problem.getSourceStart();
535 int end= problem.getSourceEnd();
536
537 if (start == -1 && end == -1)
538 return new Position(0);
539
540 if (start == -1)
541 return new Position(end);
542
543 if (end == -1)
544 return new Position(start);
545
546 int length= end - start + 1;
547 if (length < 0)
548 return null;
549
550 return new Position(start, length);
551 }
552
553 /*
554 * @see IProblemRequestor#beginReporting()
555 */
556 public void beginReporting() {
557 ProblemRequestorState state= fProblemRequestorState.get();
558 if (state == null)
559 internalBeginReporting(false);
560 }
561
562 /*
563 * @see org.eclipse.jdt.internal.ui.text.java.IProblemRequestorExtension#beginReportingSequence()
564 */
565 public void beginReportingSequence() {
566 ProblemRequestorState state= fProblemRequestorState.get();
567 if (state == null)
568 internalBeginReporting(true);
569 }
570
571 /**
572 * Sets up the infrastructure necessary for problem reporting.
573 *
574 * @param insideReportingSequence <code>true</code> if this method
575 * call is issued from inside a reporting sequence
576 */
577 private void internalBeginReporting(boolean insideReportingSequence) {
578 if (fCompilationUnit != null && fCompilationUnit.getJavaProject().isOnClasspath(fCompilationUnit)) {
579 ProblemRequestorState state= new ProblemRequestorState();
580 state.fInsideReportingSequence= insideReportingSequence;
581 state.fReportedProblems= new ArrayList<IProblem>();
582 synchronized (getLockObject()) {
583 fProblemRequestorState.set(state);
584 ++fStateCount;
585 }
586 }
587 }
588
589 /*
590 * @see IProblemRequestor#acceptProblem(IProblem)
591 */
592 public void acceptProblem(IProblem problem) {
593 if (fIsHandlingTemporaryProblems || problem.getID() == JavaSpellingReconcileStrategy.SPELLING_PROBLEM_ID) {
594 ProblemRequestorState state= fProblemRequestorState.get();
595 if (state != null)
596 state.fReportedProblems.add(problem);
597 }
598 }
599
600 /*
601 * @see IProblemRequestor#endReporting()
602 */
603 public void endReporting() {
604 ProblemRequestorState state= fProblemRequestorState.get();
605 if (state != null && !state.fInsideReportingSequence)
606 internalEndReporting(state);
607 }
608
609 /*
610 * @see org.eclipse.jdt.internal.ui.text.java.IProblemRequestorExtension#endReportingSequence()
611 */
612 public void endReportingSequence() {
613 ProblemRequestorState state= fProblemRequestorState.get();
614 if (state != null && state.fInsideReportingSequence)
615 internalEndReporting(state);
616 }
617
618 private void internalEndReporting(ProblemRequestorState state) {
619 int stateCount= 0;
620 synchronized(getLockObject()) {
621 -- fStateCount;
622 stateCount= fStateCount;
623 fProblemRequestorState.set(null);
624 }
625
626 if (stateCount == 0)
627 reportProblems(state.fReportedProblems);
628 }
629
630 /**
631 * Signals the end of problem reporting.
632 *
633 * @param reportedProblems the problems to report
634 */
635 private void reportProblems(List<IProblem> reportedProblems) {
636 if (fProgressMonitor != null && fProgressMonitor.isCanceled())
637 return;
638
639 boolean temporaryProblemsChanged= false;
640
641 synchronized (getLockObject()) {
642
643 boolean isCanceled= false;
644
645 fPreviouslyOverlaid= fCurrentlyOverlaid;
646 fCurrentlyOverlaid= new ArrayList<JavaMarkerAnnotation>();
647
648 if (fGeneratedAnnotations.size() > 0) {
649 temporaryProblemsChanged= true;
650 removeAnnotations(fGeneratedAnnotations, false, true);
651 fGeneratedAnnotations.clear();
652 }
653
654 if (reportedProblems != null && reportedProblems.size() > 0) {
655
656 Iterator<IProblem> e= reportedProblems.iterator();
657 while (e.hasNext()) {
658
659 if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
660 isCanceled= true;
661 break;
662 }
663
664 IProblem problem= e.next();
665 Position position= createPositionFromProblem(problem);
666 if (position != null) {
667
668 try {
669 ProblemAnnotation annotation= new ProblemAnnotation(problem, fCompilationUnit);
670 overlayMarkers(position, annotation);
671 addAnnotation(annotation, position, false);
672 fGeneratedAnnotations.add(annotation);
673
674 temporaryProblemsChanged= true;
675 } catch (BadLocationException x) {
676 // ignore invalid position
677 }
678 }
679 }
680 }
681
682 removeMarkerOverlays(isCanceled);
683 fPreviouslyOverlaid= null;
684 }
685
686 if (temporaryProblemsChanged)
687 fireModelChanged();
688 }
689
690 private void removeMarkerOverlays(boolean isCanceled) {
691 if (isCanceled) {
692 fCurrentlyOverlaid.addAll(fPreviouslyOverlaid);
693 } else if (fPreviouslyOverlaid != null) {
694 Iterator<JavaMarkerAnnotation> e= fPreviouslyOverlaid.iterator();
695 while (e.hasNext()) {
696 JavaMarkerAnnotation annotation= e.next();
697 annotation.setOverlay(null);
698 }
699 }
700 }
701
702 /**
703 * Overlays value with problem annotation.
704 *
705 * @param value the value
706 * @param problemAnnotation the problem annotation
707 */
708 private void setOverlay(Object value, ProblemAnnotation problemAnnotation) {
709 if (value instanceof JavaMarkerAnnotation) {
710 JavaMarkerAnnotation annotation= (JavaMarkerAnnotation) value;
711 if (annotation.isProblem()) {
712 annotation.setOverlay(problemAnnotation);
713 fPreviouslyOverlaid.remove(annotation);
714 fCurrentlyOverlaid.add(annotation);
715 }
716 } else {
717 }
718 }
719
720 private void overlayMarkers(Position position, ProblemAnnotation problemAnnotation) {
721 Object value= getAnnotations(position);
722 if (value instanceof List) {
723 List<?> list= (List<?>) value;
724 for (Iterator<?> e = list.iterator(); e.hasNext();)
725 setOverlay(e.next(), problemAnnotation);
726 } else {
727 setOverlay(value, problemAnnotation);
728 }
729 }
730
731 /**
732 * Tells this annotation model to collect temporary problems from now on.
733 */
734 private void startCollectingProblems() {
735 fGeneratedAnnotations.clear();
736 }
737
738 /**
739 * Tells this annotation model to no longer collect temporary problems.
740 */
741 private void stopCollectingProblems() {
742 removeAnnotations(fGeneratedAnnotations, true, true);
743 fGeneratedAnnotations.clear();
744 }
745
746 /*
747 * @see IProblemRequestor#isActive()
748 */
749 public synchronized boolean isActive() {
750 return fIsActive && fActiveThread == Thread.currentThread();
751 }
752
753 /*
754 * @see IProblemRequestorExtension#setProgressMonitor(IProgressMonitor)
755 */
756 public void setProgressMonitor(IProgressMonitor monitor) {
757 fProgressMonitor= monitor;
758 }
759
760 /*
761 * @see IProblemRequestorExtension#setIsActive(boolean)
762 */
763 public synchronized void setIsActive(boolean isActive) {
764 Assert.isLegal(!isActive || Display.getCurrent() == null); // must not be enabled from UI threads
765 fIsActive= isActive;
766 if (fIsActive)
767 fActiveThread= Thread.currentThread();
768 else
769 fActiveThread= null;
770 }
771
772 /*
773 * @see IProblemRequestorExtension#setIsHandlingTemporaryProblems(boolean)
774 * @since 3.1
775 */
776 public void setIsHandlingTemporaryProblems(boolean enable) {
777 if (fIsHandlingTemporaryProblems != enable) {
778 fIsHandlingTemporaryProblems= enable;
779 if (fIsHandlingTemporaryProblems)
780 startCollectingProblems();
781 else
782 stopCollectingProblems();
783 }
784
785 }
786
787 private Object getAnnotations(Position position) {
788 synchronized (getLockObject()) {
789 return fReverseMap.get(position);
790 }
791 }
792
793 /*
794 * @see AnnotationModel#addAnnotation(Annotation, Position, boolean)
795 */
796 @Override
797 protected void addAnnotation(Annotation annotation, Position position, boolean fireModelChanged) throws BadLocationException {
798 super.addAnnotation(annotation, position, fireModelChanged);
799
800 synchronized (getLockObject()) {
801 Object cached= fReverseMap.get(position);
802 if (cached == null)
803 fReverseMap.put(position, annotation);
804 else if (cached instanceof List) {
805 @SuppressWarnings("unchecked")
806 List<Object> list= (List<Object>) cached;
807 list.add(annotation);
808 } else if (cached instanceof Annotation) {
809 List<Object> list= new ArrayList<Object>(2);
810 list.add(cached);
811 list.add(annotation);
812 fReverseMap.put(position, list);
813 }
814 }
815 }
816
817 /*
818 * @see AnnotationModel#removeAllAnnotations(boolean)
819 */
820 @Override
821 protected void removeAllAnnotations(boolean fireModelChanged) {
822 super.removeAllAnnotations(fireModelChanged);
823 synchronized (getLockObject()) {
824 fReverseMap.clear();
825 }
826 }
827
828 /*
829 * @see AnnotationModel#removeAnnotation(Annotation, boolean)
830 */
831 @Override
832 protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
833 Position position= getPosition(annotation);
834 synchronized (getLockObject()) {
835 Object cached= fReverseMap.get(position);
836 if (cached instanceof List) {
837 @SuppressWarnings("unchecked")
838 List<Object> list= (List<Object>) cached;
839 list.remove(annotation);
840 if (list.size() == 1) {
841 fReverseMap.put(position, list.get(0));
842 list.clear();
843 }
844 } else if (cached instanceof Annotation) {
845 fReverseMap.remove(position);
846 }
847 }
848 super.removeAnnotation(annotation, fireModelChanged);
849 }
850 }
851
852
853 protected static class GlobalAnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
854
855 private ListenerList fListenerList;
856
857 public GlobalAnnotationModelListener() {
858 fListenerList= new ListenerList(ListenerList.IDENTITY);
859 }
860
861 /**
862 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
863 */
864 public void modelChanged(IAnnotationModel model) {
865 Object[] listeners= fListenerList.getListeners();
866 for (int i= 0; i < listeners.length; i++) {
867 ((IAnnotationModelListener) listeners[i]).modelChanged(model);
868 }
869 }
870
871 /**
872 * @see IAnnotationModelListenerExtension#modelChanged(AnnotationModelEvent)
873 */
874 public void modelChanged(AnnotationModelEvent event) {
875 Object[] listeners= fListenerList.getListeners();
876 for (int i= 0; i < listeners.length; i++) {
877 Object curr= listeners[i];
878 if (curr instanceof IAnnotationModelListenerExtension) {
879 ((IAnnotationModelListenerExtension) curr).modelChanged(event);
880 }
881 }
882 }
883
884 public void addListener(IAnnotationModelListener listener) {
885 fListenerList.add(listener);
886 }
887
888 public void removeListener(IAnnotationModelListener listener) {
889 fListenerList.remove(listener);
890 }
891 }
892
893
894
895 /** Preference key for temporary problems */
896 private final static String HANDLE_TEMPORARY_PROBLEMS= PreferenceConstants.EDITOR_EVALUTE_TEMPORARY_PROBLEMS;
897
898
899 /** Indicates whether the save has been initialized by this provider */
900 private boolean fIsAboutToSave= false;
901 /** The save policy used by this provider */
902 private ISavePolicy fSavePolicy;
903 /** Internal property changed listener */
904 private IPropertyChangeListener fPropertyListener;
905 /** Annotation model listener added to all created CU annotation models */
906 private GlobalAnnotationModelListener fGlobalAnnotationModelListener;
907 /**
908 * Element information of all connected elements with a fake CU but no file info.
909 * @since 3.2
910 */
911 private final Map<Object, CompilationUnitInfo> fFakeCUMapForMissingInfo= new HashMap<Object, CompilationUnitInfo>();
912
913
914 /**
915 * Constructor
916 */
917 public CompilationUnitDocumentProvider() {
918
919 IDocumentProvider provider= new TextFileDocumentProvider();
920 provider= new ForwardingDocumentProvider(IJavaPartitions.JAVA_PARTITIONING, new JavaDocumentSetupParticipant(), provider);
921 setParentDocumentProvider(provider);
922
923 fGlobalAnnotationModelListener= new GlobalAnnotationModelListener();
924 fPropertyListener= new IPropertyChangeListener() {
925 public void propertyChange(PropertyChangeEvent event) {
926 if (HANDLE_TEMPORARY_PROBLEMS.equals(event.getProperty()))
927 enableHandlingTemporaryProblems();
928 }
929 };
930 JavaPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(fPropertyListener);
931 }
932
933 /**
934 * Creates a compilation unit from the given file.
935 *
936 * @param file the file from which to create the compilation unit
937 * @return the fake compilation unit
938 */
939 protected ICompilationUnit createCompilationUnit(IFile file) {
940 Object element= JavaCore.create(file);
941 if (element instanceof ICompilationUnit)
942 return (ICompilationUnit) element;
943 return null;
944 }
945
946 /*
947 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#createEmptyFileInfo()
948 */
949 @Override
950 protected FileInfo createEmptyFileInfo() {
951 return new CompilationUnitInfo();
952 }
953
954 /*
955 * @see org.eclipse.core.filebuffers.IAnnotationModelFactory#createAnnotationModel(org.eclipse.core.runtime.IPath)
956 * @since 3.4
957 */
958 public IAnnotationModel createAnnotationModel(IPath path) {
959 IResource file= ResourcesPlugin.getWorkspace().getRoot().findMember(path);
960 if (file instanceof IFile)
961 return new CompilationUnitAnnotationModel(file);
962 return new AnnotationModel();
963 }
964
965 /*
966 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#createFileInfo(java.lang.Object)
967 */
968 @Override
969 protected FileInfo createFileInfo(Object element) throws CoreException {
970 ICompilationUnit original= null;
971 if (element instanceof IFileEditorInput) {
972 IFileEditorInput input= (IFileEditorInput) element;
973 original= createCompilationUnit(input.getFile());
974 if (original == null)
975 return null;
976 }
977
978 FileInfo info= super.createFileInfo(element);
979 if (!(info instanceof CompilationUnitInfo))
980 return null;
981
982 if (original == null)
983 original= createFakeCompiltationUnit(element, false);
984 if (original == null)
985 return null;
986
987 CompilationUnitInfo cuInfo= (CompilationUnitInfo) info;
988 setUpSynchronization(cuInfo);
989
990 IProblemRequestor requestor= cuInfo.fModel instanceof IProblemRequestor ? (IProblemRequestor) cuInfo.fModel : null;
991 if (requestor instanceof IProblemRequestorExtension) {
992 IProblemRequestorExtension extension= (IProblemRequestorExtension) requestor;
993 extension.setIsActive(false);
994 extension.setIsHandlingTemporaryProblems(isHandlingTemporaryProblems());
995 }
996
997 IResource resource= original.getResource();
998 if (JavaModelUtil.isPrimary(original) && (resource == null || resource.exists()))
999 original.becomeWorkingCopy(requestor, getProgressMonitor());
1000 cuInfo.fCopy= original;
1001
1002 if (cuInfo.fModel instanceof CompilationUnitAnnotationModel) {
1003 CompilationUnitAnnotationModel model= (CompilationUnitAnnotationModel) cuInfo.fModel;
1004 model.setCompilationUnit(cuInfo.fCopy);
1005 }
1006
1007 if (cuInfo.fModel != null)
1008 cuInfo.fModel.addAnnotationModelListener(fGlobalAnnotationModelListener);
1009
1010 return cuInfo;
1011 }
1012
1013 /**
1014 * Creates a fake compilation unit.
1015 *
1016 * @param element the element
1017 * @param setContents tells whether to read and set the contents to the new CU
1018 * @return the fake compilation unit
1019 * @since 3.2
1020 */
1021 private ICompilationUnit createFakeCompiltationUnit(Object element, boolean setContents) {
1022 if (element instanceof IStorageEditorInput)
1023 return createFakeCompiltationUnit((IStorageEditorInput)element, setContents);
1024 else if (element instanceof IURIEditorInput)
1025 return createFakeCompiltationUnit((IURIEditorInput)element);
1026 return null;
1027 }
1028
1029 /**
1030 * Creates a fake compilation unit.
1031 *
1032 * @param editorInput the storage editor input
1033 * @param setContents tells whether to read and set the contents to the new CU
1034 * @return the fake compilation unit
1035 * @since 3.2
1036 */
1037 private ICompilationUnit createFakeCompiltationUnit(IStorageEditorInput editorInput, boolean setContents) {
1038 try {
1039 final IStorage storage= editorInput.getStorage();
1040 final IPath storagePath= storage.getFullPath();
1041 if (storage.getName() == null || storagePath == null)
1042 return null;
1043
1044 final IPath documentPath;
1045 if (storage instanceof IFileState)
1046 documentPath= storagePath.append(Long.toString(((IFileState)storage).getModificationTime()));
1047 else if (isFileRevisionEditorInput(editorInput))
1048 documentPath= storagePath.append(Long.toString(System.currentTimeMillis()));
1049 else
1050 documentPath= storagePath;
1051
1052 WorkingCopyOwner woc= new WorkingCopyOwner() {
1053 /*
1054 * @see org.eclipse.jdt.core.WorkingCopyOwner#createBuffer(org.eclipse.jdt.core.ICompilationUnit)
1055 * @since 3.2
1056 */
1057 @Override
1058 public IBuffer createBuffer(ICompilationUnit workingCopy) {
1059 return new DocumentAdapter(workingCopy, documentPath);
1060 }
1061 };
1062
1063 IClasspathEntry[] cpEntries= null;
1064 IJavaProject jp= findJavaProject(storagePath);
1065 if (jp != null)
1066 cpEntries= jp.getResolvedClasspath(true);
1067
1068 if (cpEntries == null || cpEntries.length == 0)
1069 cpEntries= new IClasspathEntry[] { JavaRuntime.getDefaultJREContainerEntry() };
1070
1071 final ICompilationUnit cu= woc.newWorkingCopy(storage.getName(), cpEntries, getProgressMonitor());
1072 if (setContents) {
1073 int READER_CHUNK_SIZE= 2048;
1074 int BUFFER_SIZE= 8 * READER_CHUNK_SIZE;
1075
1076 String charsetName= null;
1077 if (storage instanceof IEncodedStorage)
1078 charsetName= ((IEncodedStorage)storage).getCharset();
1079 if (charsetName == null)
1080 charsetName= getDefaultEncoding();
1081
1082 Reader in= null;
1083 InputStream contents= storage.getContents();
1084 try {
1085 in= new BufferedReader(new InputStreamReader(contents, charsetName));
1086 StringBuffer buffer= new StringBuffer(BUFFER_SIZE);
1087 char[] readBuffer= new char[READER_CHUNK_SIZE];
1088 int n;
1089 n= in.read(readBuffer);
1090 while (n > 0) {
1091 buffer.append(readBuffer, 0, n);
1092 n= in.read(readBuffer);
1093 }
1094 cu.getBuffer().setContents(buffer.toString());
1095 } catch (IOException e) {
1096 JavaPlugin.log(e);
1097 return null;
1098 } finally {
1099 try {
1100 if (in != null)
1101 in.close();
1102 else
1103 contents.close();
1104 } catch (IOException x) {
1105 }
1106 }
1107 }
1108
1109 if (!isModifiable(editorInput))
1110 JavaModelUtil.reconcile(cu);
1111
1112 return cu;
1113 } catch (CoreException ex) {
1114 JavaPlugin.log(ex.getStatus());
1115 return null;
1116 }
1117 }
1118
1119 /**
1120 * Tests whether the given editor input is an instance of
1121 * <code>org.eclipse.team.internal.ui.history.FileRevisionEditorInput</code>.
1122 * <p>
1123 * XXX: Workaround for https://bugs.eclipse.org/307756, see comment 2 on how a better solution
1124 * could look like.
1125 * </p>
1126 *
1127 * @param editorInput the editor input to test
1128 * @return <code>true</code> if it is an instance of
1129 * <code>org.eclipse.team.internal.ui.history.FileRevisionEditorInput</code>
1130 * @since 3.6
1131 */
1132 private static boolean isFileRevisionEditorInput(IEditorInput editorInput) {
1133 try {
1134 return Class.forName("org.eclipse.team.internal.ui.history.FileRevisionEditorInput").isInstance(editorInput); //$NON-NLS-1$
1135 } catch (ClassNotFoundException ex) {
1136 return false;
1137 }
1138 }
1139
1140 /**
1141 * Creates a fake compilation unit.
1142 *
1143 * @param editorInput the URI editor input
1144 * @return the fake compilation unit
1145 * @since 3.3
1146 */
1147 private ICompilationUnit createFakeCompiltationUnit(IURIEditorInput editorInput) {
1148 try {
1149 final URI uri= editorInput.getURI();
1150 final IFileStore fileStore= EFS.getStore(uri);
1151 final IPath path= URIUtil.toPath(uri);
1152 String fileStoreName= fileStore.getName();
1153 if (fileStoreName == null || path == null)
1154 return null;
1155
1156 WorkingCopyOwner woc= new WorkingCopyOwner() {
1157 /*
1158 * @see org.eclipse.jdt.core.WorkingCopyOwner#createBuffer(org.eclipse.jdt.core.ICompilationUnit)
1159 * @since 3.2
1160 */
1161 @Override
1162 public IBuffer createBuffer(ICompilationUnit workingCopy) {
1163 return new DocumentAdapter(workingCopy, fileStore, path);
1164 }
1165 };
1166
1167 IClasspathEntry[] cpEntries= null;
1168 IJavaProject jp= findJavaProject(path);
1169 if (jp != null)
1170 cpEntries= jp.getResolvedClasspath(true);
1171
1172 if (cpEntries == null || cpEntries.length == 0)
1173 cpEntries= new IClasspathEntry[] { JavaRuntime.getDefaultJREContainerEntry() };
1174
1175 final ICompilationUnit cu= woc.newWorkingCopy(fileStoreName, cpEntries, getProgressMonitor());
1176
1177 if (!isModifiable(editorInput))
1178 JavaModelUtil.reconcile(cu);
1179
1180 return cu;
1181 } catch (CoreException ex) {
1182 return null;
1183 }
1184 }
1185
1186 /**
1187 * Fuzzy search for Java project in the workspace that matches
1188 * the given path.
1189 *
1190 * @param path the path to match
1191 * @return the matching Java project or <code>null</code>
1192 * @since 3.2
1193 */
1194 private IJavaProject findJavaProject(IPath path) {
1195 if (path == null)
1196 return null;
1197
1198 String[] pathSegments= path.segments();
1199 IJavaModel model= JavaCore.create(JavaPlugin.getWorkspace().getRoot());
1200 IJavaProject[] projects;
1201 try {
1202 projects= model.getJavaProjects();
1203 } catch (JavaModelException e) {
1204 return null; // ignore - use default JRE
1205 }
1206 for (int i= 0; i < projects.length; i++) {
1207 IPath projectPath= projects[i].getProject().getFullPath();
1208 String projectSegment= projectPath.segments()[0];
1209 for (int j= 0; j < pathSegments.length; j++)
1210 if (projectSegment.equals(pathSegments[j]))
1211 return projects[i];
1212 }
1213 return null;
1214 }
1215
1216 /*
1217 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#disposeFileInfo(java.lang.Object, org.eclipse.ui.editors.text.TextFileDocumentProvider.FileInfo)
1218 */
1219 @Override
1220 protected void disposeFileInfo(Object element, FileInfo info) {
1221 if (info instanceof CompilationUnitInfo) {
1222 CompilationUnitInfo cuInfo= (CompilationUnitInfo) info;
1223
1224 try {
1225 cuInfo.fCopy.discardWorkingCopy();
1226 } catch (JavaModelException x) {
1227 handleCoreException(x, x.getMessage());
1228 }
1229
1230 if (cuInfo.fModel != null)
1231 cuInfo.fModel.removeAnnotationModelListener(fGlobalAnnotationModelListener);
1232 }
1233 super.disposeFileInfo(element, info);
1234 }
1235
1236 /*
1237 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#connect(java.lang.Object)
1238 * @since 3.2
1239 */
1240 @Override
1241 public void connect(Object element) throws CoreException {
1242 super.connect(element);
1243 if (getFileInfo(element) != null)
1244 return;
1245
1246 CompilationUnitInfo info= fFakeCUMapForMissingInfo.get(element);
1247 if (info == null) {
1248 ICompilationUnit cu= createFakeCompiltationUnit(element, true);
1249 if (cu == null)
1250 return;
1251 info= new CompilationUnitInfo();
1252 info.fCopy= cu;
1253 info.fElement= element;
1254 info.fModel= new AnnotationModel();
1255 fFakeCUMapForMissingInfo.put(element, info);
1256 }
1257 info.fCount++;
1258 }
1259
1260 /*
1261 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#getAnnotationModel(java.lang.Object)
1262 * @since 3.2
1263 */
1264 @Override
1265 public IAnnotationModel getAnnotationModel(Object element) {
1266 IAnnotationModel model= super.getAnnotationModel(element);
1267 if (model != null)
1268 return model;
1269
1270 FileInfo info= fFakeCUMapForMissingInfo.get(element);
1271 if (info != null) {
1272 if (info.fModel != null)
1273 return info.fModel;
1274 if (info.fTextFileBuffer != null)
1275 return info.fTextFileBuffer.getAnnotationModel();
1276 }
1277
1278 return null;
1279 }
1280
1281 /*
1282 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#disconnect(java.lang.Object)
1283 * @since 3.2
1284 */
1285 @Override
1286 public void disconnect(Object element) {
1287 CompilationUnitInfo info= fFakeCUMapForMissingInfo.get(element);
1288 if (info != null) {
1289 if (info.fCount == 1) {
1290 fFakeCUMapForMissingInfo.remove(element);
1291 info.fModel= null;
1292 // Destroy and unregister fake working copy
1293 try {
1294 info.fCopy.discardWorkingCopy();
1295 } catch (JavaModelException ex) {
1296 handleCoreException(ex, ex.getMessage());
1297 }
1298 } else
1299 info.fCount--;
1300 }
1301 super.disconnect(element);
1302 }
1303
1304
1305 /**
1306 * Creates and returns a new sub-progress monitor for the
1307 * given parent monitor.
1308 *
1309 * @param monitor the parent progress monitor
1310 * @param ticks the number of work ticks allocated from the parent monitor
1311 * @return the new sub-progress monitor
1312 */
1313 private IProgressMonitor getSubProgressMonitor(IProgressMonitor monitor, int ticks) {
1314 if (monitor != null)
1315 return new SubProgressMonitor(monitor, ticks, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
1316
1317 return new NullProgressMonitor();
1318 }
1319
1320 protected void commitWorkingCopy(IProgressMonitor monitor, Object element, final CompilationUnitInfo info, boolean overwrite) throws CoreException {
1321
1322 if (monitor == null)
1323 monitor= new NullProgressMonitor();
1324
1325 monitor.beginTask("", 100); //$NON-NLS-1$
1326
1327 try {
1328 IDocument document= info.fTextFileBuffer.getDocument();
1329 IResource resource= info.fCopy.getResource();
1330
1331 Assert.isTrue(resource instanceof IFile);
1332
1333 boolean isSynchronized= resource.isSynchronized(IResource.DEPTH_ZERO);
1334
1335 /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=98327
1336 * Make sure file gets save in commit() if the underlying file has been deleted */
1337 if (!isSynchronized && isDeleted(element))
1338 info.fTextFileBuffer.setDirty(true);
1339
1340 if (!resource.exists()) {
1341 // underlying resource has been deleted, just recreate file, ignore the rest
1342 createFileFromDocument(monitor, (IFile) resource, document);
1343 return;
1344 }
1345
1346 if (fSavePolicy != null)
1347 fSavePolicy.preSave(info.fCopy);
1348
1349 IProgressMonitor subMonitor= null;
1350 try {
1351 fIsAboutToSave= true;
1352
1353 IPostSaveListener[] listeners= JavaPlugin.getDefault().getSaveParticipantRegistry().getEnabledPostSaveListeners(info.fCopy.getJavaProject().getProject());
1354
1355 CoreException changedRegionException= null;
1356 boolean needsChangedRegions= false;
1357 try {
1358 if (listeners.length > 0)
1359 needsChangedRegions= SaveParticipantRegistry.isChangedRegionsRequired(info.fCopy);
1360 } catch (CoreException ex) {
1361 changedRegionException= ex;
1362 }
1363
1364 IRegion[] changedRegions= null;
1365 if (needsChangedRegions) {
1366 try {
1367 changedRegions= EditorUtility.calculateChangedLineRegions(info.fTextFileBuffer, getSubProgressMonitor(monitor, 20));
1368 } catch (CoreException ex) {
1369 changedRegionException= ex;
1370 } finally {
1371 subMonitor= getSubProgressMonitor(monitor, 50);
1372 }
1373 } else
1374 subMonitor= getSubProgressMonitor(monitor, listeners.length > 0 ? 70 : 100);
1375
1376 info.fCopy.commitWorkingCopy(isSynchronized || overwrite, subMonitor);
1377 if (listeners.length > 0)
1378 notifyPostSaveListeners(info, changedRegions, listeners, getSubProgressMonitor(monitor, 30));
1379
1380 if (changedRegionException != null) {
1381 throw changedRegionException;
1382 }
1383 } catch (CoreException x) {
1384 // inform about the failure
1385 fireElementStateChangeFailed(element);
1386 throw x;
1387 } catch (RuntimeException x) {
1388 // inform about the failure
1389 fireElementStateChangeFailed(element);
1390 throw x;
1391 } finally {
1392 fIsAboutToSave= false;
1393 if (subMonitor != null)
1394 subMonitor.done();
1395 }
1396
1397 // If here, the dirty state of the editor will change to "not dirty".
1398 // Thus, the state changing flag will be reset.
1399 if (info.fModel instanceof AbstractMarkerAnnotationModel) {
1400 AbstractMarkerAnnotationModel model= (AbstractMarkerAnnotationModel) info.fModel;
1401 model.updateMarkers(document);
1402 }
1403
1404 if (fSavePolicy != null) {
1405 ICompilationUnit unit= fSavePolicy.postSave(info.fCopy);
1406 if (unit != null && info.fModel instanceof AbstractMarkerAnnotationModel) {
1407 IResource r= unit.getResource();
1408 IMarker[] markers= r.findMarkers(IMarker.MARKER, true, IResource.DEPTH_ZERO);
1409 if (markers != null && markers.length > 0) {
1410 AbstractMarkerAnnotationModel model= (AbstractMarkerAnnotationModel) info.fModel;
1411 for (int i= 0; i < markers.length; i++)
1412 model.updateMarker(document, markers[i], null);
1413 }
1414 }
1415 }
1416 } finally {
1417 monitor.done();
1418 }
1419 }
1420
1421 /*
1422 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider#createSaveOperation(java.lang.Object, org.eclipse.jface.text.IDocument, boolean)
1423 */
1424 @Override
1425 protected DocumentProviderOperation createSaveOperation(final Object element, final IDocument document, final boolean overwrite) throws CoreException {
1426 final FileInfo info= getFileInfo(element);
1427 if (info instanceof CompilationUnitInfo) {
1428
1429 // Delegate handling of non-primary CUs
1430 ICompilationUnit cu= ((CompilationUnitInfo)info).fCopy;
1431 if (cu != null && !JavaModelUtil.isPrimary(cu))
1432 return super.createSaveOperation(element, document, overwrite);
1433
1434 if (info.fTextFileBuffer.getDocument() != document) {
1435 // the info exists, but not for the given document
1436 // -> saveAs was executed with a target that is already open
1437 // in another editor
1438 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=85519
1439 Status status= new Status(IStatus.WARNING, EditorsUI.PLUGIN_ID, IStatus.ERROR, JavaEditorMessages.CompilationUnitDocumentProvider_saveAsTargetOpenInEditor, null);
1440 throw new CoreException(status);
1441 }
1442
1443 return new DocumentProviderOperation() {
1444 /*
1445 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider.DocumentProviderOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
1446 */
1447 @Override
1448 protected void execute(IProgressMonitor monitor) throws CoreException {
1449 commitWorkingCopy(monitor, element, (CompilationUnitInfo) info, overwrite);
1450 }
1451 /*
1452 * @see org.eclipse.ui.editors.text.TextFileDocumentProvider.DocumentProviderOperation#getSchedulingRule()
1453 */
1454 @Override
1455 public ISchedulingRule getSchedulingRule() {
1456 if (info.fElement instanceof IFileEditorInput) {
1457 IFile file= ((IFileEditorInput) info.fElement).getFile();
1458 return computeSchedulingRule(file);
1459 } else
1460 return null;
1461 }
1462 };
1463 }
1464
1465 return null;
1466 }
1467
1468 /**
1469 * Returns the preference whether handling temporary problems is enabled.
1470 *
1471 * @return <code>true</code> if temporary problems are handled
1472 */
1473 protected boolean isHandlingTemporaryProblems() {
1474 IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
1475 return store.getBoolean(HANDLE_TEMPORARY_PROBLEMS);
1476 }
1477
1478 /**
1479 * Switches the state of problem acceptance according to the value in the preference store.
1480 */
1481 protected void enableHandlingTemporaryProblems() {
1482 boolean enable= isHandlingTemporaryProblems();
1483 for (Iterator<FileInfo> iter= getFileInfosIterator(); iter.hasNext();) {
1484 FileInfo info= iter.next();
1485 if (info.fModel instanceof IProblemRequestorExtension) {
1486 IProblemRequestorExtension extension= (IProblemRequestorExtension) info.fModel;
1487 extension.setIsHandlingTemporaryProblems(enable);
1488 }
1489 }
1490 }
1491
1492 /*
1493 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#setSavePolicy(org.eclipse.jdt.internal.ui.javaeditor.ISavePolicy)
1494 */
1495 public void setSavePolicy(ISavePolicy savePolicy) {
1496 fSavePolicy= savePolicy;
1497 }
1498
1499 /*
1500 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#addGlobalAnnotationModelListener(org.eclipse.jface.text.source.IAnnotationModelListener)
1501 */
1502 public void addGlobalAnnotationModelListener(IAnnotationModelListener listener) {
1503 fGlobalAnnotationModelListener.addListener(listener);
1504 }
1505
1506 /*
1507 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#removeGlobalAnnotationModelListener(org.eclipse.jface.text.source.IAnnotationModelListener)
1508 */
1509 public void removeGlobalAnnotationModelListener(IAnnotationModelListener listener) {
1510 fGlobalAnnotationModelListener.removeListener(listener);
1511 }
1512
1513 /*
1514 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#getWorkingCopy(java.lang.Object)
1515 */
1516 public ICompilationUnit getWorkingCopy(Object element) {
1517 FileInfo fileInfo= getFileInfo(element);
1518 if (fileInfo instanceof CompilationUnitInfo) {
1519 CompilationUnitInfo info= (CompilationUnitInfo)fileInfo;
1520 return info.fCopy;
1521 }
1522 CompilationUnitInfo cuInfo= fFakeCUMapForMissingInfo.get(element);
1523 if (cuInfo != null)
1524 return cuInfo.fCopy;
1525
1526 return null;
1527 }
1528
1529 /*
1530 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#shutdown()
1531 */
1532 public void shutdown() {
1533 JavaPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyListener);
1534 Iterator<?> e= getConnectedElementsIterator();
1535 while (e.hasNext())
1536 disconnect(e.next());
1537 fFakeCUMapForMissingInfo.clear();
1538 }
1539
1540 /*
1541 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#saveDocumentContent(org.eclipse.core.runtime.IProgressMonitor, java.lang.Object, org.eclipse.jface.text.IDocument, boolean)
1542 */
1543 public void saveDocumentContent(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException {
1544 if (!fIsAboutToSave)
1545 return;
1546 super.saveDocument(monitor, element, document, overwrite);
1547 }
1548
1549 /*
1550 * @see org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider#createLineTracker(java.lang.Object)
1551 */
1552 public ILineTracker createLineTracker(Object element) {
1553 return new DefaultLineTracker();
1554 }
1555
1556 /**
1557 * Notify post save listeners.
1558 * <p>
1559 * <strong>Note:</strong> Post save listeners are not allowed to save the file and they must not
1560 * assumed to be called in the UI thread i.e. if they open a dialog they must ensure it ends up
1561 * in the UI thread.
1562 * </p>
1563 *
1564 * @param info compilation unit info
1565 * @param changedRegions the array with the changed regions
1566 * @param listeners the listeners to notify
1567 * @param monitor the progress monitor
1568 * @throws CoreException if something goes wrong
1569 * @see IPostSaveListener
1570 * @since 3.3
1571 */
1572 protected void notifyPostSaveListeners(final CompilationUnitInfo info, final IRegion[] changedRegions, IPostSaveListener[] listeners, final IProgressMonitor monitor) throws CoreException {
1573 final ICompilationUnit unit= info.fCopy;
1574 final IBuffer buffer= unit.getBuffer();
1575
1576 String message= JavaEditorMessages.CompilationUnitDocumentProvider_error_saveParticipantProblem;
1577 final MultiStatus errorStatus= new MultiStatus(JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION, message, null);
1578
1579 monitor.beginTask(JavaEditorMessages.CompilationUnitDocumentProvider_progressNotifyingSaveParticipants, listeners.length * 5);
1580 try {
1581 for (int i= 0; i < listeners.length; i++) {
1582 final IPostSaveListener listener= listeners[i];
1583 final String participantName= listener.getName();
1584 SafeRunner.run(new ISafeRunnable() {
1585 public void run() {
1586 try {
1587 long stamp= unit.getResource().getModificationStamp();
1588
1589 listener.saved(unit, changedRegions, getSubProgressMonitor(monitor, 4));
1590
1591 if (stamp != unit.getResource().getModificationStamp()) {
1592 String msg= Messages.format(JavaEditorMessages.CompilationUnitDocumentProvider_error_saveParticipantSavedFile, participantName);
1593 errorStatus.add(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION, msg, null));
1594 }
1595
1596 if (buffer.hasUnsavedChanges())
1597 buffer.save(getSubProgressMonitor(monitor, 1), true);
1598
1599 } catch (CoreException ex) {
1600 handleException(ex);
1601 } finally {
1602 monitor.worked(1);
1603 }
1604 }
1605
1606 public void handleException(Throwable ex) {
1607 String msg= Messages.format("The save participant ''{0}'' caused an exception: {1}", new String[] { listener.getId(), ex.toString()}); //$NON-NLS-1$
1608 JavaPlugin.log(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION, msg, ex));
1609
1610 msg= Messages.format(JavaEditorMessages.CompilationUnitDocumentProvider_error_saveParticipantFailed, new String[] { participantName, ex.toString()});
1611 errorStatus.add(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION, msg, null));
1612
1613 // Revert the changes
1614 if (buffer.hasUnsavedChanges()) {
1615 try {
1616 info.fTextFileBuffer.revert(getSubProgressMonitor(monitor, 1));
1617 } catch (CoreException e) {
1618 msg= Messages.format("Error on revert after failure of save participant ''{0}''.", participantName); //$NON-NLS-1$
1619 IStatus status= new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION, msg, ex);
1620 JavaPlugin.getDefault().getLog().log(status);
1621 }
1622
1623 if (info.fModel instanceof AbstractMarkerAnnotationModel) {
1624 AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel)info.fModel;
1625 markerModel.resetMarkers();
1626 }
1627 }
1628
1629 // XXX: Work in progress 'Save As' case
1630// else if (buffer.hasUnsavedChanges()) {
1631// try {
1632// buffer.save(getSubProgressMonitor(monitor, 1), true);
1633// } catch (JavaModelException e) {
1634// message= Messages.format("Error reverting changes after failure of save participant ''{0}''.", participantName); //$NON-NLS-1$
1635// IStatus status= new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IStatus.OK, message, ex);
1636// JavaPlugin.getDefault().getLog().log(status);
1637// }
1638// }
1639 }
1640 });
1641 }
1642 } finally {
1643 monitor.done();
1644 if (!errorStatus.isOK())
1645 throw new CoreException(errorStatus);
1646 }
1647 }
1648}