new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPDocumentProvider.java
1 package net.sourceforge.phpeclipse.phpeditor;
2
3 /**********************************************************************
4 Copyright (c) 2000, 2002 IBM Corp. and others.
5 All rights reserved. This program and the accompanying materials
6 are made available under the terms of the Common Public License v1.0
7 which accompanies this distribution, and is available at
8 http://www.eclipse.org/legal/cpl-v10.html
9
10 Contributors:
11     IBM Corporation - Initial implementation
12     Klaus Hartlage - www.eclipseproject.de
13 **********************************************************************/
14
15 import java.io.ByteArrayInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21
22 import net.sourceforge.phpdt.core.IBuffer;
23 import net.sourceforge.phpdt.core.IBufferFactory;
24 import net.sourceforge.phpdt.core.ICompilationUnit;
25 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
26 import net.sourceforge.phpdt.core.IOpenable;
27 import net.sourceforge.phpdt.core.IProblemRequestor;
28 import net.sourceforge.phpdt.core.JavaModelException;
29 import net.sourceforge.phpdt.core.compiler.IProblem;
30 import net.sourceforge.phpdt.internal.ui.PHPStatusConstants;
31 import net.sourceforge.phpdt.internal.ui.PHPUIStatus;
32 import net.sourceforge.phpdt.internal.ui.text.java.IProblemRequestorExtension;
33 import net.sourceforge.phpdt.ui.PreferenceConstants;
34 import net.sourceforge.phpdt.ui.text.JavaTextTools;
35 import net.sourceforge.phpeclipse.PHPCore;
36 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
37
38 import org.eclipse.core.resources.IFile;
39 import org.eclipse.core.resources.IMarker;
40 import org.eclipse.core.resources.IMarkerDelta;
41 import org.eclipse.core.resources.IResource;
42 import org.eclipse.core.resources.ResourcesPlugin;
43 import org.eclipse.core.runtime.CoreException;
44 import org.eclipse.core.runtime.IProgressMonitor;
45 import org.eclipse.core.runtime.IStatus;
46 import org.eclipse.core.runtime.Status;
47 import org.eclipse.jface.preference.IPreferenceStore;
48 import org.eclipse.jface.text.BadLocationException;
49 import org.eclipse.jface.text.DefaultLineTracker;
50 import org.eclipse.jface.text.Document;
51 import org.eclipse.jface.text.IDocument;
52 import org.eclipse.jface.text.IDocumentPartitioner;
53 import org.eclipse.jface.text.ILineTracker;
54 import org.eclipse.jface.text.Position;
55 import org.eclipse.jface.text.source.Annotation;
56 import org.eclipse.jface.text.source.AnnotationModelEvent;
57 import org.eclipse.jface.text.source.IAnnotationModel;
58 import org.eclipse.jface.text.source.IAnnotationModelListener;
59 import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
60 import org.eclipse.jface.util.IPropertyChangeListener;
61 import org.eclipse.jface.util.ListenerList;
62 import org.eclipse.jface.util.PropertyChangeEvent;
63 import org.eclipse.swt.SWT;
64 import org.eclipse.swt.graphics.GC;
65 import org.eclipse.swt.graphics.Image;
66 import org.eclipse.swt.graphics.Rectangle;
67 import org.eclipse.swt.widgets.Canvas;
68 import org.eclipse.swt.widgets.Display;
69 import org.eclipse.ui.IEditorInput;
70 import org.eclipse.ui.IFileEditorInput;
71 import org.eclipse.ui.editors.text.FileDocumentProvider;
72 import org.eclipse.ui.part.FileEditorInput;
73 import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
74 import org.eclipse.ui.texteditor.MarkerAnnotation;
75 import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
76
77 /** 
78  * The PHPDocumentProvider provides the IDocuments used by java editors.
79  */
80
81 public class PHPDocumentProvider extends FileDocumentProvider {
82   /**
83                          * Here for visibility issues only.
84                          */
85   protected class _FileSynchronizer extends FileSynchronizer {
86     public _FileSynchronizer(IFileEditorInput fileEditorInput) {
87       super(fileEditorInput);
88     }
89   };
90   /**
91                  * Bundle of all required informations to allow working copy management. 
92                  */
93   protected class CompilationUnitInfo extends FileInfo {
94
95     ICompilationUnit fCopy;
96
97     public CompilationUnitInfo(
98       IDocument document,
99       IAnnotationModel model,
100       _FileSynchronizer fileSynchronizer,
101       ICompilationUnit copy) {
102       super(document, model, fileSynchronizer);
103       fCopy = copy;
104     }
105
106     public void setModificationStamp(long timeStamp) {
107       fModificationStamp = timeStamp;
108     }
109   };
110   /**
111                  * Annotation model dealing with java marker annotations and temporary problems.
112                  * Also acts as problem requestor for its compilation unit. Initialiy inactive. Must explicitly be
113                  * activated.
114                  */
115   protected class CompilationUnitAnnotationModel
116     extends ResourceMarkerAnnotationModel
117     implements IProblemRequestor, IProblemRequestorExtension {
118     private List fCollectedProblems;
119     private List fCurrentlyOverlaid = new ArrayList();
120     private List fGeneratedAnnotations;
121
122     private IFileEditorInput fInput;
123     private boolean fIsActive = false;
124     private List fPreviouslyOverlaid = null;
125     private IProgressMonitor fProgressMonitor;
126
127     private ReverseMap fReverseMap = new ReverseMap();
128
129     public CompilationUnitAnnotationModel(IFileEditorInput input) {
130       super(input.getFile());
131       fInput = input;
132     }
133
134     /*
135      * @see IProblemRequestor#acceptProblem(IProblem)
136      */
137     public void acceptProblem(IProblem problem) {
138       if (isActive())
139         fCollectedProblems.add(problem);
140     }
141
142     /*
143      * @see AnnotationModel#addAnnotation(Annotation, Position, boolean)
144      */
145     protected void addAnnotation(Annotation annotation, Position position, boolean fireModelChanged) {
146       super.addAnnotation(annotation, position, fireModelChanged);
147
148       Object cached = fReverseMap.get(position);
149       if (cached == null)
150         fReverseMap.put(position, annotation);
151       else if (cached instanceof List) {
152         List list = (List) cached;
153         list.add(annotation);
154       } else if (cached instanceof Annotation) {
155         List list = new ArrayList(2);
156         list.add(cached);
157         list.add(annotation);
158         fReverseMap.put(position, list);
159       }
160     }
161
162     /*
163      * @see IProblemRequestor#beginReporting()
164      */
165     public void beginReporting() {
166       ICompilationUnit unit = getWorkingCopy(fInput);
167       if (unit != null) // && unit.getJavaProject().isOnClasspath(unit))
168         fCollectedProblems = new ArrayList();
169       else
170         fCollectedProblems = null;
171     }
172
173     protected MarkerAnnotation createMarkerAnnotation(IMarker marker) {
174       return new JavaMarkerAnnotation(marker);
175     }
176
177     protected Position createPositionFromProblem(IProblem problem) {
178       int start = problem.getSourceStart();
179       if (start < 0)
180         return null;
181
182       int length = problem.getSourceEnd() - problem.getSourceStart() + 1;
183       if (length < 0)
184         return null;
185
186       return new Position(start, length);
187     }
188
189     /*
190      * @see IProblemRequestor#endReporting()
191      */
192     public void endReporting() {
193       if (!isActive())
194         return;
195
196       if (fProgressMonitor != null && fProgressMonitor.isCanceled())
197         return;
198
199       boolean isCanceled = false;
200       boolean temporaryProblemsChanged = false;
201       fPreviouslyOverlaid = fCurrentlyOverlaid;
202       fCurrentlyOverlaid = new ArrayList();
203
204       synchronized (fAnnotations) {
205
206         if (fGeneratedAnnotations.size() > 0) {
207           temporaryProblemsChanged = true;
208           removeAnnotations(fGeneratedAnnotations, false, true);
209           fGeneratedAnnotations.clear();
210         }
211
212         if (fCollectedProblems != null && fCollectedProblems.size() > 0) {
213
214           Iterator e = fCollectedProblems.iterator();
215           while (e.hasNext()) {
216
217             IProblem problem = (IProblem) e.next();
218
219             if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
220               isCanceled = true;
221               break;
222             }
223
224             Position position = createPositionFromProblem(problem);
225             if (position != null) {
226
227               ProblemAnnotation annotation = new ProblemAnnotation(problem);
228               overlayMarkers(position, annotation);
229               fGeneratedAnnotations.add(annotation);
230               addAnnotation(annotation, position, false);
231
232               temporaryProblemsChanged = true;
233             }
234           }
235
236           fCollectedProblems.clear();
237         }
238
239         removeMarkerOverlays(isCanceled);
240         fPreviouslyOverlaid.clear();
241         fPreviouslyOverlaid = null;
242       }
243
244       if (temporaryProblemsChanged)
245         fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), false));
246     }
247
248     private Object getAnnotations(Position position) {
249       return fReverseMap.get(position);
250     }
251
252     /*
253      * @see AnnotationModel#fireModelChanged()
254      */
255     protected void fireModelChanged() {
256       fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), true));
257     }
258
259     /*
260      * @see IProblemRequestor#isActive()
261      */
262     public boolean isActive() {
263       return fIsActive && (fCollectedProblems != null);
264     }
265
266     /*
267      * @see AnnotationModel#removeAllAnnotations(boolean)
268      */
269     protected void removeAllAnnotations(boolean fireModelChanged) {
270       super.removeAllAnnotations(fireModelChanged);
271       fReverseMap.clear();
272     }
273
274     /*
275      * @see AnnotationModel#removeAnnotation(Annotation, boolean)
276      */
277     protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
278       Position position = getPosition(annotation);
279       Object cached = fReverseMap.get(position);
280       if (cached instanceof List) {
281         List list = (List) cached;
282         list.remove(annotation);
283         if (list.size() == 1) {
284           fReverseMap.put(position, list.get(0));
285           list.clear();
286         }
287       } else if (cached instanceof Annotation) {
288         fReverseMap.remove(position);
289       }
290
291       super.removeAnnotation(annotation, fireModelChanged);
292     }
293
294     private void removeMarkerOverlays(boolean isCanceled) {
295       if (isCanceled) {
296         fCurrentlyOverlaid.addAll(fPreviouslyOverlaid);
297       } else if (fPreviouslyOverlaid != null) {
298         Iterator e = fPreviouslyOverlaid.iterator();
299         while (e.hasNext()) {
300           JavaMarkerAnnotation annotation = (JavaMarkerAnnotation) e.next();
301           annotation.setOverlay(null);
302         }
303       }
304     }
305
306     /*
307      * @see IProblemRequestorExtension#setIsActive(boolean)
308      */
309     public void setIsActive(boolean isActive) {
310       if (fIsActive != isActive) {
311         fIsActive = isActive;
312         if (fIsActive)
313           startCollectingProblems();
314         else
315           stopCollectingProblems();
316       }
317     }
318
319     /*
320      * @see IProblemRequestorExtension#setProgressMonitor(IProgressMonitor)
321      */
322     public void setProgressMonitor(IProgressMonitor monitor) {
323       fProgressMonitor = monitor;
324     }
325
326     /**
327      * Overlays value with problem annotation.
328      * @param problemAnnotation
329      */
330     private void setOverlay(Object value, ProblemAnnotation problemAnnotation) {
331       if (value instanceof JavaMarkerAnnotation) {
332         JavaMarkerAnnotation annotation = (JavaMarkerAnnotation) value;
333         if (annotation.isProblem()) {
334           annotation.setOverlay(problemAnnotation);
335           fPreviouslyOverlaid.remove(annotation);
336           fCurrentlyOverlaid.add(annotation);
337         }
338       }
339     }
340
341     private void overlayMarkers(Position position, ProblemAnnotation problemAnnotation) {
342       Object value = getAnnotations(position);
343       if (value instanceof List) {
344         List list = (List) value;
345         for (Iterator e = list.iterator(); e.hasNext();)
346           setOverlay(e.next(), problemAnnotation);
347       } else {
348         setOverlay(value, problemAnnotation);
349       }
350     }
351
352     /**
353      * Tells this annotation model to collect temporary problems from now on.
354      */
355     private void startCollectingProblems() {
356       fCollectedProblems = new ArrayList();
357       fGeneratedAnnotations = new ArrayList();
358     }
359
360     /**
361      * Tells this annotation model to no longer collect temporary problems.
362      */
363     private void stopCollectingProblems() {
364       if (fGeneratedAnnotations != null) {
365         removeAnnotations(fGeneratedAnnotations, true, true);
366         fGeneratedAnnotations.clear();
367       }
368       fCollectedProblems = null;
369       fGeneratedAnnotations = null;
370     }
371
372     protected void update(IMarkerDelta[] markerDeltas) {
373
374       super.update(markerDeltas);
375
376       if (markerDeltas != null && markerDeltas.length > 0) {
377         try {
378           ICompilationUnit workingCopy = getWorkingCopy(fInput);
379           if (workingCopy != null)
380             workingCopy.reconcile(true, null);
381         } catch (JavaModelException ex) {
382           handleCoreException(ex, ex.getMessage());
383         }
384       }
385     }
386   }
387   /**
388                  * Creates <code>IBuffer</code>s based on documents.
389                  */
390   protected class BufferFactory implements IBufferFactory {
391
392     private IDocument internalGetDocument(IFileEditorInput input) throws CoreException {
393       IDocument document = getDocument(input);
394       if (document != null)
395         return document;
396       return PHPDocumentProvider.this.createDocument(input);
397     }
398
399     public IBuffer createBuffer(IOpenable owner) {
400       if (owner instanceof ICompilationUnit) {
401
402         ICompilationUnit unit = (ICompilationUnit) owner;
403         ICompilationUnit original = (ICompilationUnit) unit.getOriginalElement();
404         IResource resource = original.getResource();
405         if (resource instanceof IFile) {
406           IFileEditorInput providerKey = new FileEditorInput((IFile) resource);
407
408           IDocument document = null;
409           IStatus status = null;
410
411           try {
412             document = internalGetDocument(providerKey);
413           } catch (CoreException x) {
414             status = x.getStatus();
415             document = new Document();
416             initializeDocument(document, providerKey);
417           }
418
419           DocumentAdapter adapter =
420             new DocumentAdapter(unit, document, new DefaultLineTracker(), PHPDocumentProvider.this, providerKey);
421           adapter.setStatus(status);
422           return adapter;
423         }
424
425       }
426       return DocumentAdapter.NULL;
427     }
428   };
429
430   protected static class GlobalAnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
431
432     private ListenerList fListenerList;
433
434     public GlobalAnnotationModelListener() {
435       fListenerList = new ListenerList();
436     }
437
438     public void addListener(IAnnotationModelListener listener) {
439       fListenerList.add(listener);
440     }
441
442     /**
443      * @see IAnnotationModelListenerExtension#modelChanged(AnnotationModelEvent)
444      */
445     public void modelChanged(AnnotationModelEvent event) {
446       Object[] listeners = fListenerList.getListeners();
447       for (int i = 0; i < listeners.length; i++) {
448         Object curr = listeners[i];
449         if (curr instanceof IAnnotationModelListenerExtension) {
450           ((IAnnotationModelListenerExtension) curr).modelChanged(event);
451         }
452       }
453     }
454
455     /**
456      * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
457      */
458     public void modelChanged(IAnnotationModel model) {
459       Object[] listeners = fListenerList.getListeners();
460       for (int i = 0; i < listeners.length; i++) {
461         ((IAnnotationModelListener) listeners[i]).modelChanged(model);
462       }
463     }
464
465     public void removeListener(IAnnotationModelListener listener) {
466       fListenerList.remove(listener);
467     }
468   }
469
470   /**
471                  * Annotation representating an <code>IProblem</code>.
472                  */
473   static protected class ProblemAnnotation extends Annotation implements IJavaAnnotation {
474
475     //    private static Image fgQuickFixImage;
476     //    private static Image fgQuickFixErrorImage;
477     //    private static boolean fgQuickFixImagesInitialized = false;
478
479     private List fOverlaids;
480     private IProblem fProblem;
481     private Image fImage;
482     //    private boolean fQuickFixImagesInitialized = false;
483     private AnnotationType fType;
484
485     public ProblemAnnotation(IProblem problem) {
486
487       fProblem = problem;
488       setLayer(MarkerAnnotation.PROBLEM_LAYER + 1);
489
490       if (IProblem.Task == fProblem.getID())
491         fType = AnnotationType.TASK;
492       else if (fProblem.isWarning())
493         fType = AnnotationType.WARNING;
494       else
495         fType = AnnotationType.ERROR;
496     }
497
498     private void initializeImages() {
499       // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18936
500       //      if (!fQuickFixImagesInitialized) {
501       //        if (indicateQuixFixableProblems() && JavaCorrectionProcessor.hasCorrections(fProblem.getID())) {
502       //          if (!fgQuickFixImagesInitialized) {
503       //            fgQuickFixImage = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_PROBLEM);
504       //            fgQuickFixErrorImage = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_ERROR);
505       //            fgQuickFixImagesInitialized = true;
506       //          }
507       //          if (fType == AnnotationType.ERROR)
508       //            fImage = fgQuickFixErrorImage;
509       //          else
510       //            fImage = fgQuickFixImage;
511       //        }
512       //        fQuickFixImagesInitialized = true;
513       //      }
514     }
515
516     private boolean indicateQuixFixableProblems() {
517       return PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_CORRECTION_INDICATION);
518     }
519
520     /*
521      * @see Annotation#paint
522      */
523     public void paint(GC gc, Canvas canvas, Rectangle r) {
524       initializeImages();
525       if (fImage != null)
526         drawImage(fImage, gc, canvas, r, SWT.CENTER, SWT.TOP);
527     }
528
529     /*
530      * @see IJavaAnnotation#getImage(Display)
531      */
532     public Image getImage(Display display) {
533       initializeImages();
534       return fImage;
535     }
536
537     /*
538      * @see IJavaAnnotation#getMessage()
539      */
540     public String getMessage() {
541       return fProblem.getMessage();
542     }
543
544     /*
545      * @see IJavaAnnotation#isTemporary()
546      */
547     public boolean isTemporary() {
548       return true;
549     }
550
551     /*
552      * @see IJavaAnnotation#getArguments()
553      */
554     public String[] getArguments() {
555       return isProblem() ? fProblem.getArguments() : null;
556     }
557
558     /*
559      * @see IJavaAnnotation#getId()
560      */
561     public int getId() {
562       return isProblem() ? fProblem.getID() : -1;
563     }
564
565     /*
566      * @see IJavaAnnotation#isProblem()
567      */
568     public boolean isProblem() {
569       return fType == AnnotationType.WARNING || fType == AnnotationType.ERROR;
570     }
571
572     /*
573      * @see IJavaAnnotation#isRelevant()
574      */
575     public boolean isRelevant() {
576       return true;
577     }
578
579     /*
580      * @see IJavaAnnotation#hasOverlay()
581      */
582     public boolean hasOverlay() {
583       return false;
584     }
585
586     /*
587      * @see IJavaAnnotation#addOverlaid(IJavaAnnotation)
588      */
589     public void addOverlaid(IJavaAnnotation annotation) {
590       if (fOverlaids == null)
591         fOverlaids = new ArrayList(1);
592       fOverlaids.add(annotation);
593     }
594
595     /*
596      * @see IJavaAnnotation#removeOverlaid(IJavaAnnotation)
597      */
598     public void removeOverlaid(IJavaAnnotation annotation) {
599       if (fOverlaids != null) {
600         fOverlaids.remove(annotation);
601         if (fOverlaids.size() == 0)
602           fOverlaids = null;
603       }
604     }
605
606     /*
607      * @see IJavaAnnotation#getOverlaidIterator()
608      */
609     public Iterator getOverlaidIterator() {
610       if (fOverlaids != null)
611         return fOverlaids.iterator();
612       return null;
613     }
614
615     public AnnotationType getAnnotationType() {
616       return fType;
617     }
618   };
619   /**
620                  * Internal structure for mapping positions to some value. 
621                  * The reason for this specific structure is that positions can
622                  * change over time. Thus a lookup is based on value and not
623                  * on hash value.
624                  */
625   protected static class ReverseMap {
626
627     static class Entry {
628       Position fPosition;
629       Object fValue;
630     }
631     private int fAnchor = 0;
632
633     private List fList = new ArrayList(2);
634
635     public ReverseMap() {
636     }
637
638     public void clear() {
639       fList.clear();
640     }
641
642     public Object get(Position position) {
643
644       Entry entry;
645
646       // behind anchor
647       int length = fList.size();
648       for (int i = fAnchor; i < length; i++) {
649         entry = (Entry) fList.get(i);
650         if (entry.fPosition.equals(position)) {
651           fAnchor = i;
652           return entry.fValue;
653         }
654       }
655
656       // before anchor
657       for (int i = 0; i < fAnchor; i++) {
658         entry = (Entry) fList.get(i);
659         if (entry.fPosition.equals(position)) {
660           fAnchor = i;
661           return entry.fValue;
662         }
663       }
664
665       return null;
666     }
667
668     private int getIndex(Position position) {
669       Entry entry;
670       int length = fList.size();
671       for (int i = 0; i < length; i++) {
672         entry = (Entry) fList.get(i);
673         if (entry.fPosition.equals(position))
674           return i;
675       }
676       return -1;
677     }
678
679     public void put(Position position, Object value) {
680       int index = getIndex(position);
681       if (index == -1) {
682         Entry entry = new Entry();
683         entry.fPosition = position;
684         entry.fValue = value;
685         fList.add(entry);
686       } else {
687         Entry entry = (Entry) fList.get(index);
688         entry.fValue = value;
689       }
690     }
691
692     public void remove(Position position) {
693       int index = getIndex(position);
694       if (index > -1)
695         fList.remove(index);
696     }
697   }
698
699   /**
700                  * Document that can also be used by a background reconciler.
701                  */
702   protected static class PartiallySynchronizedDocument extends Document {
703
704     /*
705      * @see IDocumentExtension#startSequentialRewrite(boolean)
706      */
707     synchronized public void startSequentialRewrite(boolean normalized) {
708       super.startSequentialRewrite(normalized);
709     }
710
711     /*
712      * @see IDocumentExtension#stopSequentialRewrite()
713      */
714     synchronized public void stopSequentialRewrite() {
715       super.stopSequentialRewrite();
716     }
717
718     /*
719      * @see IDocument#get()
720      */
721     synchronized public String get() {
722       return super.get();
723     }
724
725     /*
726      * @see IDocument#get(int, int)
727      */
728     synchronized public String get(int offset, int length) throws BadLocationException {
729       return super.get(offset, length);
730     }
731
732     /*
733      * @see IDocument#getChar(int)
734      */
735     synchronized public char getChar(int offset) throws BadLocationException {
736       return super.getChar(offset);
737     }
738
739     /*
740      * @see IDocument#replace(int, int, String)
741      */
742     synchronized public void replace(int offset, int length, String text) throws BadLocationException {
743       super.replace(offset, length, text);
744     }
745
746     /*
747      * @see IDocument#set(String)
748      */
749     synchronized public void set(String text) {
750       super.set(text);
751     }
752   };
753   //
754   //  private static PHPPartitionScanner HTML_PARTITION_SCANNER = null;
755   //
756   //  private static PHPPartitionScanner PHP_PARTITION_SCANNER = null;
757   //  private static PHPPartitionScanner SMARTY_PARTITION_SCANNER = null;
758   //
759   //  // private final static String[] TYPES= new String[] { PHPPartitionScanner.PHP, PHPPartitionScanner.JAVA_DOC, PHPPartitionScanner.JAVA_MULTILINE_COMMENT };
760   //  private final static String[] TYPES =
761   //    new String[] {
762   //      IPHPPartitionScannerConstants.PHP,
763   //      IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
764   //      IPHPPartitionScannerConstants.HTML,
765   //      IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
766   //      IPHPPartitionScannerConstants.JAVASCRIPT,
767   //      IPHPPartitionScannerConstants.CSS,
768   //      IPHPPartitionScannerConstants.SMARTY,
769   //      IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT };
770   //  private static PHPPartitionScanner XML_PARTITION_SCANNER = null;
771
772   /* Preference key for temporary problems */
773   private final static String HANDLE_TEMPORARY_PROBLEMS = PreferenceConstants.EDITOR_EVALUTE_TEMPORARY_PROBLEMS;
774
775   /** The buffer factory */
776   private IBufferFactory fBufferFactory = new BufferFactory();
777   /** Indicates whether the save has been initialized by this provider */
778   private boolean fIsAboutToSave = false;
779   /** The save policy used by this provider */
780   private ISavePolicy fSavePolicy;
781   /** Internal property changed listener */
782   private IPropertyChangeListener fPropertyListener;
783
784   /** annotation model listener added to all created CU annotation models */
785   private GlobalAnnotationModelListener fGlobalAnnotationModelListener;
786
787   public PHPDocumentProvider() {
788
789     fPropertyListener = new IPropertyChangeListener() {
790       public void propertyChange(PropertyChangeEvent event) {
791         if (HANDLE_TEMPORARY_PROBLEMS.equals(event.getProperty()))
792           enableHandlingTemporaryProblems();
793       }
794     };
795
796     fGlobalAnnotationModelListener = new GlobalAnnotationModelListener();
797
798     PHPeclipsePlugin.getDefault().getPreferenceStore().addPropertyChangeListener(fPropertyListener);
799
800   }
801
802   /**
803    * Sets the document provider's save policy.
804    */
805   public void setSavePolicy(ISavePolicy savePolicy) {
806     fSavePolicy = savePolicy;
807   }
808
809   /**
810    * Creates a compilation unit from the given file.
811    * 
812    * @param file the file from which to create the compilation unit
813    */
814   protected ICompilationUnit createCompilationUnit(IFile file) {
815     Object element = PHPCore.create(file);
816     if (element instanceof ICompilationUnit)
817       return (ICompilationUnit) element;
818     return null;
819   }
820
821   /*
822          * @see AbstractDocumentProvider#createElementInfo(Object)
823          */
824   protected ElementInfo createElementInfo(Object element) throws CoreException {
825
826     if (!(element instanceof IFileEditorInput))
827       return super.createElementInfo(element);
828
829     IFileEditorInput input = (IFileEditorInput) element;
830     ICompilationUnit original = createCompilationUnit(input.getFile());
831     if (original != null) {
832
833       try {
834
835         try {
836           refreshFile(input.getFile());
837         } catch (CoreException x) {
838           handleCoreException(x, PHPEditorMessages.getString("PHPDocumentProvider.error.createElementInfo")); //$NON-NLS-1$
839         }
840
841         IAnnotationModel m = createCompilationUnitAnnotationModel(input);
842         IProblemRequestor r = m instanceof IProblemRequestor ? (IProblemRequestor) m : null;
843         ICompilationUnit c = (ICompilationUnit) original.getSharedWorkingCopy(getProgressMonitor(), fBufferFactory, r);
844
845         DocumentAdapter a = null;
846         try {
847           a = (DocumentAdapter) c.getBuffer();
848         } catch (ClassCastException x) {
849           IStatus status = new Status(IStatus.ERROR, PHPeclipsePlugin.PLUGIN_ID, PHPStatusConstants.TEMPLATE_IO_EXCEPTION, "Shared working copy has wrong buffer", x); //$NON-NLS-1$
850           throw new CoreException(status);
851         }
852
853         _FileSynchronizer f = new _FileSynchronizer(input);
854         f.install();
855
856         CompilationUnitInfo info = new CompilationUnitInfo(a.getDocument(), m, f, c);
857         info.setModificationStamp(computeModificationStamp(input.getFile()));
858         info.fStatus = a.getStatus();
859         info.fEncoding = getPersistedEncoding(input);
860
861         if (r instanceof IProblemRequestorExtension) {
862           IProblemRequestorExtension extension = (IProblemRequestorExtension) r;
863           extension.setIsActive(isHandlingTemporaryProblems());
864         }
865         m.addAnnotationModelListener(fGlobalAnnotationModelListener);
866
867         return info;
868
869       } catch (JavaModelException x) {
870         throw new CoreException(x.getStatus());
871       }
872     } else {
873       return super.createElementInfo(element);
874     }
875   }
876
877   /*
878    * @see AbstractDocumentProvider#disposeElementInfo(Object, ElementInfo)
879    */
880   protected void disposeElementInfo(Object element, ElementInfo info) {
881
882     if (info instanceof CompilationUnitInfo) {
883       CompilationUnitInfo cuInfo = (CompilationUnitInfo) info;
884       cuInfo.fCopy.destroy();
885       cuInfo.fModel.removeAnnotationModelListener(fGlobalAnnotationModelListener);
886     }
887
888     super.disposeElementInfo(element, info);
889   }
890
891   /*
892          * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument, boolean)
893          */
894   protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
895     throws CoreException {
896
897     ElementInfo elementInfo = getElementInfo(element);
898     if (elementInfo instanceof CompilationUnitInfo) {
899       CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
900
901       // update structure, assumes lock on info.fCopy
902       info.fCopy.reconcile();
903
904       ICompilationUnit original = (ICompilationUnit) info.fCopy.getOriginalElement();
905       IResource resource = original.getResource();
906
907       if (resource == null) {
908         // underlying resource has been deleted, just recreate file, ignore the rest
909         super.doSaveDocument(monitor, element, document, overwrite);
910         return;
911       }
912
913       if (resource != null && !overwrite)
914         checkSynchronizationState(info.fModificationStamp, resource);
915
916       if (fSavePolicy != null)
917         fSavePolicy.preSave(info.fCopy);
918
919       // inform about the upcoming content change
920       fireElementStateChanging(element);
921       try {
922         fIsAboutToSave = true;
923         // commit working copy
924         info.fCopy.commit(overwrite, monitor);
925       } catch (CoreException x) {
926         // inform about the failure
927         fireElementStateChangeFailed(element);
928         throw x;
929       } catch (RuntimeException x) {
930         // inform about the failure
931         fireElementStateChangeFailed(element);
932         throw x;
933       } finally {
934         fIsAboutToSave = false;
935       }
936
937       // If here, the dirty state of the editor will change to "not dirty".
938       // Thus, the state changing flag will be reset.
939
940       AbstractMarkerAnnotationModel model = (AbstractMarkerAnnotationModel) info.fModel;
941       model.updateMarkers(info.fDocument);
942
943       if (resource != null)
944         info.setModificationStamp(computeModificationStamp(resource));
945
946       if (fSavePolicy != null) {
947         ICompilationUnit unit = fSavePolicy.postSave(original);
948         if (unit != null) {
949           IResource r = unit.getResource();
950           IMarker[] markers = r.findMarkers(IMarker.MARKER, true, IResource.DEPTH_ZERO);
951           if (markers != null && markers.length > 0) {
952             for (int i = 0; i < markers.length; i++)
953               model.updateMarker(markers[i], info.fDocument, null);
954           }
955         }
956       }
957
958     } else {
959       super.doSaveDocument(monitor, element, document, overwrite);
960     }
961   }
962
963   /**
964    * Replaces createAnnotionModel of the super class.
965    */
966   protected IAnnotationModel createCompilationUnitAnnotationModel(Object element) throws CoreException {
967                 if ( !(element instanceof IFileEditorInput))
968                         throw new CoreException(PHPUIStatus.createError(
969                                 IJavaModelStatusConstants.INVALID_RESOURCE_TYPE, "", null)); //$NON-NLS-1$
970                 
971                 IFileEditorInput input= (IFileEditorInput) element;
972                 return new CompilationUnitAnnotationModel(input);
973   }
974
975   //  private IDocumentPartitioner createCSSPartitioner() {
976   //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
977   //  }
978
979   /* (non-Javadoc)
980    * Method declared on AbstractDocumentProvider
981    */
982   protected IDocument createDocument(Object element) throws CoreException {
983     if (element instanceof IEditorInput) {
984       Document document = new PartiallySynchronizedDocument();
985       if (setDocumentContent(document, (IEditorInput) element, getEncoding(element))) {
986         initializeDocument(document, (IEditorInput) element);
987
988         //      
989         //    IDocument document = super.createDocument(element);
990         //    if (document != null) {
991         //        IDocumentPartitioner partitioner = null;
992         //        if (element instanceof FileEditorInput) {
993         //          IFile file = (IFile) ((FileEditorInput) element).getAdapter(IFile.class);
994         //          String filename = file.getLocation().toString();
995         //          String extension = filename.substring(filename.lastIndexOf("."), filename.length());
996         //          //   System.out.println(extension);
997         //          if (extension.equalsIgnoreCase(".html") || extension.equalsIgnoreCase(".htm")) {
998         //            // html
999         //            partitioner = createHTMLPartitioner();
1000         //          } else if (extension.equalsIgnoreCase(".xml")) {
1001         //            // xml
1002         //            partitioner = createXMLPartitioner();
1003         //          } else if (extension.equalsIgnoreCase(".js")) {
1004         //            // javascript
1005         //            partitioner = createJavaScriptPartitioner();
1006         //          } else if (extension.equalsIgnoreCase(".css")) {
1007         //            // cascading style sheets
1008         //            partitioner = createCSSPartitioner();
1009         //          } else if (extension.equalsIgnoreCase(".tpl")) {
1010         //            // smarty ?
1011         //            partitioner = createSmartyPartitioner();
1012         //          } else if (extension.equalsIgnoreCase(".inc")) {
1013         //            // php include files ?
1014         //            partitioner = createIncludePartitioner();
1015         //          }
1016         //        }
1017         //
1018         //        if (partitioner == null) {
1019         //          partitioner = createPHPPartitioner();
1020         //        }
1021         //        document.setDocumentPartitioner(partitioner);
1022         //        partitioner.connect(document);
1023       }
1024       return document;
1025     }
1026     return null;
1027   }
1028
1029   //  /**
1030   //   * Return a partitioner for .html files.
1031   //   */
1032   //  private IDocumentPartitioner createHTMLPartitioner() {
1033   //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
1034   //  }
1035   //
1036   //  private IDocumentPartitioner createIncludePartitioner() {
1037   //    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
1038   //  }
1039   //
1040   //  private IDocumentPartitioner createJavaScriptPartitioner() {
1041   //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
1042   //  }
1043
1044   /**
1045    * Creates a line tracker working with the same line delimiters as the document
1046    * of the given element. Assumes the element to be managed by this document provider.
1047    * 
1048    * @param element the element serving as blue print
1049    * @return a line tracker based on the same line delimiters as the element's document
1050    */
1051   public ILineTracker createLineTracker(Object element) {
1052     return new DefaultLineTracker();
1053   }
1054
1055   //  /**
1056   //   * Return a partitioner for .php files.
1057   //   */
1058   //  private IDocumentPartitioner createPHPPartitioner() {
1059   //    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
1060   //  }
1061   //
1062   //  private IDocumentPartitioner createSmartyPartitioner() {
1063   //    return new DefaultPartitioner(getSmartyPartitionScanner(), TYPES);
1064   //  }
1065   //
1066   //  private IDocumentPartitioner createXMLPartitioner() {
1067   //    return new DefaultPartitioner(getXMLPartitionScanner(), TYPES);
1068   //  }
1069   //
1070   //  /**
1071   //   * Return a scanner for creating html partitions.
1072   //   */
1073   //  private PHPPartitionScanner getHTMLPartitionScanner() {
1074   //    if (HTML_PARTITION_SCANNER == null)
1075   //      HTML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.HTML_FILE);
1076   //    return HTML_PARTITION_SCANNER;
1077   //  }
1078   //  /**
1079   //   * Return a scanner for creating php partitions.
1080   //   */
1081   //  private PHPPartitionScanner getPHPPartitionScanner() {
1082   //    if (PHP_PARTITION_SCANNER == null)
1083   //      PHP_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.PHP_FILE);
1084   //    return PHP_PARTITION_SCANNER;
1085   //  }
1086   //
1087   //  /**
1088   //   * Return a scanner for creating smarty partitions.
1089   //   */
1090   //  private PHPPartitionScanner getSmartyPartitionScanner() {
1091   //    if (SMARTY_PARTITION_SCANNER == null)
1092   //      SMARTY_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.SMARTY_FILE);
1093   //    return SMARTY_PARTITION_SCANNER;
1094   //  }
1095   //
1096   //  /**
1097   //   * Return a scanner for creating xml partitions.
1098   //   */
1099   //  private PHPPartitionScanner getXMLPartitionScanner() {
1100   //    if (XML_PARTITION_SCANNER == null)
1101   //      XML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.XML_FILE);
1102   //    return XML_PARTITION_SCANNER;
1103   //  }
1104
1105   protected void initializeDocument(IDocument document, IEditorInput editorInput) {
1106     if (document != null) {
1107       JavaTextTools tools = PHPeclipsePlugin.getDefault().getJavaTextTools();
1108       IDocumentPartitioner partitioner = null;
1109       if (editorInput != null && editorInput instanceof FileEditorInput) {
1110         IFile file = (IFile) ((FileEditorInput) editorInput).getAdapter(IFile.class);
1111         String filename = file.getLocation().toString();
1112         String extension = filename.substring(filename.lastIndexOf("."), filename.length());
1113         partitioner = tools.createDocumentPartitioner(extension);
1114       } else {
1115         partitioner = tools.createDocumentPartitioner(".php");
1116       }
1117       document.setDocumentPartitioner(partitioner);
1118       partitioner.connect(document);
1119     }
1120   }
1121
1122   /*
1123    * @see AbstractDocumentProvider#resetDocument(Object)
1124    */
1125   public void resetDocument(Object element) throws CoreException {
1126     if (element == null)
1127       return;
1128
1129     ElementInfo elementInfo = getElementInfo(element);
1130     if (elementInfo instanceof CompilationUnitInfo) {
1131       CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
1132
1133       IDocument document;
1134       IStatus status = null;
1135
1136       try {
1137
1138         ICompilationUnit original = (ICompilationUnit) info.fCopy.getOriginalElement();
1139         IResource resource = original.getResource();
1140         if (resource instanceof IFile) {
1141
1142           IFile file = (IFile) resource;
1143
1144           try {
1145             refreshFile(file);
1146           } catch (CoreException x) {
1147             handleCoreException(x, PHPEditorMessages.getString("PHPDocumentProvider.error.resetDocument")); //$NON-NLS-1$
1148           }
1149
1150           IFileEditorInput input = new FileEditorInput(file);
1151           document = super.createDocument(input);
1152
1153         } else {
1154           document = new Document();
1155         }
1156
1157       } catch (CoreException x) {
1158         document = new Document();
1159         status = x.getStatus();
1160       }
1161
1162       fireElementContentAboutToBeReplaced(element);
1163
1164       removeUnchangedElementListeners(element, info);
1165       info.fDocument.set(document.get());
1166       info.fCanBeSaved = false;
1167       info.fStatus = status;
1168       addUnchangedElementListeners(element, info);
1169
1170       fireElementContentReplaced(element);
1171       fireElementDirtyStateChanged(element, false);
1172
1173     } else {
1174       super.resetDocument(element);
1175     }
1176   }
1177   /**
1178          * Saves the content of the given document to the given element.
1179          * This is only performed when this provider initiated the save.
1180          * 
1181          * @param monitor the progress monitor
1182          * @param element the element to which to save
1183          * @param document the document to save
1184          * @param overwrite <code>true</code> if the save should be enforced
1185          */
1186   public void saveDocumentContent(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
1187     throws CoreException {
1188
1189     if (!fIsAboutToSave)
1190       return;
1191
1192     if (element instanceof IFileEditorInput) {
1193       IFileEditorInput input = (IFileEditorInput) element;
1194       try {
1195         String encoding = getEncoding(element);
1196         if (encoding == null)
1197           encoding = ResourcesPlugin.getEncoding();
1198         InputStream stream = new ByteArrayInputStream(document.get().getBytes(encoding));
1199         IFile file = input.getFile();
1200         file.setContents(stream, overwrite, true, monitor);
1201       } catch (IOException x) {
1202         IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin.PLUGIN_ID, IStatus.OK, x.getMessage(), x);
1203         throw new CoreException(s);
1204       }
1205     }
1206   }
1207   /**
1208    * Returns the underlying resource for the given element.
1209    * 
1210    * @param the element
1211    * @return the underlying resource of the given element
1212    */
1213   public IResource getUnderlyingResource(Object element) {
1214     if (element instanceof IFileEditorInput) {
1215       IFileEditorInput input = (IFileEditorInput) element;
1216       return input.getFile();
1217     }
1218     return null;
1219   }
1220
1221   /**
1222    * Returns the working copy this document provider maintains for the given
1223    * element.
1224    * 
1225    * @param element the given element
1226    * @return the working copy for the given element
1227    */
1228   ICompilationUnit getWorkingCopy(IEditorInput element) {
1229
1230     ElementInfo elementInfo = getElementInfo(element);
1231     if (elementInfo instanceof CompilationUnitInfo) {
1232       CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
1233       return info.fCopy;
1234     }
1235
1236     return null;
1237   }
1238
1239   /**
1240    * Gets the BufferFactory.
1241    */
1242   public IBufferFactory getBufferFactory() {
1243     return fBufferFactory;
1244   }
1245
1246   /**
1247    * Shuts down this document provider.
1248    */
1249   public void shutdown() {
1250
1251     PHPeclipsePlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyListener);
1252
1253     Iterator e = getConnectedElements();
1254     while (e.hasNext())
1255       disconnect(e.next());
1256   }
1257
1258   /**
1259    * Returns the preference whether handling temporary problems is enabled.
1260    */
1261   protected boolean isHandlingTemporaryProblems() {
1262     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
1263     return store.getBoolean(HANDLE_TEMPORARY_PROBLEMS);
1264   }
1265
1266   /**
1267    * Switches the state of problem acceptance according to the value in the preference store.
1268    */
1269   protected void enableHandlingTemporaryProblems() {
1270     boolean enable = isHandlingTemporaryProblems();
1271     for (Iterator iter = getConnectedElements(); iter.hasNext();) {
1272       ElementInfo element = getElementInfo(iter.next());
1273       if (element instanceof CompilationUnitInfo) {
1274         CompilationUnitInfo info = (CompilationUnitInfo) element;
1275         if (info.fModel instanceof IProblemRequestorExtension) {
1276           IProblemRequestorExtension extension = (IProblemRequestorExtension) info.fModel;
1277           extension.setIsActive(enable);
1278         }
1279       }
1280     }
1281   }
1282
1283   /**
1284    * Adds a listener that reports changes from all compilation unit annotation models.
1285    */
1286   public void addGlobalAnnotationModelListener(IAnnotationModelListener listener) {
1287     fGlobalAnnotationModelListener.addListener(listener);
1288   }
1289
1290   /**
1291    * Removes the listener.
1292    */
1293   public void removeGlobalAnnotationModelListener(IAnnotationModelListener listener) {
1294     fGlobalAnnotationModelListener.removeListener(listener);
1295   }
1296
1297   /**
1298    * Returns whether the given element is connected to this document provider.
1299    * 
1300    * @param element the element
1301    * @return <code>true</code> if the element is connected, <code>false</code> otherwise
1302    */
1303   boolean isConnected(Object element) {
1304     return getElementInfo(element) != null;
1305   }
1306 }