Skip to main content
summaryrefslogtreecommitdiffstats
blob: bb6d4337c13250119d8fd6c0a0a286017f958b58 (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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core.hierarchy;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.IGenericType;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.Openable;
import org.eclipse.jdt.internal.core.ResolvedBinaryType;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory;
import org.eclipse.jdt.internal.core.util.ResourceCompilationUnit;
import org.eclipse.jdt.internal.core.util.Util;

@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class HierarchyBuilder {
	/**
	 * The hierarchy being built.
	 */
	protected TypeHierarchy hierarchy;
	/**
	 * @see NameLookup
	 */
	protected NameLookup nameLookup;
	/**
	 * The resolver used to resolve type hierarchies
	 * @see HierarchyResolver
	 */
	protected HierarchyResolver hierarchyResolver;
	/**
	 * A temporary cache of infos to handles to speed info
	 * to handle translation - it only contains the entries
	 * for the types in the region (in other words, it contains
	 * no supertypes outside the region).
	 */
	protected Map infoToHandle;
	/*
	 * The dot-separated fully qualified name of the focus type, or null of none.
	 */
	protected String focusQualifiedName;

	public HierarchyBuilder(TypeHierarchy hierarchy) throws JavaModelException {

		this.hierarchy = hierarchy;
		JavaProject project = (JavaProject) hierarchy.javaProject();

		IType focusType = hierarchy.getType();
		org.eclipse.jdt.core.ICompilationUnit unitToLookInside = focusType == null ? null : focusType.getCompilationUnit();
		org.eclipse.jdt.core.ICompilationUnit[] workingCopies = this.hierarchy.workingCopies;
		org.eclipse.jdt.core.ICompilationUnit[] unitsToLookInside;
		if (unitToLookInside != null) {
			int wcLength = workingCopies == null ? 0 : workingCopies.length;
			if (wcLength == 0) {
				unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[] {unitToLookInside};
			} else {
				unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[wcLength+1];
				unitsToLookInside[0] = unitToLookInside;
				System.arraycopy(workingCopies, 0, unitsToLookInside, 1, wcLength);
			}
		} else {
			unitsToLookInside = workingCopies;
		}
		if (project != null) {
			SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside);
			this.nameLookup = searchableEnvironment.nameLookup;
			this.hierarchyResolver =
				new HierarchyResolver(
					searchableEnvironment,
					project.getOptions(true),
					this,
					new DefaultProblemFactory());
		}
		this.infoToHandle = new HashMap(5);
		this.focusQualifiedName = focusType == null ? null : focusType.getFullyQualifiedName();
	}

	public abstract void build(boolean computeSubtypes)
		throws JavaModelException, CoreException;
	/**
	 * Configure this type hierarchy by computing the supertypes only.
	 */
	protected void buildSupertypes() {
		IType focusType = getType();
		if (focusType == null)
			return;
		// get generic type from focus type
		IGenericType type;
		try {
			type = (IGenericType) ((JavaElement) focusType).getElementInfo();
		} catch (JavaModelException e) {
			// if the focus type is not present, or if cannot get workbench path
			// we cannot create the hierarchy
			return;
		}
		//NB: no need to set focus type on hierarchy resolver since no other type is injected
		//    in the hierarchy resolver, thus there is no need to check that a type is
		//    a sub or super type of the focus type.
		this.hierarchyResolver.resolve(type);

		// Add focus if not already in (case of a type with no explicit super type)
		if (!this.hierarchy.contains(focusType)) {
			this.hierarchy.addRootClass(focusType);
		}
	}
	/** Wrapper method as hook for OTTypeHierarchies. */
	public void hookableConnect(
			ReferenceBinding focusType,
			ReferenceBinding typeBinding,
			IGenericType type,
			IType typeHandle,
			boolean isPhantom,
			IType superclassHandle,
			IType[] tsuperclassHandles,
			boolean[] arePhantoms,
			IType[] superinterfaceHandles) 
	{
		connect(type, typeHandle, superclassHandle, superinterfaceHandles);
	}
	/**
	 * Connect the supplied type to its superclass & superinterfaces.
	 * The superclass & superinterfaces are the identical binary or source types as
	 * supplied by the name environment.
	 */
	public void connect(
		IGenericType type,
		IType typeHandle,
		IType superclassHandle,
		IType[] superinterfaceHandles) {

		/*
		 * Temporary workaround for 1G2O5WK: ITPJCORE:WINNT - NullPointerException when selecting "Show in Type Hierarchy" for a inner class
		 */
		if (typeHandle == null)
			return;
		if (TypeHierarchy.DEBUG) {
			System.out.println(
				"Connecting: " + ((JavaElement) typeHandle).toStringWithAncestors()); //$NON-NLS-1$
			System.out.println(
				"  to superclass: " //$NON-NLS-1$
					+ (superclassHandle == null
						? "<None>" //$NON-NLS-1$
						: ((JavaElement) superclassHandle).toStringWithAncestors()));
			System.out.print("  and superinterfaces:"); //$NON-NLS-1$
			if (superinterfaceHandles == null || superinterfaceHandles.length == 0) {
				System.out.println(" <None>"); //$NON-NLS-1$
			} else {
				System.out.println();
				for (int i = 0, length = superinterfaceHandles.length; i < length; i++) {
					if (superinterfaceHandles[i] == null) continue;
					System.out.println(
						"    " + ((JavaElement) superinterfaceHandles[i]).toStringWithAncestors()); //$NON-NLS-1$
				}
			}
		}
		// now do the caching
		switch (TypeDeclaration.kind(type.getModifiers())) {
			case TypeDeclaration.CLASS_DECL :
			case TypeDeclaration.ENUM_DECL :
				if (superclassHandle == null) {
					this.hierarchy.addRootClass(typeHandle);
				} else {
					this.hierarchy.cacheSuperclass(typeHandle, superclassHandle);
				}
				break;
			case TypeDeclaration.INTERFACE_DECL :
			case TypeDeclaration.ANNOTATION_TYPE_DECL :
				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=329663
				if (this.hierarchy.typeToSuperInterfaces.get(typeHandle) == null)
					this.hierarchy.addInterface(typeHandle);
				break;
		}
		if (superinterfaceHandles == null) {
			superinterfaceHandles = TypeHierarchy.NO_TYPE;
		}
		this.hierarchy.cacheSuperInterfaces(typeHandle, superinterfaceHandles);

		// record flags
		this.hierarchy.cacheFlags(typeHandle, type.getModifiers());
	}
	/**
	 * Returns a handle for the given generic type or null if not found.
	 */
	protected IType getHandle(IGenericType genericType, ReferenceBinding binding) {
		if (genericType == null)
			return null;
		if (genericType instanceof HierarchyType) {
			IType handle = (IType)this.infoToHandle.get(genericType);
			if (handle == null) {
				handle = ((HierarchyType)genericType).typeHandle;
				handle = (IType) ((JavaElement) handle).resolved(binding);
				this.infoToHandle.put(genericType, handle);
			}
			return handle;
		} else if (genericType.isBinaryType()) {
			ClassFile classFile = (ClassFile) this.infoToHandle.get(genericType);
			// if it's null, it's from outside the region, so do lookup
			if (classFile == null) {
				IType handle = lookupBinaryHandle((IBinaryType) genericType);
				if (handle == null)
					return null;
				// case of an anonymous type (see 1G2O5WK: ITPJCORE:WINNT - NullPointerException when selecting "Show in Type Hierarchy" for a inner class)
				// optimization: remember the handle for next call (case of java.io.Serializable that a lot of classes implement)
				classFile = (ClassFile) handle.getParent();
				this.infoToHandle.put(genericType, classFile);
			}
			return new ResolvedBinaryType(classFile, classFile.getTypeName(), new String(binding.computeUniqueKey()));
		} else if (genericType instanceof SourceTypeElementInfo) {
			IType handle = ((SourceTypeElementInfo) genericType).getHandle();
			return (IType) ((JavaElement) handle).resolved(binding);
		} else
			return null;
	}
	protected IType getType() {
		return this.hierarchy.getType();
	}
	/**
	 * Looks up and returns a handle for the given binary info.
	 */
	protected IType lookupBinaryHandle(IBinaryType typeInfo) {
		int flag;
		String qualifiedName;
		switch (TypeDeclaration.kind(typeInfo.getModifiers())) {
			case TypeDeclaration.CLASS_DECL :
				flag = NameLookup.ACCEPT_CLASSES;
				break;
			case TypeDeclaration.INTERFACE_DECL :
				flag = NameLookup.ACCEPT_INTERFACES;
				break;
			case TypeDeclaration.ENUM_DECL :
				flag = NameLookup.ACCEPT_ENUMS;
				break;
			default:
				//case IGenericType.ANNOTATION :
				flag = NameLookup.ACCEPT_ANNOTATIONS;
				break;
		}
		char[] bName = typeInfo.getName();
		qualifiedName = new String(ClassFile.translatedName(bName));
		if (qualifiedName.equals(this.focusQualifiedName)) return getType();
		NameLookup.Answer answer = this.nameLookup.findType(qualifiedName,
			false,
			flag,
			true/* consider secondary types */,
			false/* do NOT wait for indexes */,
			false/*don't check restrictions*/,
			null);
		return answer == null || answer.type == null || !answer.type.isBinary() ? null : answer.type;

	}
	protected void worked(IProgressMonitor monitor, int work) {
		if (monitor != null) {
			if (monitor.isCanceled()) {
				throw new OperationCanceledException();
			} else {
				monitor.worked(work);
			}
		}
	}
/**
 * Create an ICompilationUnit info from the given compilation unit on disk.
 */
	protected ICompilationUnit createCompilationUnitFromPath(Openable handle, IFile file, char[] moduleName) {
		final char[] elementName = handle.getElementName().toCharArray();
		return new ResourceCompilationUnit(file, moduleName) {
			@Override
			public char[] getFileName() {
				return elementName;
			}
		};
	}
	/**
 * Creates the type info from the given class file on disk and
 * adds it to the given list of infos.
 */
protected IBinaryType createInfoFromClassFile(Openable handle, IResource file) {
	IBinaryType info = null;
	try {
		info = Util.newClassFileReader(file);
	} catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) {
		if (TypeHierarchy.DEBUG) {
			e.printStackTrace();
		}
		return null;
	} catch (java.io.IOException e) {
		if (TypeHierarchy.DEBUG) {
			e.printStackTrace();
		}
		return null;
	} catch (CoreException e) {
		if (TypeHierarchy.DEBUG) {
			e.printStackTrace();
		}
		return null;
	}
	this.infoToHandle.put(info, handle);
	return info;
}
	/**
 * Create a type info from the given class file in a jar and adds it to the given list of infos.
 */
protected IBinaryType createInfoFromClassFileInJar(Openable classFile) {
	IOrdinaryClassFile cf = (IOrdinaryClassFile)classFile;
	IBinaryType info;
	try {
		info = BinaryTypeFactory.create(cf, null);
	} catch (JavaModelException | ClassFormatException e) {
		if (TypeHierarchy.DEBUG) {
			e.printStackTrace();
		}
		return null;
	}

	this.infoToHandle.put(info, classFile);
	return info;
}

}

Back to the top