1 package no.uio.ifi.refaktor.utils;
3 import java.lang.ref.SoftReference;
5 import no.uio.ifi.refaktor.analyze.collectors.LastStatementCollector;
7 import org.eclipse.core.resources.IProject;
8 import org.eclipse.jdt.core.ICompilationUnit;
9 import org.eclipse.jdt.core.IJavaElement;
10 import org.eclipse.jdt.core.IType;
11 import org.eclipse.jdt.core.dom.ASTNode;
12 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
13 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
14 import org.eclipse.jdt.core.dom.CompilationUnit;
15 import org.eclipse.jdt.core.dom.ITypeBinding;
16 import org.eclipse.jdt.core.dom.NodeFinder;
17 import org.eclipse.jdt.core.dom.Statement;
18 import org.eclipse.jface.text.IDocument;
19 import org.eclipse.jface.text.ITextSelection;
20 import org.eclipse.jface.text.TextSelection;
23 * A custom TextSelection that enforces the presence of
24 * a compilation unit to back the selection.
26 public class CompilationUnitTextSelection extends TextSelection implements ITextSelection {
28 private class NodeFinderCache {
29 private SoftReference<NodeFinder> nodeFinderRef = new NullSoftReference<NodeFinder>();
30 private int selectionOffsetForNodeFinder;
32 private NodeFinder getNodeFinder() {
33 NodeFinder nodeFinder = nodeFinderRef.get();
34 if (cacheIsDirty(nodeFinder)) {
35 nodeFinder = ParseUtils.createNodeFinder(ParseUtils.parse(compilationUnit), CompilationUnitTextSelection.this);
36 nodeFinderRef = new SoftReference<NodeFinder>(nodeFinder);
37 selectionOffsetForNodeFinder = getOffset();
42 private boolean cacheIsDirty(NodeFinder nodeFinder) {
43 return nodeFinder == null || selectionOffsetForNodeFinder != getOffset();
47 private ICompilationUnit compilationUnit;
48 private final NodeFinderCache nodeFinderCache;
50 public CompilationUnitTextSelection(CompilationUnitTextSelection textSelection) {
51 this(textSelection.getCompilationUnit(), textSelection);
54 public CompilationUnitTextSelection(ICompilationUnit compilationUnit, ITextSelection selection) {
55 this(compilationUnit, selection.getOffset(), selection.getLength());
58 public CompilationUnitTextSelection(ICompilationUnit compilationUnit, int offset, int length) {
59 super(offset, length);
60 if (compilationUnit == null)
61 throw new NullPointerException(this.getClass().getCanonicalName() + ": given compilation unit cannot be null");
62 this.compilationUnit = compilationUnit;
63 nodeFinderCache = new NodeFinderCache();
67 public IDocument getDocument() {
68 return DocumentUtils.getDocumentFromCompilationUnit(compilationUnit);
72 return getOffset() + getLength();
75 public IProject getProject() {
76 return compilationUnit.getJavaProject().getProject();
79 public ICompilationUnit getCompilationUnit() {
80 return compilationUnit;
83 public ASTNode getCoveringNode() {
84 return getNodeFinder().getCoveringNode();
87 public ASTNode getCoveredNode() {
88 return getNodeFinder().getCoveredNode();
91 private NodeFinder getNodeFinder() {
92 return nodeFinderCache.getNodeFinder();
95 public boolean surroundsNode(ASTNode node) {
96 return getOffset() <= node.getStartPosition() && endOfNode(node) <= getEnd();
99 private int endOfNode(ASTNode node) {
100 return node.getStartPosition() + node.getLength();
103 public String getPackageName() {
104 return getConcreteCompilationUnit().getPackage().getName().getFullyQualifiedName();
107 public String getFullyQualifiedNameOfSurroundingType() {
108 ASTNode node = findClosestSurroundingClassDeclaration();
110 if (!isRootNode(node)) {
111 assert isClassDeclaration(node);
113 ITypeBinding binding = null;
115 if (node instanceof AbstractTypeDeclaration) {
116 binding = ((AbstractTypeDeclaration) node).resolveBinding();
117 } else if (node instanceof AnonymousClassDeclaration) {
118 binding = ((AnonymousClassDeclaration) node).resolveBinding();
121 if (binding != null) {
122 IJavaElement javaElement = binding.getJavaElement();
124 if (javaElement instanceof IType)
125 return ((IType) javaElement).getFullyQualifiedName();
132 private ASTNode findClosestSurroundingClassDeclaration() {
133 ASTNode node = getCoveringNode();
135 while (!(isClassDeclaration(node) || isRootNode(node)))
136 node = node.getParent();
140 private boolean isClassDeclaration(ASTNode node) {
141 return node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration;
144 private boolean isRootNode(ASTNode node) {
145 return node.getNodeType() == ASTNode.COMPILATION_UNIT;
148 private CompilationUnit getConcreteCompilationUnit() {
149 ASTNode root = getCoveringNode().getRoot();
150 assert root instanceof CompilationUnit;
151 return (CompilationUnit) root;
155 public boolean equals(Object obj) {
156 if (obj instanceof CompilationUnitTextSelection)
157 return super.equals(obj) && compilationUnit.equals(((CompilationUnitTextSelection)obj).compilationUnit);
162 public int hashCode() {
163 return (getOffset() << 24) | (getLength() << 16) | compilationUnit.hashCode();
167 public String toString() {
168 return getOffsetAndLengthString() + "; End: " + getEnd() + "; CU handle identifier: " + compilationUnit.getHandleIdentifier();
171 public String getOffsetAndLengthString() {
172 return "Offset: " + getOffset() + "; Length: " + getLength();
176 public int getStartLine() {
177 return createTextSelectionWithDocument().getStartLine();
181 public int getEndLine() {
182 return createTextSelectionWithDocument().getEndLine();
186 public String getText() {
187 return createTextSelectionWithDocument().getText();
190 private TextSelection createTextSelectionWithDocument() {
191 return new TextSelection(getDocument(), getOffset(), getLength());
194 public boolean isEquivalentTo(CompilationUnitTextSelection textSelection) {
195 return equals(textSelection) || servesTheSamePurposeAs(textSelection);
199 private boolean servesTheSamePurposeAs(CompilationUnitTextSelection textSelection) {
200 return getOffset() == textSelection.getOffset() &&
201 getLength() == textSelection.getLength() &&
202 getCompilationUnit().equals(textSelection.getCompilationUnit());
205 public Statement getLastStatement() {
206 ASTNode coveringNode = getCoveringNode();
207 if (coveringNode == getCoveredNode()) {
208 assert coveringNode instanceof Statement;
209 return (Statement) coveringNode;
212 LastStatementCollector lastStatementCollector = new LastStatementCollector(this, coveringNode);
213 coveringNode.accept(lastStatementCollector);
214 return lastStatementCollector.getLastStatement();