Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7fc6f1f621aee1b13a4eb5786b75190de01e8dc6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.search.*;
import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.util.ASTNodeFinder;
import org.eclipse.jdt.internal.core.util.Util;

/**
 * Collects the super type names of a given declaring type.
 * Returns NOT_FOUND_DECLARING_TYPE if the declaring type was not found.
 * Returns null if the declaring type pattern doesn't require an exact match.
 */
public class SuperTypeNamesCollector {

	/**
	 * An ast visitor that visits type declarations and member type declarations
	 * collecting their super type names.
	 */
	public class TypeDeclarationVisitor extends ASTVisitor {
		public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) {
			ReferenceBinding binding = typeDeclaration.binding;
			if (SuperTypeNamesCollector.this.matches(binding))
				collectSuperTypeNames(binding, binding.compoundName);
			return true;
		}
		public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
			ReferenceBinding binding = typeDeclaration.binding;
			if (SuperTypeNamesCollector.this.matches(binding))
				collectSuperTypeNames(binding, binding.compoundName);
			return true;
		}
		public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
			ReferenceBinding binding = memberTypeDeclaration.binding;
			if (SuperTypeNamesCollector.this.matches(binding))
				collectSuperTypeNames(binding, binding.compoundName);
			return true;
		}
		public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
			return false; // don't visit field declarations
		}
		public boolean visit(Initializer initializer, MethodScope scope) {
			return false; // don't visit initializers
		}
		public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
			return false; // don't visit constructor declarations
		}
		public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
			return false; // don't visit method declarations
		}
	}
SearchPattern pattern;
char[] typeSimpleName;
char[] typeQualification;
MatchLocator locator;
IType type;
IProgressMonitor progressMonitor;
char[][][] result;
int resultIndex;

char[][][] samePackageSuperTypeName; // set only if focus is null
int samePackageIndex;

public SuperTypeNamesCollector(
	SearchPattern pattern,
	char[] typeSimpleName,
	char[] typeQualification,
	MatchLocator locator,
	IType type,
	IProgressMonitor progressMonitor) {

	this.pattern = pattern;
	this.typeSimpleName = typeSimpleName;
	this.typeQualification = typeQualification;
	this.locator = locator;
	this.type = type;
	this.progressMonitor = progressMonitor;
}

private boolean addIfSamePackage(char[][] compoundName, char[][] path) {
	if (compoundName.length != path.length) return false;
	int resultLength = this.samePackageSuperTypeName.length;
	for (int i = 0; i < resultLength; i++)
		if (CharOperation.equals(this.samePackageSuperTypeName[i], compoundName)) return false; // already known
	
	for (int i = 0, length = compoundName.length - 1; i < length; i ++) {
		if (!CharOperation.equals(compoundName[i], path[i])) return false;
	}
	if (resultLength == this.samePackageIndex)
		System.arraycopy(this.samePackageSuperTypeName, 0, this.samePackageSuperTypeName = new char[resultLength*2][][], 0, resultLength);
	this.samePackageSuperTypeName[this.samePackageIndex++] = compoundName;
	return true;
}

protected void addToResult(char[][] compoundName) {
	int resultLength = this.result.length;
	for (int i = 0; i < resultLength; i++)
		if (CharOperation.equals(this.result[i], compoundName)) return; // already known

	if (resultLength == this.resultIndex)
		System.arraycopy(this.result, 0, this.result = new char[resultLength*2][][], 0, resultLength);
	this.result[this.resultIndex++] = compoundName;
}

/*
 * Parse the given compiation unit and build its type bindings.
 */
protected CompilationUnitDeclaration buildBindings(ICompilationUnit compilationUnit, boolean isTopLevelOrMember) throws JavaModelException {
	// source unit
	org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) compilationUnit;

	CompilationResult compilationResult = new CompilationResult(sourceUnit, 1, 1, 0);
	CompilationUnitDeclaration unit =
		isTopLevelOrMember ?
			this.locator.basicParser().dietParse(sourceUnit, compilationResult) :
			this.locator.basicParser().parse(sourceUnit, compilationResult);
	if (unit != null) {
		this.locator.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/);
		this.locator.lookupEnvironment.completeTypeBindings(unit, !isTopLevelOrMember);
		if (!isTopLevelOrMember) {
			if (unit.scope != null)
				unit.scope.faultInTypes(); // fault in fields & methods
			unit.resolve();
		}
	}
	return unit;
}
public char[][][] collect() throws JavaModelException {
	if (this.type != null) {
		// Collect the paths of the cus that are in the hierarchy of the given type
		this.result = new char[1][][];
		this.resultIndex = 0;
		JavaProject javaProject = (JavaProject) this.type.getJavaProject();
		this.locator.initialize(javaProject, 0);
		try {
			if (this.type.isBinary()) {
				BinaryTypeBinding binding = this.locator.cacheBinaryType(this.type, null);
				if (binding != null)
					collectSuperTypeNames(binding, null);
			} else {
				ICompilationUnit unit = this.type.getCompilationUnit();
				SourceType sourceType = (SourceType) this.type;
				boolean isTopLevelOrMember = sourceType.getOuterMostLocalContext() == null;
				CompilationUnitDeclaration parsedUnit = buildBindings(unit, isTopLevelOrMember);
				if (parsedUnit != null) {
					TypeDeclaration typeDecl = new ASTNodeFinder(parsedUnit).findType(this.type);
					if (typeDecl != null && typeDecl.binding != null)
						collectSuperTypeNames(typeDecl.binding, null);
				}
			}
		} catch (AbortCompilation e) {
			// problem with classpath: report inacurrate matches
			return null;
		}
		if (this.result.length > this.resultIndex)
			System.arraycopy(this.result, 0, this.result = new char[this.resultIndex][][], 0, this.resultIndex);
		return this.result;
	}

	// Collect the paths of the cus that declare a type which matches declaringQualification + declaringSimpleName
	String[] paths = getPathsOfDeclaringType();
	if (paths == null) return null;

	// Create bindings from source types and binary types and collect super type names of the type declaration
	// that match the given declaring type
	Util.sort(paths); // sort by projects
	JavaProject previousProject = null;
	this.result = new char[1][][];
	this.samePackageSuperTypeName = new char[1][][];
	this.resultIndex = 0;
	for (int i = 0, length = paths.length; i < length; i++) {
		try {
			Openable openable = this.locator.handleFactory.createOpenable(paths[i], this.locator.scope);
			if (openable == null) continue; // outside classpath

			IJavaProject project = openable.getJavaProject();
			if (!project.equals(previousProject)) {
				previousProject = (JavaProject) project;
				this.locator.initialize(previousProject, 0);
			}
			if (openable instanceof ICompilationUnit) {
				ICompilationUnit unit = (ICompilationUnit) openable;
				CompilationUnitDeclaration parsedUnit = buildBindings(unit, true /*only toplevel and member types are visible to the focus type*/);
				if (parsedUnit != null)
					parsedUnit.traverse(new TypeDeclarationVisitor(), parsedUnit.scope);
			} else if (openable instanceof IClassFile) {
				IClassFile classFile = (IClassFile) openable;
				BinaryTypeBinding binding = this.locator.cacheBinaryType(classFile.getType(), null);
				if (matches(binding))
					collectSuperTypeNames(binding, binding.compoundName);
			}
		} catch (AbortCompilation e) {
			// ignore: continue with next element
		} catch (JavaModelException e) {
			// ignore: continue with next element
		}
	}
	if (this.result.length > this.resultIndex)
		System.arraycopy(this.result, 0, this.result = new char[this.resultIndex][][], 0, this.resultIndex);
	return this.result;
}
/**
 * Collects the names of all the supertypes of the given type.
 */
protected void collectSuperTypeNames(ReferenceBinding binding, char[][] path) {
	ReferenceBinding superclass = binding.superclass();
	if (path != null) {
		boolean samePackage = addIfSamePackage(superclass.compoundName, path);
		if (!samePackage) path = null;
	}
	if (superclass != null) {
		addToResult(superclass.compoundName);
		collectSuperTypeNames(superclass, null);
	}

	ReferenceBinding[] interfaces = binding.superInterfaces();
	if (interfaces != null) {
		for (int i = 0; i < interfaces.length; i++) {
			ReferenceBinding interfaceBinding = interfaces[i];
			addToResult(interfaceBinding.compoundName);
			collectSuperTypeNames(interfaceBinding, null);
		}
	}
}
protected String[] getPathsOfDeclaringType() {
	if (this.typeQualification == null && this.typeSimpleName == null) return null;

	final PathCollector pathCollector = new PathCollector();
	IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
	IndexManager indexManager = JavaModelManager.getIndexManager();
	SearchPattern searchPattern = new TypeDeclarationPattern(
		this.typeSimpleName != null ? null : this.typeQualification, // use the qualification only if no simple name
		null, // do find member types
		this.typeSimpleName,
		IIndexConstants.TYPE_SUFFIX,
		this.pattern.getMatchRule());
	IndexQueryRequestor searchRequestor = new IndexQueryRequestor(){
		public boolean acceptIndexMatch(String documentPath, SearchPattern indexRecord, SearchParticipant participant, AccessRuleSet access) {
			TypeDeclarationPattern record = (TypeDeclarationPattern)indexRecord;
			if (record.enclosingTypeNames != IIndexConstants.ONE_ZERO_CHAR) {  // filter out local and anonymous classes
				pathCollector.acceptIndexMatch(documentPath, indexRecord, participant, access);
			}
			return true;
		}
	};

	indexManager.performConcurrentJob(
		new PatternSearchJob(
			searchPattern,
			new JavaSearchParticipant(),
			scope,
			searchRequestor),
		IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
		this.progressMonitor == null ? null : new SubProgressMonitor(this.progressMonitor, 100));
	return pathCollector.getPaths();
}
public char[][][] getSamePackageSuperTypeNames() {
	return this.samePackageSuperTypeName;
}
protected boolean matches(char[][] compoundName) {
	int length = compoundName.length;
	if (length == 0) return false;
	char[] simpleName = compoundName[length-1];
	int last = length - 1;
	if (this.typeSimpleName == null || this.pattern.matchesName(simpleName, this.typeSimpleName)) {
		// most frequent case: simple name equals last segment of compoundName
		char[][] qualification = new char[last][];
		System.arraycopy(compoundName, 0, qualification, 0, last);
		return this.pattern.matchesName(this.typeQualification, CharOperation.concatWith(qualification, '.'));
	}

	if (!CharOperation.endsWith(simpleName, this.typeSimpleName)) return false;

	// member type -> transform A.B.C$D into A.B.C.D
	System.arraycopy(compoundName, 0, compoundName = new char[length+1][], 0, last);
	int dollar = CharOperation.indexOf('$', simpleName);
	if (dollar == -1) return false;
	compoundName[last] = CharOperation.subarray(simpleName, 0, dollar);
	compoundName[length] = CharOperation.subarray(simpleName, dollar+1, simpleName.length);
	return this.matches(compoundName);
}
protected boolean matches(ReferenceBinding binding) {
	return binding != null && binding.compoundName != null && this.matches(binding.compoundName);
}
}

Back to the top