package net.sourceforge.phpeclipse.phpeditor; /********************************************************************** Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html Contributors: IBM Corporation - Initial implementation 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.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 org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel; /** * 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[] { 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(); } }