1 package net.sourceforge.phpeclipse.phpeditor;
3 /**********************************************************************
4 Copyright (c) 2000, 2002 IBM Corp. and others.
5 All rights reserved. This program and the accompanying materials
6 are made available under the terms of the Common Public License v1.0
7 which accompanies this distribution, and is available at
8 http://www.eclipse.org/legal/cpl-v10.html
11 IBM Corporation - Initial implementation
12 Klaus Hartlage - www.eclipseproject.de
13 **********************************************************************/
15 import java.util.ArrayList;
16 import java.util.Iterator;
17 import java.util.List;
19 import net.sourceforge.phpdt.core.IProblemRequestor;
20 import net.sourceforge.phpdt.core.compiler.IProblem;
21 import net.sourceforge.phpdt.internal.ui.text.java.IProblemRequestorExtension;
22 import net.sourceforge.phpeclipse.phpeditor.php.IPHPPartitionScannerConstants;
23 import net.sourceforge.phpeclipse.phpeditor.php.PHPPartitionScanner;
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.core.resources.IMarker;
27 import org.eclipse.core.resources.IMarkerDelta;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.jface.text.DefaultLineTracker;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.IDocumentPartitioner;
33 import org.eclipse.jface.text.ILineTracker;
34 import org.eclipse.jface.text.Position;
35 import org.eclipse.jface.text.rules.DefaultPartitioner;
36 import org.eclipse.jface.text.source.Annotation;
37 import org.eclipse.jface.text.source.AnnotationModelEvent;
38 import org.eclipse.jface.text.source.IAnnotationModel;
39 import org.eclipse.jface.text.source.IAnnotationModelListener;
40 import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
41 import org.eclipse.jface.util.ListenerList;
42 import org.eclipse.ui.IFileEditorInput;
43 import org.eclipse.ui.editors.text.FileDocumentProvider;
44 import org.eclipse.ui.part.FileEditorInput;
45 import org.eclipse.ui.texteditor.MarkerAnnotation;
46 import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
49 * The PHPDocumentProvider provides the IDocuments used by java editors.
52 public class PHPDocumentProvider extends FileDocumentProvider {
54 // private final static String[] TYPES= new String[] { PHPPartitionScanner.PHP, PHPPartitionScanner.JAVA_DOC, PHPPartitionScanner.JAVA_MULTILINE_COMMENT };
55 private final static String[] TYPES =
57 IPHPPartitionScannerConstants.PHP,
58 IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
59 IPHPPartitionScannerConstants.HTML,
60 IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
61 IPHPPartitionScannerConstants.JAVASCRIPT,
62 IPHPPartitionScannerConstants.CSS,
63 IPHPPartitionScannerConstants.SMARTY,
64 IPHPPartitionScannerConstants.SMARTY_MULTILINE_COMMENT };
66 private static PHPPartitionScanner PHP_PARTITION_SCANNER = null;
67 private static PHPPartitionScanner HTML_PARTITION_SCANNER = null;
68 private static PHPPartitionScanner XML_PARTITION_SCANNER = null;
69 private static PHPPartitionScanner SMARTY_PARTITION_SCANNER = null;
71 /** annotation model listener added to all created CU annotation models */
72 // private GlobalAnnotationModelListener fGlobalAnnotationModelListener;
75 * Internal structure for mapping positions to some value.
76 * The reason for this specific structure is that positions can
77 * change over time. Thus a lookup is based on value and not
80 protected static class ReverseMap {
87 private List fList = new ArrayList(2);
88 private int fAnchor = 0;
93 public Object get(Position position) {
98 int length = fList.size();
99 for (int i = fAnchor; i < length; i++) {
100 entry = (Entry) fList.get(i);
101 if (entry.fPosition.equals(position)) {
108 for (int i = 0; i < fAnchor; i++) {
109 entry = (Entry) fList.get(i);
110 if (entry.fPosition.equals(position)) {
119 private int getIndex(Position position) {
121 int length = fList.size();
122 for (int i = 0; i < length; i++) {
123 entry = (Entry) fList.get(i);
124 if (entry.fPosition.equals(position))
130 public void put(Position position, Object value) {
131 int index = getIndex(position);
133 Entry entry = new Entry();
134 entry.fPosition = position;
135 entry.fValue = value;
138 Entry entry = (Entry) fList.get(index);
139 entry.fValue = value;
143 public void remove(Position position) {
144 int index = getIndex(position);
149 public void clear() {
155 * Annotation model dealing with java marker annotations and temporary problems.
156 * Also acts as problem requestor for its compilation unit. Initialiy inactive. Must explicitly be
159 protected class CompilationUnitAnnotationModel
160 extends ResourceMarkerAnnotationModel
161 implements IProblemRequestor, IProblemRequestorExtension {
163 private IFileEditorInput fInput;
164 private List fCollectedProblems;
165 private List fGeneratedAnnotations;
166 private IProgressMonitor fProgressMonitor;
167 private boolean fIsActive = false;
169 private ReverseMap fReverseMap = new ReverseMap();
170 private List fPreviouslyOverlaid = null;
171 private List fCurrentlyOverlaid = new ArrayList();
173 public CompilationUnitAnnotationModel(IFileEditorInput input) {
174 super(input.getFile());
178 protected MarkerAnnotation createMarkerAnnotation(IMarker marker) {
179 return new JavaMarkerAnnotation(marker);
182 protected Position createPositionFromProblem(IProblem problem) {
183 int start = problem.getSourceStart();
187 int length = problem.getSourceEnd() - problem.getSourceStart() + 1;
191 return new Position(start, length);
194 protected void update(IMarkerDelta[] markerDeltas) {
196 super.update(markerDeltas);
198 // if (markerDeltas != null && markerDeltas.length > 0) {
200 // ICompilationUnit workingCopy = getWorkingCopy(fInput);
201 // if (workingCopy != null)
202 // workingCopy.reconcile(true, null);
203 // } catch (JavaModelException ex) {
204 // handleCoreException(ex, ex.getMessage());
210 * @see IProblemRequestor#beginReporting()
212 public void beginReporting() {
213 // ICompilationUnit unit= getWorkingCopy(fInput);
214 // if (unit != null && unit.getJavaProject().isOnClasspath(unit))
215 // fCollectedProblems= new ArrayList();
217 fCollectedProblems = null;
221 * @see IProblemRequestor#acceptProblem(IProblem)
223 public void acceptProblem(IProblem problem) {
225 fCollectedProblems.add(problem);
229 * @see IProblemRequestor#endReporting()
231 public void endReporting() {
235 if (fProgressMonitor != null && fProgressMonitor.isCanceled())
238 boolean isCanceled = false;
239 boolean temporaryProblemsChanged = false;
240 fPreviouslyOverlaid = fCurrentlyOverlaid;
241 fCurrentlyOverlaid = new ArrayList();
243 synchronized (fAnnotations) {
245 if (fGeneratedAnnotations.size() > 0) {
246 temporaryProblemsChanged = true;
247 removeAnnotations(fGeneratedAnnotations, false, true);
248 fGeneratedAnnotations.clear();
251 if (fCollectedProblems != null && fCollectedProblems.size() > 0) {
253 Iterator e = fCollectedProblems.iterator();
254 while (e.hasNext()) {
256 IProblem problem = (IProblem) e.next();
258 if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
263 Position position = createPositionFromProblem(problem);
264 if (position != null) {
266 // ProblemAnnotation annotation= new ProblemAnnotation(problem);
267 // overlayMarkers(position, annotation);
268 // fGeneratedAnnotations.add(annotation);
269 // addAnnotation(annotation, position, false);
271 temporaryProblemsChanged = true;
275 fCollectedProblems.clear();
278 removeMarkerOverlays(isCanceled);
279 fPreviouslyOverlaid.clear();
280 fPreviouslyOverlaid = null;
283 // if (temporaryProblemsChanged)
284 // fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), false));
287 private void removeMarkerOverlays(boolean isCanceled) {
289 fCurrentlyOverlaid.addAll(fPreviouslyOverlaid);
290 } else if (fPreviouslyOverlaid != null) {
291 Iterator e = fPreviouslyOverlaid.iterator();
292 while (e.hasNext()) {
293 JavaMarkerAnnotation annotation = (JavaMarkerAnnotation) e.next();
294 annotation.setOverlay(null);
300 * Overlays value with problem annotation.
301 * @param problemAnnotation
303 // private void setOverlay(Object value, ProblemAnnotation problemAnnotation) {
304 // if (value instanceof JavaMarkerAnnotation) {
305 // JavaMarkerAnnotation annotation= (JavaMarkerAnnotation) value;
306 // if (annotation.isProblem()) {
307 // annotation.setOverlay(problemAnnotation);
308 // fPreviouslyOverlaid.remove(annotation);
309 // fCurrentlyOverlaid.add(annotation);
314 // private void overlayMarkers(Position position, ProblemAnnotation problemAnnotation) {
315 // Object value= getAnnotations(position);
316 // if (value instanceof List) {
317 // List list= (List) value;
318 // for (Iterator e = list.iterator(); e.hasNext();)
319 // setOverlay(e.next(), problemAnnotation);
321 // setOverlay(value, problemAnnotation);
326 * Tells this annotation model to collect temporary problems from now on.
328 private void startCollectingProblems() {
329 fCollectedProblems = new ArrayList();
330 fGeneratedAnnotations = new ArrayList();
334 * Tells this annotation model to no longer collect temporary problems.
336 private void stopCollectingProblems() {
337 if (fGeneratedAnnotations != null) {
338 removeAnnotations(fGeneratedAnnotations, true, true);
339 fGeneratedAnnotations.clear();
341 fCollectedProblems = null;
342 fGeneratedAnnotations = null;
346 * @see AnnotationModel#fireModelChanged()
348 // protected void fireModelChanged() {
349 // fireModelChanged(new CompilationUnitAnnotationModelEvent(this, getResource(), true));
353 * @see IProblemRequestor#isActive()
355 public boolean isActive() {
356 return fIsActive && (fCollectedProblems != null);
360 * @see IProblemRequestorExtension#setProgressMonitor(IProgressMonitor)
362 public void setProgressMonitor(IProgressMonitor monitor) {
363 fProgressMonitor = monitor;
367 * @see IProblemRequestorExtension#setIsActive(boolean)
369 public void setIsActive(boolean isActive) {
370 if (fIsActive != isActive) {
371 fIsActive = isActive;
373 startCollectingProblems();
375 stopCollectingProblems();
379 private Object getAnnotations(Position position) {
380 return fReverseMap.get(position);
384 * @see AnnotationModel#addAnnotation(Annotation, Position, boolean)
386 protected void addAnnotation(Annotation annotation, Position position, boolean fireModelChanged) {
387 super.addAnnotation(annotation, position, fireModelChanged);
389 Object cached = fReverseMap.get(position);
391 fReverseMap.put(position, annotation);
392 else if (cached instanceof List) {
393 List list = (List) cached;
394 list.add(annotation);
395 } else if (cached instanceof Annotation) {
396 List list = new ArrayList(2);
398 list.add(annotation);
399 fReverseMap.put(position, list);
404 * @see AnnotationModel#removeAllAnnotations(boolean)
406 protected void removeAllAnnotations(boolean fireModelChanged) {
407 super.removeAllAnnotations(fireModelChanged);
412 * @see AnnotationModel#removeAnnotation(Annotation, boolean)
414 protected void removeAnnotation(Annotation annotation, boolean fireModelChanged) {
415 Position position = getPosition(annotation);
416 Object cached = fReverseMap.get(position);
417 if (cached instanceof List) {
418 List list = (List) cached;
419 list.remove(annotation);
420 if (list.size() == 1) {
421 fReverseMap.put(position, list.get(0));
424 } else if (cached instanceof Annotation) {
425 fReverseMap.remove(position);
428 super.removeAnnotation(annotation, fireModelChanged);
432 protected static class GlobalAnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
434 private ListenerList fListenerList;
436 public GlobalAnnotationModelListener() {
437 fListenerList = new ListenerList();
441 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
443 public void modelChanged(IAnnotationModel model) {
444 Object[] listeners = fListenerList.getListeners();
445 for (int i = 0; i < listeners.length; i++) {
446 ((IAnnotationModelListener) listeners[i]).modelChanged(model);
451 * @see IAnnotationModelListenerExtension#modelChanged(AnnotationModelEvent)
453 public void modelChanged(AnnotationModelEvent event) {
454 Object[] listeners = fListenerList.getListeners();
455 for (int i = 0; i < listeners.length; i++) {
456 Object curr = listeners[i];
457 if (curr instanceof IAnnotationModelListenerExtension) {
458 ((IAnnotationModelListenerExtension) curr).modelChanged(event);
463 public void addListener(IAnnotationModelListener listener) {
464 fListenerList.add(listener);
467 public void removeListener(IAnnotationModelListener listener) {
468 fListenerList.remove(listener);
472 public PHPDocumentProvider() {
475 // fGlobalAnnotationModelListener= new GlobalAnnotationModelListener();
480 * Method declared on AbstractDocumentProvider
482 protected IDocument createDocument(Object element) throws CoreException {
483 IDocument document = super.createDocument(element);
484 if (document != null) {
485 // int fileType = 0; // PHP
486 IDocumentPartitioner partitioner = null;
487 if (element instanceof FileEditorInput) {
488 IFile file = (IFile) ((FileEditorInput) element).getAdapter(IFile.class);
489 String filename = file.getLocation().toString();
490 String extension = filename.substring(filename.lastIndexOf("."), filename.length());
491 // System.out.println(extension);
492 if (extension.equalsIgnoreCase(".html") || extension.equalsIgnoreCase(".htm")) {
494 partitioner = createHTMLPartitioner();
495 } else if (extension.equalsIgnoreCase(".xml")) {
497 partitioner = createXMLPartitioner();
498 } else if (extension.equalsIgnoreCase(".js")) {
500 partitioner = createJavaScriptPartitioner();
501 } else if (extension.equalsIgnoreCase(".css")) {
502 // cascading style sheets
503 partitioner = createCSSPartitioner();
504 } else if (extension.equalsIgnoreCase(".tpl")) {
506 partitioner = createSmartyPartitioner();
507 } else if (extension.equalsIgnoreCase(".inc")) {
508 // php include files ?
509 partitioner = createIncludePartitioner();
513 if (partitioner == null) {
514 partitioner = createPHPPartitioner();
516 document.setDocumentPartitioner(partitioner);
517 partitioner.connect(document);
523 * Return a partitioner for .php files.
525 private IDocumentPartitioner createPHPPartitioner() {
526 return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
530 * Return a partitioner for .html files.
532 private IDocumentPartitioner createHTMLPartitioner() {
533 return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
536 private IDocumentPartitioner createXMLPartitioner() {
537 return new DefaultPartitioner(getXMLPartitionScanner(), TYPES);
540 private IDocumentPartitioner createJavaScriptPartitioner() {
541 return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
544 private IDocumentPartitioner createCSSPartitioner() {
545 return new DefaultPartitioner(getHTMLPartitionScanner(), TYPES);
548 private IDocumentPartitioner createSmartyPartitioner() {
549 return new DefaultPartitioner(getSmartyPartitionScanner(), TYPES);
552 private IDocumentPartitioner createIncludePartitioner() {
553 return new DefaultPartitioner(getPHPPartitionScanner(), TYPES);
556 * Return a scanner for creating php partitions.
558 private PHPPartitionScanner getPHPPartitionScanner() {
559 if (PHP_PARTITION_SCANNER == null)
560 PHP_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.PHP_FILE);
561 return PHP_PARTITION_SCANNER;
565 * Return a scanner for creating html partitions.
567 private PHPPartitionScanner getHTMLPartitionScanner() {
568 if (HTML_PARTITION_SCANNER == null)
569 HTML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.HTML_FILE);
570 return HTML_PARTITION_SCANNER;
574 * Return a scanner for creating xml partitions.
576 private PHPPartitionScanner getXMLPartitionScanner() {
577 if (XML_PARTITION_SCANNER == null)
578 XML_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.XML_FILE);
579 return XML_PARTITION_SCANNER;
583 * Return a scanner for creating smarty partitions.
585 private PHPPartitionScanner getSmartyPartitionScanner() {
586 if (SMARTY_PARTITION_SCANNER == null)
587 SMARTY_PARTITION_SCANNER = new PHPPartitionScanner(IPHPPartitionScannerConstants.SMARTY_FILE);
588 return SMARTY_PARTITION_SCANNER;
592 * Creates a line tracker working with the same line delimiters as the document
593 * of the given element. Assumes the element to be managed by this document provider.
595 * @param element the element serving as blue print
596 * @return a line tracker based on the same line delimiters as the element's document
598 public ILineTracker createLineTracker(Object element) {
599 return new DefaultLineTracker();