1 /*******************************************************************************
2  * Copyright (c) 2000, 2010 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.ui.javaeditor;
15 
16 import org.eclipse.jface.text.IDocument;
17 import org.eclipse.jface.text.TextSelection;
18 
19 import org.eclipse.jdt.core.IJavaElement;
20 import org.eclipse.jdt.core.ITypeRoot;
21 import org.eclipse.jdt.core.JavaModelException;
22 import org.eclipse.jdt.core.dom.ASTNode;
23 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
24 import org.eclipse.jdt.core.dom.Annotation;
25 import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
26 import org.eclipse.jdt.core.dom.BodyDeclaration;
27 import org.eclipse.jdt.core.dom.CompilationUnit;
28 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
29 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
30 import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
31 
32 import org.eclipse.jdt.internal.corext.dom.Selection;
33 import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
34 
35 import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
36 
37 /**
38  * A special text selection that gives access to the resolved and
39  * enclosing element.
40  */
41 public class JavaTextSelection extends TextSelection {
42 
43 	private ITypeRoot fElement;
44 	private IJavaElement[] fResolvedElements;
45 
46 	private boolean fEnclosingElementRequested;
47 	private IJavaElement fEnclosingElement;
48 
49 	private boolean fPartialASTRequested;
50 	private CompilationUnit fPartialAST;
51 
52 	private boolean fNodesRequested;
53 	private ASTNode[] fSelectedNodes;
54 	private ASTNode fCoveringNode;
55 
56 	private boolean fInMethodBodyRequested;
57 	private boolean fInMethodBody;
58 
59 	private boolean fInClassInitializerRequested;
60 	private boolean fInClassInitializer;
61 
62 	private boolean fInVariableInitializerRequested;
63 	private boolean fInVariableInitializer;
64 
65 	/**
66 	 * Indicates whether the selection node has been checked to be of type <code>Annotation</code>.
67 	 *
68 	 * @since 3.7
69 	 */
70 	private boolean fInAnnotationRequested;
71 
72 	/**
73 	 * Indicates whether selection node is of type <code>Annotation</code>.
74 	 *
75 	 * @since 3.7
76 	 */
77 	private boolean fInAnnotation;
78 
79 	/**
80 	 * Creates a new text selection at the given offset and length.
81 	 * @param element the root element
82 	 * @param document the document
83 	 * @param offset offset of the selection
84 	 * @param length length of the selection
85 	 */
JavaTextSelection(ITypeRoot element, IDocument document, int offset, int length)86 	public JavaTextSelection(ITypeRoot element, IDocument document, int offset, int length) {
87 		super(document, offset, length);
88 		fElement= element;
89 	}
90 
91 	/**
92 	 * Resolves the <code>IJavaElement</code>s at the current offset. Returns
93 	 * an empty array if the string under the offset doesn't resolve to a
94 	 * <code>IJavaElement</code>.
95 	 *
96 	 * @return the resolved java elements at the current offset
97 	 * @throws JavaModelException passed from the underlying code resolve API
98 	 */
resolveElementAtOffset()99 	public IJavaElement[] resolveElementAtOffset() throws JavaModelException {
100 		if (fResolvedElements != null)
101 			return fResolvedElements;
102 		// long start= System.currentTimeMillis();
103 		fResolvedElements= SelectionConverter.codeResolve(fElement, this);
104 		// System.out.println("Time resolving element: " + (System.currentTimeMillis() - start));
105 		return fResolvedElements;
106 	}
107 
resolveEnclosingElement()108 	public IJavaElement resolveEnclosingElement() throws JavaModelException {
109 		if (fEnclosingElementRequested)
110 			return fEnclosingElement;
111 		fEnclosingElementRequested= true;
112 		fEnclosingElement= SelectionConverter.resolveEnclosingElement(fElement, this);
113 		return fEnclosingElement;
114 	}
115 
resolvePartialAstAtOffset()116 	public CompilationUnit resolvePartialAstAtOffset() {
117 		if (fPartialASTRequested)
118 			return fPartialAST;
119 		fPartialASTRequested= true;
120 		// long start= System.currentTimeMillis();
121 		fPartialAST= SharedASTProviderCore.getAST(fElement, SharedASTProviderCore.WAIT_YES, null);
122 		// System.out.println("Time requesting partial AST: " + (System.currentTimeMillis() - start));
123 		return fPartialAST;
124 	}
125 
resolveSelectedNodes()126 	public ASTNode[] resolveSelectedNodes() {
127 		if (fNodesRequested)
128 			return fSelectedNodes;
129 		fNodesRequested= true;
130 		CompilationUnit root= resolvePartialAstAtOffset();
131 		if (root == null)
132 			return null;
133 		Selection ds= Selection.createFromStartLength(getOffset(), getLength());
134 		SelectionAnalyzer analyzer= new SelectionAnalyzer(ds, false);
135 		root.accept(analyzer);
136 		fSelectedNodes= analyzer.getSelectedNodes();
137 		fCoveringNode= analyzer.getLastCoveringNode();
138 		return fSelectedNodes;
139 	}
140 
resolveCoveringNode()141 	public ASTNode resolveCoveringNode() {
142 		if (fNodesRequested)
143 			return fCoveringNode;
144 		resolveSelectedNodes();
145 		return fCoveringNode;
146 	}
147 
resolveInMethodBody()148 	public boolean resolveInMethodBody() {
149 		if (fInMethodBodyRequested)
150 			return fInMethodBody;
151 		fInMethodBodyRequested= true;
152 		resolveSelectedNodes();
153 		ASTNode node= getStartNode();
154 		if (node == null) {
155 			fInMethodBody= true;
156 		} else {
157 			while (node != null) {
158 				int nodeType= node.getNodeType();
159 				if (nodeType == ASTNode.BLOCK && node.getParent() instanceof BodyDeclaration) {
160 					fInMethodBody= node.getParent().getNodeType() == ASTNode.METHOD_DECLARATION;
161 					break;
162 				} else if (nodeType == ASTNode.ANONYMOUS_CLASS_DECLARATION) {
163 					fInMethodBody= false;
164 					break;
165 				}
166 				node= node.getParent();
167 			}
168 		}
169 		return fInMethodBody;
170 	}
171 
resolveInClassInitializer()172 	public boolean resolveInClassInitializer() {
173 		if (fInClassInitializerRequested)
174 			return fInClassInitializer;
175 		fInClassInitializerRequested= true;
176 		resolveSelectedNodes();
177 		ASTNode node= getStartNode();
178 		if (node == null) {
179 			fInClassInitializer= true;
180 		} else {
181 			while (node != null) {
182 				int nodeType= node.getNodeType();
183 				if (node instanceof AbstractTypeDeclaration) {
184 					fInClassInitializer= false;
185 					break;
186 				} else if (nodeType == ASTNode.ANONYMOUS_CLASS_DECLARATION) {
187 					fInClassInitializer= false;
188 					break;
189 				} else if (nodeType == ASTNode.INITIALIZER) {
190 					fInClassInitializer= true;
191 					break;
192 				}
193 				node= node.getParent();
194 			}
195 		}
196 		return fInClassInitializer;
197 	}
198 
resolveInVariableInitializer()199 	public boolean resolveInVariableInitializer() {
200 		if (fInVariableInitializerRequested)
201 			return fInVariableInitializer;
202 		fInVariableInitializerRequested= true;
203 		resolveSelectedNodes();
204 		ASTNode node= getStartNode();
205 		ASTNode last= null;
206 		while (node != null) {
207 			int nodeType= node.getNodeType();
208 			if (node instanceof AbstractTypeDeclaration) {
209 				fInVariableInitializer= false;
210 				break;
211 			} else if (nodeType == ASTNode.ANONYMOUS_CLASS_DECLARATION) {
212 				fInVariableInitializer= false;
213 				break;
214 			} else if (nodeType == ASTNode.VARIABLE_DECLARATION_FRAGMENT &&
215 					   ((VariableDeclarationFragment)node).getInitializer() == last) {
216 				fInVariableInitializer= true;
217 				break;
218 			} else if (nodeType == ASTNode.SINGLE_VARIABLE_DECLARATION &&
219 				       ((SingleVariableDeclaration)node).getInitializer() == last) {
220 				fInVariableInitializer= true;
221 				break;
222 			} else if (nodeType == ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION &&
223 				       ((AnnotationTypeMemberDeclaration)node).getDefault() == last) {
224 				fInVariableInitializer= true;
225 				break;
226 			}
227 			last= node;
228 			node= node.getParent();
229 		}
230 		return fInVariableInitializer;
231 	}
232 
233 	/**
234 	 * Resolves the selected nodes and returns <code>true</code> if the node or any of its ancestors
235 	 * is of type <code>Annotation</code>, <code>false</code> otherwise.
236 	 *
237 	 * @return <code>true</code> if the node or any of its ancestors is of type
238 	 *         <code>Annotation</code>, <code>false</code> otherwise
239 	 * @since 3.7
240 	 */
resolveInAnnotation()241 	public boolean resolveInAnnotation() {
242 		if (fInAnnotationRequested)
243 			return fInAnnotation;
244 		fInAnnotationRequested= true;
245 		resolveSelectedNodes();
246 		ASTNode node= getStartNode();
247 		while (node != null) {
248 			if (node instanceof Annotation) {
249 				fInAnnotation= true;
250 				break;
251 			}
252 			node= node.getParent();
253 		}
254 		return fInAnnotation;
255 	}
256 
getStartNode()257 	private ASTNode getStartNode() {
258 		if (fSelectedNodes != null && fSelectedNodes.length > 0)
259 			return fSelectedNodes[0];
260 		else
261 			return fCoveringNode;
262 	}
263 }
264