new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPDocumentProvider.java
index 919bdd9..fb4f623 100644 (file)
@@ -12,55 +12,1295 @@ Contributors:
     Klaus Hartlage - www.eclipseproject.de
 **********************************************************************/
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import net.sourceforge.phpdt.core.IBuffer;
+import net.sourceforge.phpdt.core.IBufferFactory;
+import net.sourceforge.phpdt.core.ICompilationUnit;
+import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
+import net.sourceforge.phpdt.core.IOpenable;
+import net.sourceforge.phpdt.core.IProblemRequestor;
+import net.sourceforge.phpdt.core.JavaModelException;
+import net.sourceforge.phpdt.core.compiler.IProblem;
+import net.sourceforge.phpdt.internal.ui.PHPStatusConstants;
+import net.sourceforge.phpdt.internal.ui.PHPUIStatus;
+import net.sourceforge.phpdt.internal.ui.text.java.IProblemRequestorExtension;
+import net.sourceforge.phpdt.ui.PreferenceConstants;
+import net.sourceforge.phpdt.ui.text.JavaTextTools;
+import net.sourceforge.phpeclipse.PHPCore;
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.Document;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentPartitioner;
-import org.eclipse.jface.text.rules.DefaultPartitioner;
-import org.eclipse.jface.text.rules.RuleBasedPartitioner;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModelEvent;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.editors.text.FileDocumentProvider;
-import net.sourceforge.phpeclipse.phpeditor.php.PHPPartitionScanner;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
+import org.eclipse.ui.texteditor.MarkerAnnotation;
+import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
 
 /** 
- * The JavaDocumentProvider provides the IDocuments used by java editors.
+ * The PHPDocumentProvider provides the IDocuments used by java editors.
  */
 
 public class PHPDocumentProvider extends FileDocumentProvider {
+  /**
+                        * Here for visibility issues only.
+                        */
+  protected class _FileSynchronizer extends FileSynchronizer {
+    public _FileSynchronizer(IFileEditorInput fileEditorInput) {
+      super(fileEditorInput);
+    }
+  };
+  /**
+                * Bundle of all required informations to allow working copy management. 
+                */
+  protected class CompilationUnitInfo extends FileInfo {
+
+    ICompilationUnit fCopy;
+
+    public CompilationUnitInfo(
+      IDocument document,
+      IAnnotationModel model,
+      _FileSynchronizer fileSynchronizer,
+      ICompilationUnit copy) {
+      super(document, model, fileSynchronizer);
+      fCopy = copy;
+    }
+
+    public void setModificationStamp(long timeStamp) {
+      fModificationStamp = timeStamp;
+    }
+  };
+  /**
+                * Annotation model dealing with java marker annotations and temporary problems.
+                * Also acts as problem requestor for its compilation unit. Initialiy inactive. Must explicitly be
+                * activated.
+                */
+  protected class CompilationUnitAnnotationModel
+    extends ResourceMarkerAnnotationModel
+    implements IProblemRequestor, IProblemRequestorExtension {
+    private List fCollectedProblems;
+    private List fCurrentlyOverlaid = new ArrayList();
+    private List fGeneratedAnnotations;
+
+    private IFileEditorInput fInput;
+    private boolean fIsActive = false;
+    private List fPreviouslyOverlaid = null;
+    private IProgressMonitor fProgressMonitor;
+
+    private ReverseMap fReverseMap = new ReverseMap();
+
+    public CompilationUnitAnnotationModel(IFileEditorInput input) {
+      super(input.getFile());
+      fInput = input;
+    }
+
+    /*
+     * @see IProblemRequestor#acceptProblem(IProblem)
+     */
+    public void acceptProblem(IProblem problem) {
+      if (isActive())
+        fCollectedProblems.add(problem);
+    }
+
+    /*
+     * @see AnnotationModel#addAnnotation(Annotation, Position, boolean)
+     */
+    protected void addAnnotation(Annotation annotation, Position position, boolean fireModelChanged) {
+      super.addAnnotation(annotation, position, fireModelChanged);
+
+      Object cached = fReverseMap.get(position);
+      if (cached == null)
+        fReverseMap.put(position, annotation);
+      else if (cached instanceof List) {
+        List list = (List) cached;
+        list.add(annotation);
+      } else if (cached instanceof Annotation) {
+        List list = new ArrayList(2);
+        list.add(cached);
+        list.add(annotation);
+        fReverseMap.put(position, list);
+      }
+    }
+
+    /*
+     * @see IProblemRequestor#beginReporting()
+     */
+    public void beginReporting() {
+      ICompilationUnit unit = getWorkingCopy(fInput);
+      if (unit != null) // && unit.getJavaProject().isOnClasspath(unit))
+        fCollectedProblems = new ArrayList();
+      else
+        fCollectedProblems = null;
+    }
+
+    protected MarkerAnnotation createMarkerAnnotation(IMarker marker) {
+      return new JavaMarkerAnnotation(marker);
+    }
+
+    protected Position createPositionFromProblem(IProblem problem) {
+      int start = problem.getSourceStart();
+      if (start < 0)
+        return null;
+
+      int length = problem.getSourceEnd() - problem.getSourceStart() + 1;
+      if (length < 0)
+        return null;
+
+      return new Position(start, length);
+    }
+
+    /*
+     * @see IProblemRequestor#endReporting()
+     */
+    public void endReporting() {
+      if (!isActive())
+        return;
+
+      if (fProgressMonitor != null && fProgressMonitor.isCanceled())
+        return;
+
+      boolean isCanceled = false;
+      boolean temporaryProblemsChanged = false;
+      fPreviouslyOverlaid = fCurrentlyOverlaid;
+      fCurrentlyOverlaid = new ArrayList();
+
+      synchronized (fAnnotations) {
+
+        if (fGeneratedAnnotations.size() > 0) {
+          temporaryProblemsChanged = true;
+          removeAnnotations(fGeneratedAnnotations, false, true);
+          fGeneratedAnnotations.clear();
+        }
+
+        if (fCollectedProblems != null && fCollectedProblems.size() > 0) {
+
+          Iterator e = fCollectedProblems.iterator();
+          while (e.hasNext()) {
+
+            IProblem problem = (IProblem) e.next();
+
+            if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
+              isCanceled = true;
+              break;
+            }
+
+            Position position = createPositionFromProblem(problem);
+            if (position != null) {
+
+              ProblemAnnotation annotation = new ProblemAnnotation(problem);
+              overlayMarkers(position, annotation);
+              fGeneratedAnnotations.add(annotation);
+              addAnnotation(annotation, position, false);
+
+              temporaryProblemsChanged = true;
+            }
+          }
+
+          fCollectedProblems.clear();
+        }
+
+        removeMarkerOverlays(isCanceled);
+        fPreviouslyOverlaid.clear();
+        fPreviouslyOverlaid = null;
+      }
+
+      if (temporaryProblemsChanged)
+        fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), false));
+    }
+
+    private Object getAnnotations(Position position) {
+      return fReverseMap.get(position);
+    }
+
+    /*
+     * @see AnnotationModel#fireModelChanged()
+     */
+    protected void fireModelChanged() {
+      fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), true));
+    }
+
+    /*
+     * @see IProblemRequestor#isActive()
+     */
+    public boolean isActive() {
+      return fIsActive && (fCollectedProblems != null);
+    }
+
+    /*
+     * @see AnnotationModel#removeAllAnnotations(boolean)
+     */
+    protected void removeAllAnnotations(boolean fireModelChanged) {
+      super.removeAllAnnotations(fireModelChanged);
+      fReverseMap.clear();
+    }
+
+    /*
+     * @see AnnotationModel#removeAnnotation(Annotation, boolean)
+     */
+    protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
+      Position position = getPosition(annotation);
+      Object cached = fReverseMap.get(position);
+      if (cached instanceof List) {
+        List list = (List) cached;
+        list.remove(annotation);
+        if (list.size() == 1) {
+          fReverseMap.put(position, list.get(0));
+          list.clear();
+        }
+      } else if (cached instanceof Annotation) {
+        fReverseMap.remove(position);
+      }
+
+      super.removeAnnotation(annotation, fireModelChanged);
+    }
+
+    private void removeMarkerOverlays(boolean isCanceled) {
+      if (isCanceled) {
+        fCurrentlyOverlaid.addAll(fPreviouslyOverlaid);
+      } else if (fPreviouslyOverlaid != null) {
+        Iterator e = fPreviouslyOverlaid.iterator();
+        while (e.hasNext()) {
+          JavaMarkerAnnotation annotation = (JavaMarkerAnnotation) e.next();
+          annotation.setOverlay(null);
+        }
+      }
+    }
+
+    /*
+     * @see IProblemRequestorExtension#setIsActive(boolean)
+     */
+    public void setIsActive(boolean isActive) {
+      if (fIsActive != isActive) {
+        fIsActive = isActive;
+        if (fIsActive)
+          startCollectingProblems();
+        else
+          stopCollectingProblems();
+      }
+    }
+
+    /*
+     * @see IProblemRequestorExtension#setProgressMonitor(IProgressMonitor)
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+      fProgressMonitor = monitor;
+    }
+
+    /**
+     * Overlays value with problem annotation.
+     * @param problemAnnotation
+     */
+    private void setOverlay(Object value, ProblemAnnotation problemAnnotation) {
+      if (value instanceof JavaMarkerAnnotation) {
+        JavaMarkerAnnotation annotation = (JavaMarkerAnnotation) value;
+        if (annotation.isProblem()) {
+          annotation.setOverlay(problemAnnotation);
+          fPreviouslyOverlaid.remove(annotation);
+          fCurrentlyOverlaid.add(annotation);
+        }
+      }
+    }
+
+    private void overlayMarkers(Position position, ProblemAnnotation problemAnnotation) {
+      Object value = getAnnotations(position);
+      if (value instanceof List) {
+        List list = (List) value;
+        for (Iterator e = list.iterator(); e.hasNext();)
+          setOverlay(e.next(), problemAnnotation);
+      } else {
+        setOverlay(value, problemAnnotation);
+      }
+    }
+
+    /**
+     * Tells this annotation model to collect temporary problems from now on.
+     */
+    private void startCollectingProblems() {
+      fCollectedProblems = new ArrayList();
+      fGeneratedAnnotations = new ArrayList();
+    }
+
+    /**
+     * Tells this annotation model to no longer collect temporary problems.
+     */
+    private void stopCollectingProblems() {
+      if (fGeneratedAnnotations != null) {
+        removeAnnotations(fGeneratedAnnotations, true, true);
+        fGeneratedAnnotations.clear();
+      }
+      fCollectedProblems = null;
+      fGeneratedAnnotations = null;
+    }
+
+    protected void update(IMarkerDelta[] markerDeltas) {
+
+      super.update(markerDeltas);
+
+      if (markerDeltas != null && markerDeltas.length > 0) {
+        try {
+          ICompilationUnit workingCopy = getWorkingCopy(fInput);
+          if (workingCopy != null)
+            workingCopy.reconcile(true, null);
+        } catch (JavaModelException ex) {
+          handleCoreException(ex, ex.getMessage());
+        }
+      }
+    }
+  }
+  /**
+                * Creates <code>IBuffer</code>s based on documents.
+                */
+  protected class BufferFactory implements IBufferFactory {
+
+    private IDocument internalGetDocument(IFileEditorInput input) throws CoreException {
+      IDocument document = getDocument(input);
+      if (document != null)
+        return document;
+      return PHPDocumentProvider.this.createDocument(input);
+    }
+
+    public IBuffer createBuffer(IOpenable owner) {
+      if (owner instanceof ICompilationUnit) {
+
+        ICompilationUnit unit = (ICompilationUnit) owner;
+        ICompilationUnit original = (ICompilationUnit) unit.getOriginalElement();
+        IResource resource = original.getResource();
+        if (resource instanceof IFile) {
+          IFileEditorInput providerKey = new FileEditorInput((IFile) resource);
+
+          IDocument document = null;
+          IStatus status = null;
+
+          try {
+            document = internalGetDocument(providerKey);
+          } catch (CoreException x) {
+            status = x.getStatus();
+            document = new Document();
+            initializeDocument(document, providerKey);
+          }
+
+          DocumentAdapter adapter =
+            new DocumentAdapter(unit, document, new DefaultLineTracker(), PHPDocumentProvider.this, providerKey);
+          adapter.setStatus(status);
+          return adapter;
+        }
+
+      }
+      return DocumentAdapter.NULL;
+    }
+  };
+
+  protected static class GlobalAnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+    private ListenerList fListenerList;
+
+    public GlobalAnnotationModelListener() {
+      fListenerList = new ListenerList();
+    }
+
+    public void addListener(IAnnotationModelListener listener) {
+      fListenerList.add(listener);
+    }
+
+    /**
+     * @see IAnnotationModelListenerExtension#modelChanged(AnnotationModelEvent)
+     */
+    public void modelChanged(AnnotationModelEvent event) {
+      Object[] listeners = fListenerList.getListeners();
+      for (int i = 0; i < listeners.length; i++) {
+        Object curr = listeners[i];
+        if (curr instanceof IAnnotationModelListenerExtension) {
+          ((IAnnotationModelListenerExtension) curr).modelChanged(event);
+        }
+      }
+    }
+
+    /**
+     * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+     */
+    public void modelChanged(IAnnotationModel model) {
+      Object[] listeners = fListenerList.getListeners();
+      for (int i = 0; i < listeners.length; i++) {
+        ((IAnnotationModelListener) listeners[i]).modelChanged(model);
+      }
+    }
+
+    public void removeListener(IAnnotationModelListener listener) {
+      fListenerList.remove(listener);
+    }
+  }
+
+  /**
+                * Annotation representating an <code>IProblem</code>.
+                */
+  static protected class ProblemAnnotation extends Annotation implements IJavaAnnotation {
+
+    //    private static Image fgQuickFixImage;
+    //    private static Image fgQuickFixErrorImage;
+    //    private static boolean fgQuickFixImagesInitialized = false;
+
+    private List fOverlaids;
+    private IProblem fProblem;
+    private Image fImage;
+    //    private boolean fQuickFixImagesInitialized = false;
+    private AnnotationType fType;
+
+    public ProblemAnnotation(IProblem problem) {
+
+      fProblem = problem;
+      setLayer(MarkerAnnotation.PROBLEM_LAYER + 1);
+
+      if (IProblem.Task == fProblem.getID())
+        fType = AnnotationType.TASK;
+      else if (fProblem.isWarning())
+        fType = AnnotationType.WARNING;
+      else
+        fType = AnnotationType.ERROR;
+    }
+
+    private void initializeImages() {
+      // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18936
+      //      if (!fQuickFixImagesInitialized) {
+      //        if (indicateQuixFixableProblems() && JavaCorrectionProcessor.hasCorrections(fProblem.getID())) {
+      //          if (!fgQuickFixImagesInitialized) {
+      //            fgQuickFixImage = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_PROBLEM);
+      //            fgQuickFixErrorImage = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_FIXABLE_ERROR);
+      //            fgQuickFixImagesInitialized = true;
+      //          }
+      //          if (fType == AnnotationType.ERROR)
+      //            fImage = fgQuickFixErrorImage;
+      //          else
+      //            fImage = fgQuickFixImage;
+      //        }
+      //        fQuickFixImagesInitialized = true;
+      //      }
+    }
+
+    private boolean indicateQuixFixableProblems() {
+      return PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_CORRECTION_INDICATION);
+    }
+
+    /*
+     * @see Annotation#paint
+     */
+    public void paint(GC gc, Canvas canvas, Rectangle r) {
+      initializeImages();
+      if (fImage != null)
+        drawImage(fImage, gc, canvas, r, SWT.CENTER, SWT.TOP);
+    }
+
+    /*
+     * @see IJavaAnnotation#getImage(Display)
+     */
+    public Image getImage(Display display) {
+      initializeImages();
+      return fImage;
+    }
+
+    /*
+     * @see IJavaAnnotation#getMessage()
+     */
+    public String getMessage() {
+      return fProblem.getMessage();
+    }
+
+    /*
+     * @see IJavaAnnotation#isTemporary()
+     */
+    public boolean isTemporary() {
+      return true;
+    }
+
+    /*
+     * @see IJavaAnnotation#getArguments()
+     */
+    public String[] getArguments() {
+      return isProblem() ? fProblem.getArguments() : null;
+    }
+
+    /*
+     * @see IJavaAnnotation#getId()
+     */
+    public int getId() {
+      return isProblem() ? fProblem.getID() : -1;
+    }
+
+    /*
+     * @see IJavaAnnotation#isProblem()
+     */
+    public boolean isProblem() {
+      return fType == AnnotationType.WARNING || fType == AnnotationType.ERROR;
+    }
+
+    /*
+     * @see IJavaAnnotation#isRelevant()
+     */
+    public boolean isRelevant() {
+      return true;
+    }
+
+    /*
+     * @see IJavaAnnotation#hasOverlay()
+     */
+    public boolean hasOverlay() {
+      return false;
+    }
+
+    /*
+     * @see IJavaAnnotation#addOverlaid(IJavaAnnotation)
+     */
+    public void addOverlaid(IJavaAnnotation annotation) {
+      if (fOverlaids == null)
+        fOverlaids = new ArrayList(1);
+      fOverlaids.add(annotation);
+    }
+
+    /*
+     * @see IJavaAnnotation#removeOverlaid(IJavaAnnotation)
+     */
+    public void removeOverlaid(IJavaAnnotation annotation) {
+      if (fOverlaids != null) {
+        fOverlaids.remove(annotation);
+        if (fOverlaids.size() == 0)
+          fOverlaids = null;
+      }
+    }
+
+    /*
+     * @see IJavaAnnotation#getOverlaidIterator()
+     */
+    public Iterator getOverlaidIterator() {
+      if (fOverlaids != null)
+        return fOverlaids.iterator();
+      return null;
+    }
+
+    public AnnotationType getAnnotationType() {
+      return fType;
+    }
+  };
+  /**
+                * Internal structure for mapping positions to some value. 
+                * The reason for this specific structure is that positions can
+                * change over time. Thus a lookup is based on value and not
+                * on hash value.
+                */
+  protected static class ReverseMap {
+
+    static class Entry {
+      Position fPosition;
+      Object fValue;
+    }
+    private int fAnchor = 0;
+
+    private List fList = new ArrayList(2);
+
+    public ReverseMap() {
+    }
+
+    public void clear() {
+      fList.clear();
+    }
+
+    public Object get(Position position) {
+
+      Entry entry;
+
+      // behind anchor
+      int length = fList.size();
+      for (int i = fAnchor; i < length; i++) {
+        entry = (Entry) fList.get(i);
+        if (entry.fPosition.equals(position)) {
+          fAnchor = i;
+          return entry.fValue;
+        }
+      }
+
+      // before anchor
+      for (int i = 0; i < fAnchor; i++) {
+        entry = (Entry) fList.get(i);
+        if (entry.fPosition.equals(position)) {
+          fAnchor = i;
+          return entry.fValue;
+        }
+      }
+
+      return null;
+    }
+
+    private int getIndex(Position position) {
+      Entry entry;
+      int length = fList.size();
+      for (int i = 0; i < length; i++) {
+        entry = (Entry) fList.get(i);
+        if (entry.fPosition.equals(position))
+          return i;
+      }
+      return -1;
+    }
+
+    public void put(Position position, Object value) {
+      int index = getIndex(position);
+      if (index == -1) {
+        Entry entry = new Entry();
+        entry.fPosition = position;
+        entry.fValue = value;
+        fList.add(entry);
+      } else {
+        Entry entry = (Entry) fList.get(index);
+        entry.fValue = value;
+      }
+    }
+
+    public void remove(Position position) {
+      int index = getIndex(position);
+      if (index > -1)
+        fList.remove(index);
+    }
+  }
+
+  /**
+                * Document that can also be used by a background reconciler.
+                */
+  protected static class PartiallySynchronizedDocument extends Document {
+
+    /*
+     * @see IDocumentExtension#startSequentialRewrite(boolean)
+     */
+    synchronized public void startSequentialRewrite(boolean normalized) {
+      super.startSequentialRewrite(normalized);
+    }
+
+    /*
+     * @see IDocumentExtension#stopSequentialRewrite()
+     */
+    synchronized public void stopSequentialRewrite() {
+      super.stopSequentialRewrite();
+    }
+
+    /*
+     * @see IDocument#get()
+     */
+    synchronized public String get() {
+      return super.get();
+    }
+
+    /*
+     * @see IDocument#get(int, int)
+     */
+    synchronized public String get(int offset, int length) throws BadLocationException {
+      return super.get(offset, length);
+    }
+
+    /*
+     * @see IDocument#getChar(int)
+     */
+    synchronized public char getChar(int offset) throws BadLocationException {
+      return super.getChar(offset);
+    }
+
+    /*
+     * @see IDocument#replace(int, int, String)
+     */
+    synchronized public void replace(int offset, int length, String text) throws BadLocationException {
+      super.replace(offset, length, text);
+    }
+
+    /*
+     * @see IDocument#set(String)
+     */
+    synchronized public void set(String text) {
+      super.set(text);
+    }
+  };
+  //
+  //  private static PHPPartitionScanner HTML_PARTITION_SCANNER = null;
+  //
+  //  private static PHPPartitionScanner PHP_PARTITION_SCANNER = null;
+  //  private static PHPPartitionScanner SMARTY_PARTITION_SCANNER = null;
+  //
+  //  // private final static String[] TYPES= new String[] { PHPPartitionScanner.PHP, PHPPartitionScanner.JAVA_DOC, PHPPartitionScanner.JAVA_MULTILINE_COMMENT };
+  //  private final static String[] TYPES =
+  //    new String[] {
+  //      IPHPPartitionScannerConstants.PHP,
+  //      IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
+  //      IPHPPartitionScannerConstants.HTML,
+  //      IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
+  //      IPHPPartitionScannerConstants.JAVASCRIPT,
+  //      IPHPPartitionScannerConstants.CSS,
+  //      IPHPPartitionScannerConstants.SMARTY,
+  //      IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT };
+  //  private static PHPPartitionScanner XML_PARTITION_SCANNER = null;
+
+  /* Preference key for temporary problems */
+  private final static String HANDLE_TEMPORARY_PROBLEMS = PreferenceConstants.EDITOR_EVALUTE_TEMPORARY_PROBLEMS;
+
+  /** The buffer factory */
+  private IBufferFactory fBufferFactory = new BufferFactory();
+  /** Indicates whether the save has been initialized by this provider */
+  private boolean fIsAboutToSave = false;
+  /** The save policy used by this provider */
+  private ISavePolicy fSavePolicy;
+  /** Internal property changed listener */
+  private IPropertyChangeListener fPropertyListener;
+
+  /** annotation model listener added to all created CU annotation models */
+  private GlobalAnnotationModelListener fGlobalAnnotationModelListener;
+
+  public PHPDocumentProvider() {
+
+    fPropertyListener = new IPropertyChangeListener() {
+      public void propertyChange(PropertyChangeEvent event) {
+        if (HANDLE_TEMPORARY_PROBLEMS.equals(event.getProperty()))
+          enableHandlingTemporaryProblems();
+      }
+    };
+
+    fGlobalAnnotationModelListener = new GlobalAnnotationModelListener();
+
+    PHPeclipsePlugin.getDefault().getPreferenceStore().addPropertyChangeListener(fPropertyListener);
+
+  }
+
+  /**
+   * Sets the document provider's save policy.
+   */
+  public void setSavePolicy(ISavePolicy savePolicy) {
+    fSavePolicy = savePolicy;
+  }
+
+  /**
+   * Creates a compilation unit from the given file.
+   * 
+   * @param file the file from which to create the compilation unit
+   */
+  protected ICompilationUnit createCompilationUnit(IFile file) {
+    Object element = PHPCore.create(file);
+    if (element instanceof ICompilationUnit)
+      return (ICompilationUnit) element;
+    return null;
+  }
+
+  /*
+        * @see AbstractDocumentProvider#createElementInfo(Object)
+        */
+  protected ElementInfo createElementInfo(Object element) throws CoreException {
+
+    if (!(element instanceof IFileEditorInput))
+      return super.createElementInfo(element);
+
+    IFileEditorInput input = (IFileEditorInput) element;
+    ICompilationUnit original = createCompilationUnit(input.getFile());
+    if (original != null) {
+
+      try {
+
+        try {
+          refreshFile(input.getFile());
+        } catch (CoreException x) {
+          handleCoreException(x, PHPEditorMessages.getString("PHPDocumentProvider.error.createElementInfo")); //$NON-NLS-1$
+        }
+
+        IAnnotationModel m = createCompilationUnitAnnotationModel(input);
+        IProblemRequestor r = m instanceof IProblemRequestor ? (IProblemRequestor) m : null;
+        ICompilationUnit c = (ICompilationUnit) original.getSharedWorkingCopy(getProgressMonitor(), fBufferFactory, r);
+
+        DocumentAdapter a = null;
+        try {
+          a = (DocumentAdapter) c.getBuffer();
+        } catch (ClassCastException x) {
+          IStatus status = new Status(IStatus.ERROR, PHPeclipsePlugin.PLUGIN_ID, PHPStatusConstants.TEMPLATE_IO_EXCEPTION, "Shared working copy has wrong buffer", x); //$NON-NLS-1$
+          throw new CoreException(status);
+        }
+
+        _FileSynchronizer f = new _FileSynchronizer(input);
+        f.install();
+
+        CompilationUnitInfo info = new CompilationUnitInfo(a.getDocument(), m, f, c);
+        info.setModificationStamp(computeModificationStamp(input.getFile()));
+        info.fStatus = a.getStatus();
+        info.fEncoding = getPersistedEncoding(input);
+
+        if (r instanceof IProblemRequestorExtension) {
+          IProblemRequestorExtension extension = (IProblemRequestorExtension) r;
+          extension.setIsActive(isHandlingTemporaryProblems());
+        }
+        m.addAnnotationModelListener(fGlobalAnnotationModelListener);
+
+        return info;
+
+      } catch (JavaModelException x) {
+        throw new CoreException(x.getStatus());
+      }
+    } else {
+      return super.createElementInfo(element);
+    }
+  }
+
+  /*
+   * @see AbstractDocumentProvider#disposeElementInfo(Object, ElementInfo)
+   */
+  protected void disposeElementInfo(Object element, ElementInfo info) {
+
+    if (info instanceof CompilationUnitInfo) {
+      CompilationUnitInfo cuInfo = (CompilationUnitInfo) info;
+      cuInfo.fCopy.destroy();
+      cuInfo.fModel.removeAnnotationModelListener(fGlobalAnnotationModelListener);
+    }
+
+    super.disposeElementInfo(element, info);
+  }
+
+  /*
+        * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument, boolean)
+        */
+  protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
+    throws CoreException {
+
+    ElementInfo elementInfo = getElementInfo(element);
+    if (elementInfo instanceof CompilationUnitInfo) {
+      CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
+
+      // update structure, assumes lock on info.fCopy
+      info.fCopy.reconcile();
+
+      ICompilationUnit original = (ICompilationUnit) info.fCopy.getOriginalElement();
+      IResource resource = original.getResource();
+
+      if (resource == null) {
+        // underlying resource has been deleted, just recreate file, ignore the rest
+        super.doSaveDocument(monitor, element, document, overwrite);
+        return;
+      }
+
+      if (resource != null && !overwrite)
+        checkSynchronizationState(info.fModificationStamp, resource);
+
+      if (fSavePolicy != null)
+        fSavePolicy.preSave(info.fCopy);
+
+      // inform about the upcoming content change
+      fireElementStateChanging(element);
+      try {
+        fIsAboutToSave = true;
+        // commit working copy
+        info.fCopy.commit(overwrite, monitor);
+      } catch (CoreException x) {
+        // inform about the failure
+        fireElementStateChangeFailed(element);
+        throw x;
+      } catch (RuntimeException x) {
+        // inform about the failure
+        fireElementStateChangeFailed(element);
+        throw x;
+      } finally {
+        fIsAboutToSave = false;
+      }
+
+      // If here, the dirty state of the editor will change to "not dirty".
+      // Thus, the state changing flag will be reset.
+
+      AbstractMarkerAnnotationModel model = (AbstractMarkerAnnotationModel) info.fModel;
+      model.updateMarkers(info.fDocument);
+
+      if (resource != null)
+        info.setModificationStamp(computeModificationStamp(resource));
+
+      if (fSavePolicy != null) {
+        ICompilationUnit unit = fSavePolicy.postSave(original);
+        if (unit != null) {
+          IResource r = unit.getResource();
+          IMarker[] markers = r.findMarkers(IMarker.MARKER, true, IResource.DEPTH_ZERO);
+          if (markers != null && markers.length > 0) {
+            for (int i = 0; i < markers.length; i++)
+              model.updateMarker(markers[i], info.fDocument, null);
+          }
+        }
+      }
+
+    } else {
+      super.doSaveDocument(monitor, element, document, overwrite);
+    }
+  }
+
+  /**
+   * Replaces createAnnotionModel of the super class.
+   */
+  protected IAnnotationModel createCompilationUnitAnnotationModel(Object element) throws CoreException {
+               if ( !(element instanceof IFileEditorInput))
+                       throw new CoreException(PHPUIStatus.createError(
+                               IJavaModelStatusConstants.INVALID_RESOURCE_TYPE, "", null)); //$NON-NLS-1$
+               
+               IFileEditorInput input= (IFileEditorInput) element;
+               return new CompilationUnitAnnotationModel(input);
+  }
+
+  //  private IDocumentPartitioner createCSSPartitioner() {
+  //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  //  }
+
+  /* (non-Javadoc)
+   * Method declared on AbstractDocumentProvider
+   */
+  protected IDocument createDocument(Object element) throws CoreException {
+    if (element instanceof IEditorInput) {
+      Document document = new PartiallySynchronizedDocument();
+      if (setDocumentContent(document, (IEditorInput) element, getEncoding(element))) {
+        initializeDocument(document, (IEditorInput) element);
+
+        //     
+        //    IDocument document = super.createDocument(element);
+        //    if (document != null) {
+        //        IDocumentPartitioner partitioner = null;
+        //        if (element instanceof FileEditorInput) {
+        //          IFile file = (IFile) ((FileEditorInput) element).getAdapter(IFile.class);
+        //          String filename = file.getLocation().toString();
+        //          String extension = filename.substring(filename.lastIndexOf("."), filename.length());
+        //          //   System.out.println(extension);
+        //          if (extension.equalsIgnoreCase(".html") || extension.equalsIgnoreCase(".htm")) {
+        //            // html
+        //            partitioner = createHTMLPartitioner();
+        //          } else if (extension.equalsIgnoreCase(".xml")) {
+        //            // xml
+        //            partitioner = createXMLPartitioner();
+        //          } else if (extension.equalsIgnoreCase(".js")) {
+        //            // javascript
+        //            partitioner = createJavaScriptPartitioner();
+        //          } else if (extension.equalsIgnoreCase(".css")) {
+        //            // cascading style sheets
+        //            partitioner = createCSSPartitioner();
+        //          } else if (extension.equalsIgnoreCase(".tpl")) {
+        //            // smarty ?
+        //            partitioner = createSmartyPartitioner();
+        //          } else if (extension.equalsIgnoreCase(".inc")) {
+        //            // php include files ?
+        //            partitioner = createIncludePartitioner();
+        //          }
+        //        }
+        //
+        //        if (partitioner == null) {
+        //          partitioner = createPHPPartitioner();
+        //        }
+        //        document.setDocumentPartitioner(partitioner);
+        //        partitioner.connect(document);
+      }
+      return document;
+    }
+    return null;
+  }
+
+  //  /**
+  //   * Return a partitioner for .html files.
+  //   */
+  //  private IDocumentPartitioner createHTMLPartitioner() {
+  //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  //  }
+  //
+  //  private IDocumentPartitioner createIncludePartitioner() {
+  //    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
+  //  }
+  //
+  //  private IDocumentPartitioner createJavaScriptPartitioner() {
+  //    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  //  }
+
+  /**
+   * Creates a line tracker working with the same line delimiters as the document
+   * of the given element. Assumes the element to be managed by this document provider.
+   * 
+   * @param element the element serving as blue print
+   * @return a line tracker based on the same line delimiters as the element's document
+   */
+  public ILineTracker createLineTracker(Object element) {
+    return new DefaultLineTracker();
+  }
+
+  //  /**
+  //   * Return a partitioner for .php files.
+  //   */
+  //  private IDocumentPartitioner createPHPPartitioner() {
+  //    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
+  //  }
+  //
+  //  private IDocumentPartitioner createSmartyPartitioner() {
+  //    return new DefaultPartitioner(getSmartyPartitionScanner(), TYPES);
+  //  }
+  //
+  //  private IDocumentPartitioner createXMLPartitioner() {
+  //    return new DefaultPartitioner(getXMLPartitionScanner(), TYPES);
+  //  }
+  //
+  //  /**
+  //   * Return a scanner for creating html partitions.
+  //   */
+  //  private PHPPartitionScanner getHTMLPartitionScanner() {
+  //    if (HTML_PARTITION_SCANNER == null)
+  //      HTML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.HTML_FILE);
+  //    return HTML_PARTITION_SCANNER;
+  //  }
+  //  /**
+  //   * Return a scanner for creating php partitions.
+  //   */
+  //  private PHPPartitionScanner getPHPPartitionScanner() {
+  //    if (PHP_PARTITION_SCANNER == null)
+  //      PHP_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.PHP_FILE);
+  //    return PHP_PARTITION_SCANNER;
+  //  }
+  //
+  //  /**
+  //   * Return a scanner for creating smarty partitions.
+  //   */
+  //  private PHPPartitionScanner getSmartyPartitionScanner() {
+  //    if (SMARTY_PARTITION_SCANNER == null)
+  //      SMARTY_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.SMARTY_FILE);
+  //    return SMARTY_PARTITION_SCANNER;
+  //  }
+  //
+  //  /**
+  //   * Return a scanner for creating xml partitions.
+  //   */
+  //  private PHPPartitionScanner getXMLPartitionScanner() {
+  //    if (XML_PARTITION_SCANNER == null)
+  //      XML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.XML_FILE);
+  //    return XML_PARTITION_SCANNER;
+  //  }
+
+  protected void initializeDocument(IDocument document, IEditorInput editorInput) {
+    if (document != null) {
+      JavaTextTools tools = PHPeclipsePlugin.getDefault().getJavaTextTools();
+      IDocumentPartitioner partitioner = null;
+      if (editorInput != null && editorInput instanceof FileEditorInput) {
+        IFile file = (IFile) ((FileEditorInput) editorInput).getAdapter(IFile.class);
+        String filename = file.getLocation().toString();
+        String extension = filename.substring(filename.lastIndexOf("."), filename.length());
+        partitioner = tools.createDocumentPartitioner(extension);
+      } else {
+        partitioner = tools.createDocumentPartitioner(".php");
+      }
+      document.setDocumentPartitioner(partitioner);
+      partitioner.connect(document);
+    }
+  }
+
+  /*
+   * @see AbstractDocumentProvider#resetDocument(Object)
+   */
+  public void resetDocument(Object element) throws CoreException {
+    if (element == null)
+      return;
+
+    ElementInfo elementInfo = getElementInfo(element);
+    if (elementInfo instanceof CompilationUnitInfo) {
+      CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
+
+      IDocument document;
+      IStatus status = null;
+
+      try {
+
+        ICompilationUnit original = (ICompilationUnit) info.fCopy.getOriginalElement();
+        IResource resource = original.getResource();
+        if (resource instanceof IFile) {
+
+          IFile file = (IFile) resource;
+
+          try {
+            refreshFile(file);
+          } catch (CoreException x) {
+            handleCoreException(x, PHPEditorMessages.getString("PHPDocumentProvider.error.resetDocument")); //$NON-NLS-1$
+          }
+
+          IFileEditorInput input = new FileEditorInput(file);
+          document = super.createDocument(input);
+
+        } else {
+          document = new Document();
+        }
+
+      } catch (CoreException x) {
+        document = new Document();
+        status = x.getStatus();
+      }
+
+      fireElementContentAboutToBeReplaced(element);
+
+      removeUnchangedElementListeners(element, info);
+      info.fDocument.set(document.get());
+      info.fCanBeSaved = false;
+      info.fStatus = status;
+      addUnchangedElementListeners(element, info);
+
+      fireElementContentReplaced(element);
+      fireElementDirtyStateChanged(element, false);
+
+    } else {
+      super.resetDocument(element);
+    }
+  }
+  /**
+        * Saves the content of the given document to the given element.
+        * This is only performed when this provider initiated the save.
+        * 
+        * @param monitor the progress monitor
+        * @param element the element to which to save
+        * @param document the document to save
+        * @param overwrite <code>true</code> if the save should be enforced
+        */
+  public void saveDocumentContent(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
+    throws CoreException {
+
+    if (!fIsAboutToSave)
+      return;
+
+    if (element instanceof IFileEditorInput) {
+      IFileEditorInput input = (IFileEditorInput) element;
+      try {
+        String encoding = getEncoding(element);
+        if (encoding == null)
+          encoding = ResourcesPlugin.getEncoding();
+        InputStream stream = new ByteArrayInputStream(document.get().getBytes(encoding));
+        IFile file = input.getFile();
+        file.setContents(stream, overwrite, true, monitor);
+      } catch (IOException x) {
+        IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin.PLUGIN_ID, IStatus.OK, x.getMessage(), x);
+        throw new CoreException(s);
+      }
+    }
+  }
+  /**
+   * Returns the underlying resource for the given element.
+   * 
+   * @param the element
+   * @return the underlying resource of the given element
+   */
+  public IResource getUnderlyingResource(Object element) {
+    if (element instanceof IFileEditorInput) {
+      IFileEditorInput input = (IFileEditorInput) element;
+      return input.getFile();
+    }
+    return null;
+  }
+
+  /**
+   * Returns the working copy this document provider maintains for the given
+   * element.
+   * 
+   * @param element the given element
+   * @return the working copy for the given element
+   */
+  ICompilationUnit getWorkingCopy(IEditorInput element) {
+
+    ElementInfo elementInfo = getElementInfo(element);
+    if (elementInfo instanceof CompilationUnitInfo) {
+      CompilationUnitInfo info = (CompilationUnitInfo) elementInfo;
+      return info.fCopy;
+    }
+
+    return null;
+  }
+
+  /**
+   * Gets the BufferFactory.
+   */
+  public IBufferFactory getBufferFactory() {
+    return fBufferFactory;
+  }
+
+  /**
+   * Shuts down this document provider.
+   */
+  public void shutdown() {
+
+    PHPeclipsePlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyListener);
+
+    Iterator e = getConnectedElements();
+    while (e.hasNext())
+      disconnect(e.next());
+  }
+
+  /**
+   * Returns the preference whether handling temporary problems is enabled.
+   */
+  protected boolean isHandlingTemporaryProblems() {
+    IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
+    return store.getBoolean(HANDLE_TEMPORARY_PROBLEMS);
+  }
+
+  /**
+   * Switches the state of problem acceptance according to the value in the preference store.
+   */
+  protected void enableHandlingTemporaryProblems() {
+    boolean enable = isHandlingTemporaryProblems();
+    for (Iterator iter = getConnectedElements(); iter.hasNext();) {
+      ElementInfo element = getElementInfo(iter.next());
+      if (element instanceof CompilationUnitInfo) {
+        CompilationUnitInfo info = (CompilationUnitInfo) element;
+        if (info.fModel instanceof IProblemRequestorExtension) {
+          IProblemRequestorExtension extension = (IProblemRequestorExtension) info.fModel;
+          extension.setIsActive(enable);
+        }
+      }
+    }
+  }
+
+  /**
+   * Adds a listener that reports changes from all compilation unit annotation models.
+   */
+  public void addGlobalAnnotationModelListener(IAnnotationModelListener listener) {
+    fGlobalAnnotationModelListener.addListener(listener);
+  }
+
+  /**
+   * Removes the listener.
+   */
+  public void removeGlobalAnnotationModelListener(IAnnotationModelListener listener) {
+    fGlobalAnnotationModelListener.removeListener(listener);
+  }
 
-       // private final static String[] TYPES= new String[] { PHPPartitionScanner.PHP, PHPPartitionScanner.JAVA_DOC, PHPPartitionScanner.JAVA_MULTILINE_COMMENT };
-private final static String[] TYPES= new String[] { PHPPartitionScanner.PHP, PHPPartitionScanner.JAVA_MULTILINE_COMMENT };
-
-       private static PHPPartitionScanner fgScanner= null;
-
-       public PHPDocumentProvider() {
-               super();
-       }
-       
-       /* (non-Javadoc)
-        * Method declared on AbstractDocumentProvider
-        */
-        protected IDocument createDocument(Object element) throws CoreException {
-               IDocument document= super.createDocument(element);
-               if (document != null) {
-                       IDocumentPartitioner partitioner= createJavaPartitioner();
-                       document.setDocumentPartitioner(partitioner);
-                       partitioner.connect(document);
-               }
-               return document;
-       }
-       
-       /**
-        * Return a partitioner for .java files.
-        */
-        private IDocumentPartitioner createJavaPartitioner() {
-               return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
-       }
-       
-       /**
-        * Return a scanner for creating java partitions.
-        */
-        private PHPPartitionScanner getPHPPartitionScanner() {
-               if (fgScanner == null)
-                       fgScanner= new PHPPartitionScanner();
-               return fgScanner;
-       }
+  /**
+   * Returns whether the given element is connected to this document provider.
+   * 
+   * @param element the element
+   * @return <code>true</code> if the element is connected, <code>false</code> otherwise
+   */
+  boolean isConnected(Object element) {
+    return getElementInfo(element) != null;
+  }
 }