001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.util;
011:
012: import java.util.HashMap;
013: import java.util.HashSet;
014:
015: import org.eclipse.core.resources.IFile;
016: import org.eclipse.core.resources.IProject;
017: import org.eclipse.core.resources.ResourcesPlugin;
018: import org.eclipse.core.runtime.CoreException;
019: import org.eclipse.core.runtime.IPath;
020: import org.eclipse.core.runtime.Path;
021: import org.eclipse.jdt.core.IClassFile;
022: import org.eclipse.jdt.core.IClasspathEntry;
023: import org.eclipse.jdt.core.ICompilationUnit;
024: import org.eclipse.jdt.core.IJavaElement;
025: import org.eclipse.jdt.core.IJavaProject;
026: import org.eclipse.jdt.core.IMember;
027: import org.eclipse.jdt.core.IPackageFragment;
028: import org.eclipse.jdt.core.IPackageFragmentRoot;
029: import org.eclipse.jdt.core.IType;
030: import org.eclipse.jdt.core.JavaCore;
031: import org.eclipse.jdt.core.JavaModelException;
032: import org.eclipse.jdt.core.compiler.CharOperation;
033: import org.eclipse.jdt.core.search.IJavaSearchScope;
034: import org.eclipse.jdt.internal.compiler.ast.*;
035: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
036: import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
037: import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
038: import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
039: import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
040: import org.eclipse.jdt.internal.compiler.lookup.Scope;
041: import org.eclipse.jdt.internal.core.*;
042: import org.eclipse.jdt.internal.core.JavaModel;
043: import org.eclipse.jdt.internal.core.JavaModelManager;
044: import org.eclipse.jdt.internal.core.JavaProject;
045: import org.eclipse.jdt.internal.core.Openable;
046: import org.eclipse.jdt.internal.core.PackageFragmentRoot;
047: import org.eclipse.jdt.internal.core.util.Util;
048:
049: /**
050: * Creates java element handles.
051: */
052: public class HandleFactory {
053:
054: /**
055: * Cache package fragment root information to optimize speed performance.
056: */
057: private String lastPkgFragmentRootPath;
058: private IPackageFragmentRoot lastPkgFragmentRoot;
059:
060: /**
061: * Cache package handles to optimize memory.
062: */
063: private HashtableOfArrayToObject packageHandles;
064:
065: private JavaModel javaModel;
066:
067: public HandleFactory() {
068: this .javaModel = JavaModelManager.getJavaModelManager()
069: .getJavaModel();
070: }
071:
072: /**
073: * Creates an Openable handle from the given resource path.
074: * The resource path can be a path to a file in the workbench (eg. /Proj/com/ibm/jdt/core/HandleFactory.java)
075: * or a path to a file in a jar file - it then contains the path to the jar file and the path to the file in the jar
076: * (eg. c:/jdk1.2.2/jre/lib/rt.jar|java/lang/Object.class or /Proj/rt.jar|java/lang/Object.class)
077: * NOTE: This assumes that the resource path is the toString() of an IPath,
078: * in other words, it uses the IPath.SEPARATOR for file path
079: * and it uses '/' for entries in a zip file.
080: * If not null, uses the given scope as a hint for getting Java project handles.
081: */
082: public Openable createOpenable(String resourcePath,
083: IJavaSearchScope scope) {
084: int separatorIndex;
085: if ((separatorIndex = resourcePath
086: .indexOf(IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR)) > -1) {
087: // path to a class file inside a jar
088: // Optimization: cache package fragment root handle and package handles
089: int rootPathLength;
090: if (this .lastPkgFragmentRootPath == null
091: || (rootPathLength = this .lastPkgFragmentRootPath
092: .length()) != resourcePath.length()
093: || !resourcePath.regionMatches(0,
094: this .lastPkgFragmentRootPath, 0,
095: rootPathLength)) {
096: String jarPath = resourcePath.substring(0,
097: separatorIndex);
098: IPackageFragmentRoot root = this .getJarPkgFragmentRoot(
099: jarPath, scope);
100: if (root == null)
101: return null; // match is outside classpath
102: this .lastPkgFragmentRootPath = jarPath;
103: this .lastPkgFragmentRoot = root;
104: this .packageHandles = new HashtableOfArrayToObject(5);
105: }
106: // create handle
107: String classFilePath = resourcePath
108: .substring(separatorIndex + 1);
109: String[] simpleNames = new Path(classFilePath).segments();
110: String[] pkgName;
111: int length = simpleNames.length - 1;
112: if (length > 0) {
113: pkgName = new String[length];
114: System.arraycopy(simpleNames, 0, pkgName, 0, length);
115: } else {
116: pkgName = CharOperation.NO_STRINGS;
117: }
118: IPackageFragment pkgFragment = (IPackageFragment) this .packageHandles
119: .get(pkgName);
120: if (pkgFragment == null) {
121: pkgFragment = ((PackageFragmentRoot) this .lastPkgFragmentRoot)
122: .getPackageFragment(pkgName);
123: this .packageHandles.put(pkgName, pkgFragment);
124: }
125: IClassFile classFile = pkgFragment
126: .getClassFile(simpleNames[length]);
127: return (Openable) classFile;
128: } else {
129: // path to a file in a directory
130: // Optimization: cache package fragment root handle and package handles
131: int rootPathLength = -1;
132: if (this .lastPkgFragmentRootPath == null
133: || !(resourcePath
134: .startsWith(this .lastPkgFragmentRootPath)
135: && (rootPathLength = this .lastPkgFragmentRootPath
136: .length()) > 0 && resourcePath
137: .charAt(rootPathLength) == '/')) {
138: IPackageFragmentRoot root = this
139: .getPkgFragmentRoot(resourcePath);
140: if (root == null)
141: return null; // match is outside classpath
142: this .lastPkgFragmentRoot = root;
143: this .lastPkgFragmentRootPath = this .lastPkgFragmentRoot
144: .getPath().toString();
145: this .packageHandles = new HashtableOfArrayToObject(5);
146: }
147: // create handle
148: resourcePath = resourcePath
149: .substring(this .lastPkgFragmentRootPath.length() + 1);
150: String[] simpleNames = new Path(resourcePath).segments();
151: String[] pkgName;
152: int length = simpleNames.length - 1;
153: if (length > 0) {
154: pkgName = new String[length];
155: System.arraycopy(simpleNames, 0, pkgName, 0, length);
156: } else {
157: pkgName = CharOperation.NO_STRINGS;
158: }
159: IPackageFragment pkgFragment = (IPackageFragment) this .packageHandles
160: .get(pkgName);
161: if (pkgFragment == null) {
162: pkgFragment = ((PackageFragmentRoot) this .lastPkgFragmentRoot)
163: .getPackageFragment(pkgName);
164: this .packageHandles.put(pkgName, pkgFragment);
165: }
166: String simpleName = simpleNames[length];
167: if (org.eclipse.jdt.internal.core.util.Util
168: .isJavaLikeFileName(simpleName)) {
169: ICompilationUnit unit = pkgFragment
170: .getCompilationUnit(simpleName);
171: return (Openable) unit;
172: } else {
173: IClassFile classFile = pkgFragment
174: .getClassFile(simpleName);
175: return (Openable) classFile;
176: }
177: }
178: }
179:
180: /**
181: * Returns a handle denoting the class member identified by its scope.
182: */
183: public IJavaElement createElement(ClassScope scope,
184: ICompilationUnit unit, HashSet existingElements,
185: HashMap knownScopes) {
186: return createElement(scope, scope.referenceContext.sourceStart,
187: unit, existingElements, knownScopes);
188: }
189:
190: /**
191: * Create handle by adding child to parent obtained by recursing into parent scopes.
192: */
193: private IJavaElement createElement(Scope scope,
194: int elementPosition, ICompilationUnit unit,
195: HashSet existingElements, HashMap knownScopes) {
196: IJavaElement newElement = (IJavaElement) knownScopes.get(scope);
197: if (newElement != null)
198: return newElement;
199:
200: switch (scope.kind) {
201: case Scope.COMPILATION_UNIT_SCOPE:
202: newElement = unit;
203: break;
204: case Scope.CLASS_SCOPE:
205: IJavaElement parentElement = createElement(scope.parent,
206: elementPosition, unit, existingElements,
207: knownScopes);
208: switch (parentElement.getElementType()) {
209: case IJavaElement.COMPILATION_UNIT:
210: newElement = ((ICompilationUnit) parentElement)
211: .getType(new String(
212: scope.enclosingSourceType().sourceName));
213: break;
214: case IJavaElement.TYPE:
215: newElement = ((IType) parentElement)
216: .getType(new String(
217: scope.enclosingSourceType().sourceName));
218: break;
219: case IJavaElement.FIELD:
220: case IJavaElement.INITIALIZER:
221: case IJavaElement.METHOD:
222: IMember member = (IMember) parentElement;
223: if (member.isBinary()) {
224: return null;
225: } else {
226: newElement = member.getType(new String(scope
227: .enclosingSourceType().sourceName), 1);
228: // increment occurrence count if collision is detected
229: if (newElement != null) {
230: while (!existingElements.add(newElement))
231: ((SourceRefElement) newElement).occurrenceCount++;
232: }
233: }
234: break;
235: }
236: if (newElement != null) {
237: knownScopes.put(scope, newElement);
238: }
239: break;
240: case Scope.METHOD_SCOPE:
241: IType parentType = (IType) createElement(scope.parent,
242: elementPosition, unit, existingElements,
243: knownScopes);
244: MethodScope methodScope = (MethodScope) scope;
245: if (methodScope.isInsideInitializer()) {
246: // inside field or initializer, must find proper one
247: TypeDeclaration type = methodScope.referenceType();
248: int occurenceCount = 1;
249: for (int i = 0, length = type.fields.length; i < length; i++) {
250: FieldDeclaration field = type.fields[i];
251: if (field.declarationSourceStart < elementPosition
252: && field.declarationSourceEnd > elementPosition) {
253: switch (field.getKind()) {
254: case AbstractVariableDeclaration.FIELD:
255: case AbstractVariableDeclaration.ENUM_CONSTANT:
256: newElement = parentType
257: .getField(new String(field.name));
258: break;
259: case AbstractVariableDeclaration.INITIALIZER:
260: newElement = parentType
261: .getInitializer(occurenceCount);
262: break;
263: }
264: break;
265: } else if (field.getKind() == AbstractVariableDeclaration.INITIALIZER) {
266: occurenceCount++;
267: }
268: }
269: } else {
270: // method element
271: AbstractMethodDeclaration method = methodScope
272: .referenceMethod();
273: newElement = parentType.getMethod(new String(
274: method.selector), Util
275: .typeParameterSignatures(method));
276: if (newElement != null) {
277: knownScopes.put(scope, newElement);
278: }
279: }
280: break;
281: case Scope.BLOCK_SCOPE:
282: // standard block, no element per se
283: newElement = createElement(scope.parent, elementPosition,
284: unit, existingElements, knownScopes);
285: break;
286: }
287: return newElement;
288: }
289:
290: /**
291: * Returns the package fragment root that corresponds to the given jar path.
292: * See createOpenable(...) for the format of the jar path string.
293: * If not null, uses the given scope as a hint for getting Java project handles.
294: */
295: private IPackageFragmentRoot getJarPkgFragmentRoot(
296: String jarPathString, IJavaSearchScope scope) {
297:
298: IPath jarPath = new Path(jarPathString);
299:
300: Object target = JavaModel.getTarget(ResourcesPlugin
301: .getWorkspace().getRoot(), jarPath, false);
302: if (target instanceof IFile) {
303: // internal jar: is it on the classpath of its project?
304: // e.g. org.eclipse.swt.win32/ws/win32/swt.jar
305: // is NOT on the classpath of org.eclipse.swt.win32
306: IFile jarFile = (IFile) target;
307: JavaProject javaProject = (JavaProject) this .javaModel
308: .getJavaProject(jarFile);
309: try {
310: IClasspathEntry entry = javaProject
311: .getClasspathEntryFor(jarPath);
312: if (entry != null) {
313: return javaProject.getPackageFragmentRoot(jarFile);
314: }
315: } catch (JavaModelException e) {
316: // ignore and try to find another project
317: }
318: }
319:
320: // walk projects in the scope and find the first one that has the given jar path in its classpath
321: IJavaProject[] projects;
322: if (scope != null) {
323: IPath[] enclosingProjectsAndJars = scope
324: .enclosingProjectsAndJars();
325: int length = enclosingProjectsAndJars.length;
326: projects = new IJavaProject[length];
327: int index = 0;
328: for (int i = 0; i < length; i++) {
329: IPath path = enclosingProjectsAndJars[i];
330: if (!org.eclipse.jdt.internal.compiler.util.Util
331: .isArchiveFileName(path.lastSegment())) {
332: projects[index++] = this .javaModel
333: .getJavaProject(path.segment(0));
334: }
335: }
336: if (index < length) {
337: System.arraycopy(projects, 0,
338: projects = new IJavaProject[index], 0, index);
339: }
340: IPackageFragmentRoot root = getJarPkgFragmentRoot(jarPath,
341: target, projects);
342: if (root != null) {
343: return root;
344: }
345: }
346:
347: // not found in the scope, walk all projects
348: try {
349: projects = this .javaModel.getJavaProjects();
350: } catch (JavaModelException e) {
351: // java model is not accessible
352: return null;
353: }
354: return getJarPkgFragmentRoot(jarPath, target, projects);
355: }
356:
357: private IPackageFragmentRoot getJarPkgFragmentRoot(IPath jarPath,
358: Object target, IJavaProject[] projects) {
359: for (int i = 0, projectCount = projects.length; i < projectCount; i++) {
360: try {
361: JavaProject javaProject = (JavaProject) projects[i];
362: IClasspathEntry classpathEnty = javaProject
363: .getClasspathEntryFor(jarPath);
364: if (classpathEnty != null) {
365: if (target instanceof IFile) {
366: // internal jar
367: return javaProject
368: .getPackageFragmentRoot((IFile) target);
369: } else {
370: // external jar
371: return javaProject
372: .getPackageFragmentRoot0(jarPath);
373: }
374: }
375: } catch (JavaModelException e) {
376: // JavaModelException from getResolvedClasspath - a problem occured while accessing project: nothing we can do, ignore
377: }
378: }
379: return null;
380: }
381:
382: /**
383: * Returns the package fragment root that contains the given resource path.
384: */
385: private IPackageFragmentRoot getPkgFragmentRoot(String pathString) {
386:
387: IPath path = new Path(pathString);
388: IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
389: .getProjects();
390: for (int i = 0, max = projects.length; i < max; i++) {
391: try {
392: IProject project = projects[i];
393: if (!project.isAccessible()
394: || !project.hasNature(JavaCore.NATURE_ID))
395: continue;
396: IJavaProject javaProject = this .javaModel
397: .getJavaProject(project);
398: IPackageFragmentRoot[] roots = javaProject
399: .getPackageFragmentRoots();
400: for (int j = 0, rootCount = roots.length; j < rootCount; j++) {
401: PackageFragmentRoot root = (PackageFragmentRoot) roots[j];
402: if (root.getPath().isPrefixOf(path)
403: && !Util
404: .isExcluded(
405: path,
406: root
407: .fullInclusionPatternChars(),
408: root
409: .fullExclusionPatternChars(),
410: false)) {
411: return root;
412: }
413: }
414: } catch (CoreException e) {
415: // CoreException from hasNature - should not happen since we check that the project is accessible
416: // JavaModelException from getPackageFragmentRoots - a problem occured while accessing project: nothing we can do, ignore
417: }
418: }
419: return null;
420: }
421:
422: }
|