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();
+ }
}