X-Git-Url: http://secure.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/folding/DefaultJavaFoldingStructureProvider.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/folding/DefaultJavaFoldingStructureProvider.java new file mode 100644 index 0000000..6011085 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/folding/DefaultJavaFoldingStructureProvider.java @@ -0,0 +1,537 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation + *******************************************************************************/ +package net.sourceforge.phpdt.internal.ui.text.folding; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.sourceforge.phpdt.core.ElementChangedEvent; +import net.sourceforge.phpdt.core.IElementChangedListener; +import net.sourceforge.phpdt.core.IJavaElement; +import net.sourceforge.phpdt.core.IJavaElementDelta; +import net.sourceforge.phpdt.core.IParent; +import net.sourceforge.phpdt.core.ISourceRange; +import net.sourceforge.phpdt.core.ISourceReference; +import net.sourceforge.phpdt.core.IType; +import net.sourceforge.phpdt.core.JavaCore; +import net.sourceforge.phpdt.core.JavaModelException; +import net.sourceforge.phpdt.core.ToolFactory; +import net.sourceforge.phpdt.core.compiler.IScanner; +import net.sourceforge.phpdt.core.compiler.ITerminalSymbols; +import net.sourceforge.phpdt.core.compiler.InvalidInputException; +import net.sourceforge.phpdt.ui.IWorkingCopyManager; +import net.sourceforge.phpdt.ui.PreferenceConstants; +import net.sourceforge.phpdt.ui.text.folding.IJavaFoldingStructureProvider; +import net.sourceforge.phpeclipse.PHPeclipsePlugin; +import net.sourceforge.phpeclipse.phpeditor.PHPEditor; +import net.sourceforge.phpeclipse.phpeditor.PHPUnitEditor; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.projection.IProjectionListener; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + + +/** + * Updates the projection model of a class file or compilation unit. + * + * @since 3.0 + */ +public class DefaultJavaFoldingStructureProvider implements IProjectionListener, IJavaFoldingStructureProvider { + + private static class JavaProjectionAnnotation extends ProjectionAnnotation { + + private IJavaElement fJavaElement; + private boolean fIsComment; + + public JavaProjectionAnnotation(IJavaElement element, boolean isCollapsed, boolean isComment) { + super(isCollapsed); + fJavaElement= element; + fIsComment= isComment; + } + + public IJavaElement getElement() { + return fJavaElement; + } + + public void setElement(IJavaElement element) { + fJavaElement= element; + } + + public boolean isComment() { + return fIsComment; + } + + public void setIsComment(boolean isComment) { + fIsComment= isComment; + } + } + + private class ElementChangedListener implements IElementChangedListener { + + /* + * @see net.sourceforge.phpdt.core.IElementChangedListener#elementChanged(net.sourceforge.phpdt.core.ElementChangedEvent) + */ + public void elementChanged(ElementChangedEvent e) { + IJavaElementDelta delta= findElement(fInput, e.getDelta()); + if (delta != null) + processDelta(delta); + } + + private IJavaElementDelta findElement(IJavaElement target, IJavaElementDelta delta) { + + if (delta == null || target == null) + return null; + + IJavaElement element= delta.getElement(); + + if (element.getElementType() > IJavaElement.CLASS_FILE) + return null; + + if (target.equals(element)) + return delta; + + IJavaElementDelta[] children= delta.getAffectedChildren(); + if (children == null || children.length == 0) + return null; + + for (int i= 0; i < children.length; i++) { + IJavaElementDelta d= findElement(target, children[i]); + if (d != null) + return d; + } + + return null; + } + } + + + private IDocument fCachedDocument; + + private ITextEditor fEditor; + private ProjectionViewer fViewer; + private IJavaElement fInput; + private IElementChangedListener fElementListener; + + private boolean fAllowCollapsing= false; + private boolean fCollapseJavadoc= false; + private boolean fCollapseImportContainer= true; + private boolean fCollapseInnerTypes= true; + private boolean fCollapseMethods= false; + + public DefaultJavaFoldingStructureProvider() { + } + + public void install(ITextEditor editor, ProjectionViewer viewer) { + if (editor instanceof PHPEditor) { + fEditor= editor; + fViewer= viewer; + fViewer.addProjectionListener(this); + } + } + + public void uninstall() { + if (isInstalled()) { + projectionDisabled(); + fViewer.removeProjectionListener(this); + fViewer= null; + fEditor= null; + } + } + + protected boolean isInstalled() { + return fEditor != null; + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled() + */ + public void projectionEnabled() { + if (fEditor instanceof PHPEditor) { + initialize(); + fElementListener= new ElementChangedListener(); + JavaCore.addElementChangedListener(fElementListener); + } + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled() + */ + public void projectionDisabled() { + fCachedDocument= null; + if (fElementListener != null) { + JavaCore.removeElementChangedListener(fElementListener); + fElementListener= null; + } + } + + public void initialize() { + + if (!isInstalled()) + return; + + initializePreferences(); + + try { + + IDocumentProvider provider= fEditor.getDocumentProvider(); + fCachedDocument= provider.getDocument(fEditor.getEditorInput()); + fAllowCollapsing= true; + + if (fEditor instanceof PHPUnitEditor) { + IWorkingCopyManager manager= PHPeclipsePlugin.getDefault().getWorkingCopyManager(); + fInput= manager.getWorkingCopy(fEditor.getEditorInput()); + } +// else if (fEditor instanceof ClassFileEditor) { +// IClassFileEditorInput editorInput= (IClassFileEditorInput) fEditor.getEditorInput(); +// fInput= editorInput.getClassFile(); +// } + + if (fInput != null) { + ProjectionAnnotationModel model= (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class); + if (model != null) { + Map additions= computeAdditions((IParent) fInput); + model.removeAllAnnotations(); + model.replaceAnnotations(null, additions); + } + } + + } finally { + fCachedDocument= null; + fAllowCollapsing= false; + } + } + + private void initializePreferences() { + IPreferenceStore store= PHPeclipsePlugin.getDefault().getPreferenceStore(); + fCollapseInnerTypes= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_INNERTYPES); + fCollapseImportContainer= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_IMPORTS); + fCollapseJavadoc= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_JAVADOC); + fCollapseMethods= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS); + } + + private Map computeAdditions(IParent parent) { + Map map= new HashMap(); + try { + computeAdditions(parent.getChildren(), map); + } catch (JavaModelException x) { + } + return map; + } + + private void computeAdditions(IJavaElement[] elements, Map map) throws JavaModelException { + for (int i= 0; i < elements.length; i++) { + IJavaElement element= elements[i]; + + computeAdditions(element, map); + + if (element instanceof IParent) { + IParent parent= (IParent) element; + computeAdditions(parent.getChildren(), map); + } + } + } + + private void computeAdditions(IJavaElement element, Map map) { + + boolean createProjection= false; + + boolean collapse= false; + switch (element.getElementType()) { + + case IJavaElement.IMPORT_CONTAINER: + collapse= fAllowCollapsing && fCollapseImportContainer; + createProjection= true; + break; + case IJavaElement.TYPE: + collapse= fAllowCollapsing && fCollapseInnerTypes && isInnerType((IType) element); + createProjection= true; + break; + case IJavaElement.METHOD: + collapse= fAllowCollapsing && fCollapseMethods; + createProjection= true; + break; + } + + if (createProjection) { + IRegion[] regions= computeProjectionRanges(element); + if (regions != null) { + // comments + for (int i= 0; i < regions.length - 1; i++) { + Position position= createProjectionPosition(regions[i]); + if (position != null) + map.put(new JavaProjectionAnnotation(element, fAllowCollapsing && fCollapseJavadoc, true), position); + } + // code + Position position= createProjectionPosition(regions[regions.length - 1]); + if (position != null) + map.put(new JavaProjectionAnnotation(element, collapse, false), position); + } + } + } + + private boolean isInnerType(IType type) { + + try { + return type.isMember(); + } catch (JavaModelException x) { + IJavaElement parent= type.getParent(); + if (parent != null) { + int parentType= parent.getElementType(); + return (parentType != IJavaElement.COMPILATION_UNIT && parentType != IJavaElement.CLASS_FILE); + } + } + + return false; + } + + private IRegion[] computeProjectionRanges(IJavaElement element) { + + try { + if (element instanceof ISourceReference) { + ISourceReference reference= (ISourceReference) element; + ISourceRange range= reference.getSourceRange(); + String contents= reference.getSource(); + if (contents == null) + return null; + + IScanner scanner= ToolFactory.createScanner(true, false, false);//, false); + scanner.setSource(contents.toCharArray()); + List regions= new ArrayList(); + int shift= range.getOffset(); + int start= shift; + while (true) { + + int token= scanner.getNextToken(); + start= shift + scanner.getCurrentTokenStartPosition(); + + switch (token) { + case ITerminalSymbols.TokenNameCOMMENT_PHPDOC: // COMMENT_JAVADOC: + case ITerminalSymbols.TokenNameCOMMENT_BLOCK: { + int end= shift + scanner.getCurrentTokenEndPosition() + 1; + regions.add(new Region(start, end - start)); + } + case ITerminalSymbols.TokenNameCOMMENT_LINE: + continue; + } + + break; + } + + regions.add(new Region(start, range.getOffset() + range.getLength() - start)); + + if (regions.size() > 0) { + IRegion[] result= new IRegion[regions.size()]; + regions.toArray(result); + return result; + } + } + } catch (JavaModelException e) { + } catch (InvalidInputException e) { + } + + return null; + } + + private Position createProjectionPosition(IRegion region) { + + if (fCachedDocument == null) + return null; + + try { + + int start= fCachedDocument.getLineOfOffset(region.getOffset()); + int end= fCachedDocument.getLineOfOffset(region.getOffset() + region.getLength()); + if (start != end) { + int offset= fCachedDocument.getLineOffset(start); + int endOffset= fCachedDocument.getLineOffset(end + 1); + return new Position(offset, endOffset - offset); + } + + } catch (BadLocationException x) { + } + + return null; + } + + protected void processDelta(IJavaElementDelta delta) { + + if (!isInstalled()) + return; + + ProjectionAnnotationModel model= (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class); + if (model == null) + return; + + try { + + IDocumentProvider provider= fEditor.getDocumentProvider(); + fCachedDocument= provider.getDocument(fEditor.getEditorInput()); + fAllowCollapsing= false; + + Map additions= new HashMap(); + List deletions= new ArrayList(); + List updates= new ArrayList(); + + Map updated= computeAdditions((IParent) fInput); + Map previous= createAnnotationMap(model); + + + Iterator e= updated.keySet().iterator(); + while (e.hasNext()) { + JavaProjectionAnnotation annotation= (JavaProjectionAnnotation) e.next(); + IJavaElement element= annotation.getElement(); + Position position= (Position) updated.get(annotation); + + List annotations= (List) previous.get(element); + if (annotations == null) { + + additions.put(annotation, position); + + } else { + + Iterator x= annotations.iterator(); + while (x.hasNext()) { + JavaProjectionAnnotation a= (JavaProjectionAnnotation) x.next(); + if (annotation.isComment() == a.isComment()) { + Position p= model.getPosition(a); + if (p != null && !position.equals(p)) { + p.setOffset(position.getOffset()); + p.setLength(position.getLength()); + updates.add(a); + } + x.remove(); + break; + } + } + + if (annotations.isEmpty()) + previous.remove(element); + } + } + + e= previous.values().iterator(); + while (e.hasNext()) { + List list= (List) e.next(); + int size= list.size(); + for (int i= 0; i < size; i++) + deletions.add(list.get(i)); + } + + match(model, deletions, additions, updates); + + Annotation[] removals= new Annotation[deletions.size()]; + deletions.toArray(removals); + Annotation[] changes= new Annotation[updates.size()]; + updates.toArray(changes); + model.modifyAnnotations(removals, additions, changes); + + } finally { + fCachedDocument= null; + fAllowCollapsing= true; + } + } + + private void match(ProjectionAnnotationModel model, List deletions, Map additions, List changes) { + if (deletions.isEmpty() || (additions.isEmpty() && changes.isEmpty())) + return; + + List newDeletions= new ArrayList(); + List newChanges= new ArrayList(); + + Iterator deletionIterator= deletions.iterator(); + outer: while (deletionIterator.hasNext()) { + JavaProjectionAnnotation deleted= (JavaProjectionAnnotation) deletionIterator.next(); + Position deletedPosition= model.getPosition(deleted); + if (deletedPosition == null) + continue; + + Iterator changesIterator= changes.iterator(); + while (changesIterator.hasNext()) { + JavaProjectionAnnotation changed= (JavaProjectionAnnotation) changesIterator.next(); + if (deleted.isComment() == changed.isComment()) { + Position changedPosition= model.getPosition(changed); + if (changedPosition == null) + continue; + + if (deletedPosition.getOffset() == changedPosition.getOffset()) { + + deletedPosition.setLength(changedPosition.getLength()); + deleted.setElement(changed.getElement()); + + deletionIterator.remove(); + newChanges.add(deleted); + + changesIterator.remove(); + newDeletions.add(changed); + + continue outer; + } + } + } + + Iterator additionsIterator= additions.keySet().iterator(); + while (additionsIterator.hasNext()) { + JavaProjectionAnnotation added= (JavaProjectionAnnotation) additionsIterator.next(); + if (deleted.isComment() == added.isComment()) { + Position addedPosition= (Position) additions.get(added); + + if (deletedPosition.getOffset() == addedPosition.getOffset()) { + + deletedPosition.setLength(addedPosition.getLength()); + deleted.setElement(added.getElement()); + + deletionIterator.remove(); + newChanges.add(deleted); + + additionsIterator.remove(); + + break; + } + } + } + } + + deletions.addAll(newDeletions); + changes.addAll(newChanges); + } + + private Map createAnnotationMap(IAnnotationModel model) { + Map map= new HashMap(); + Iterator e= model.getAnnotationIterator(); + while (e.hasNext()) { + Object annotation= e.next(); + if (annotation instanceof JavaProjectionAnnotation) { + JavaProjectionAnnotation java= (JavaProjectionAnnotation) annotation; + List list= (List) map.get(java.getElement()); + if (list == null) { + list= new ArrayList(2); + map.put(java.getElement(), list); + } + list.add(java); + } + } + return map; + } +}