stack frame bugs fixed
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPDocumentProvider.java
index 919bdd9..891f7f2 100644 (file)
@@ -12,55 +12,590 @@ Contributors:
     Klaus Hartlage - www.eclipseproject.de
 **********************************************************************/
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import net.sourceforge.phpdt.core.IProblemRequestor;
+import net.sourceforge.phpdt.core.compiler.IProblem;
+import net.sourceforge.phpdt.internal.ui.text.java.IProblemRequestorExtension;
+import net.sourceforge.phpeclipse.phpeditor.php.IPHPPartitionScannerConstants;
+import net.sourceforge.phpeclipse.phpeditor.php.PHPPartitionScanner;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.DefaultLineTracker;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.Position;
 import org.eclipse.jface.text.rules.DefaultPartitioner;
-import org.eclipse.jface.text.rules.RuleBasedPartitioner;
+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.ListenerList;
+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.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 {
 
-       // 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;
-       }
+  // 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 PHP_PARTITION_SCANNER = null;
+  private static PHPPartitionScanner HTML_PARTITION_SCANNER = null;
+  private static PHPPartitionScanner XML_PARTITION_SCANNER = null;
+  private static PHPPartitionScanner SMARTY_PARTITION_SCANNER = null;
+
+  /** annotation model listener added to all created CU annotation models */
+  //           private GlobalAnnotationModelListener fGlobalAnnotationModelListener;   
+
+  /**
+                * 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 List fList = new ArrayList(2);
+    private int fAnchor = 0;
+
+    public ReverseMap() {
+    }
+
+    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);
+    }
+
+    public void clear() {
+      fList.clear();
+    }
+  };
+
+  /**
+                * 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 IFileEditorInput fInput;
+    private List fCollectedProblems;
+    private List fGeneratedAnnotations;
+    private IProgressMonitor fProgressMonitor;
+    private boolean fIsActive = false;
+
+    private ReverseMap fReverseMap = new ReverseMap();
+    private List fPreviouslyOverlaid = null;
+    private List fCurrentlyOverlaid = new ArrayList();
+
+    public CompilationUnitAnnotationModel(IFileEditorInput input) {
+      super(input.getFile());
+      fInput = input;
+    }
+
+    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);
+    }
+
+    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());
+      //                                               }
+      //                                       }
+    }
+
+    /*
+     * @see IProblemRequestor#beginReporting()
+     */
+    public void beginReporting() {
+      //                                       ICompilationUnit unit= getWorkingCopy(fInput);
+      //                                       if (unit != null && unit.getJavaProject().isOnClasspath(unit))
+      //                                               fCollectedProblems= new ArrayList();
+      //                                       else
+      fCollectedProblems = null;
+    }
+
+    /*
+     * @see IProblemRequestor#acceptProblem(IProblem)
+     */
+    public void acceptProblem(IProblem problem) {
+      if (isActive())
+        fCollectedProblems.add(problem);
+    }
+
+    /*
+     * @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 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);
+        }
+      }
+    }
+
+    /**
+     * 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;
+    }
+
+    /*
+     * @see AnnotationModel#fireModelChanged()
+     */
+    //                         protected void fireModelChanged() {
+    //                                 fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), true));
+    //                         }
+
+    /*
+     * @see IProblemRequestor#isActive()
+     */
+    public boolean isActive() {
+      return fIsActive && (fCollectedProblems != null);
+    }
+
+    /*
+     * @see IProblemRequestorExtension#setProgressMonitor(IProgressMonitor)
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+      fProgressMonitor = monitor;
+    }
+
+    /*
+     * @see IProblemRequestorExtension#setIsActive(boolean)
+     */
+    public void setIsActive(boolean isActive) {
+      if (fIsActive != isActive) {
+        fIsActive = isActive;
+        if (fIsActive)
+          startCollectingProblems();
+        else
+          stopCollectingProblems();
+      }
+    }
+
+    private Object getAnnotations(Position position) {
+      return fReverseMap.get(position);
+    }
+
+    /*
+     * @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 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);
+    }
+  };
+
+  protected static class GlobalAnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+    private ListenerList fListenerList;
+
+    public GlobalAnnotationModelListener() {
+      fListenerList = new ListenerList();
+    }
+
+    /**
+     * @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);
+      }
+    }
+
+    /**
+     * @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);
+        }
+      }
+    }
+
+    public void addListener(IAnnotationModelListener listener) {
+      fListenerList.add(listener);
+    }
+
+    public void removeListener(IAnnotationModelListener listener) {
+      fListenerList.remove(listener);
+    }
+  };
+
+  public PHPDocumentProvider() {
+    super();
+
+    //         fGlobalAnnotationModelListener= new GlobalAnnotationModelListener();
+
+  }
+
+  /* (non-Javadoc)
+   * Method declared on AbstractDocumentProvider
+   */
+  protected IDocument createDocument(Object element) throws CoreException {
+    IDocument document = super.createDocument(element);
+    if (document != null) {
+      //  int fileType = 0; // PHP 
+      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 a partitioner for .php files.
+   */
+  private IDocumentPartitioner createPHPPartitioner() {
+    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
+  }
+
+  /**
+   * Return a partitioner for .html files.
+   */
+  private IDocumentPartitioner createHTMLPartitioner() {
+    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  }
+
+  private IDocumentPartitioner createXMLPartitioner() {
+    return new DefaultPartitioner(getXMLPartitionScanner(), TYPES);
+  }
+
+  private IDocumentPartitioner createJavaScriptPartitioner() {
+    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  }
+
+  private IDocumentPartitioner createCSSPartitioner() {
+    return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
+  }
+
+  private IDocumentPartitioner createSmartyPartitioner() {
+    return new DefaultPartitioner(getSmartyPartitionScanner(), TYPES);
+  }
+
+  private IDocumentPartitioner createIncludePartitioner() {
+    return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
+  }
+  /**
+   * 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 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 xml partitions.
+   */
+  private PHPPartitionScanner getXMLPartitionScanner() {
+    if (XML_PARTITION_SCANNER == null)
+      XML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.XML_FILE);
+    return XML_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;
+  }
+
+  /**
+   * 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();
+  }
 }