/******************************************************************************* * Copyright (c) 2000, 2011 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 * Stephan Herrmann - Contribution for bug 215139 *******************************************************************************/ package org.eclipse.jdt.internal.core.search; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IAccessRule; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.TypeNameMatchRequestor; import org.eclipse.jdt.core.search.TypeNameRequestor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.core.Openable; import org.eclipse.jdt.internal.core.PackageFragmentRoot; import org.eclipse.jdt.internal.core.util.HandleFactory; import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; /** * Wrapper used to link {@link IRestrictedAccessTypeRequestor} with {@link TypeNameRequestor}. * This wrapper specifically allows usage of internal method {@link BasicSearchEngine#searchAllTypeNames( * char[] packageName, * int packageMatchRule, * char[] typeName, * int typeMatchRule, * int searchFor, * org.eclipse.jdt.core.search.IJavaSearchScope scope, * IRestrictedAccessTypeRequestor nameRequestor, * int waitingPolicy, * org.eclipse.core.runtime.IProgressMonitor monitor) }. * from API method {@link org.eclipse.jdt.core.search.SearchEngine#searchAllTypeNames( * char[] packageName, * int packageMatchRule, * char[] typeName, * int matchRule, * int searchFor, * org.eclipse.jdt.core.search.IJavaSearchScope scope, * TypeNameRequestor nameRequestor, * int waitingPolicy, * org.eclipse.core.runtime.IProgressMonitor monitor) }. */ public class TypeNameMatchRequestorWrapper implements IRestrictedAccessTypeRequestor { TypeNameMatchRequestor requestor; private IJavaSearchScope scope; // scope is needed to retrieve project path for external resource private HandleFactory handleFactory; // in case of IJavaSearchScope defined by clients, use an HandleFactory instead /** * Cache package fragment root information to optimize speed performance. */ private String lastPkgFragmentRootPath; private IPackageFragmentRoot lastPkgFragmentRoot; /** * Cache package handles to optimize memory. */ private HashtableOfArrayToObject packageHandles; private Object lastProject; private long complianceValue; public TypeNameMatchRequestorWrapper(TypeNameMatchRequestor requestor, IJavaSearchScope scope) { this.requestor = requestor; this.scope = scope; if (!(scope instanceof AbstractJavaSearchScope)) { this.handleFactory = new HandleFactory(); } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor#acceptType(int, char[], char[], char[][], java.lang.String, org.eclipse.jdt.internal.compiler.env.AccessRestriction) */ public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) { // Get type try { IType type = null; if (this.handleFactory != null) { Openable openable = this.handleFactory.createOpenable(path, this.scope); if (openable == null) return; switch (openable.getElementType()) { case IJavaElement.COMPILATION_UNIT: ICompilationUnit cu = (ICompilationUnit) openable; if (enclosingTypeNames != null && enclosingTypeNames.length > 0) { type = cu.getType(new String(enclosingTypeNames[0])); for (int j=1, l=enclosingTypeNames.length; j<l; j++) { type = type.getType(new String(enclosingTypeNames[j])); } type = type.getType(new String(simpleTypeName)); } else { type = cu.getType(new String(simpleTypeName)); } break; case IJavaElement.CLASS_FILE: type = ((IClassFile)openable).getType(); break; } } else { int separatorIndex= path.indexOf(IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR); type = separatorIndex == -1 ? createTypeFromPath(path, new String(simpleTypeName), enclosingTypeNames) : createTypeFromJar(path, separatorIndex); } // Accept match if the type has been found if (type != null) { // hierarchy scopes require one more check: if (!(this.scope instanceof HierarchyScope) || ((HierarchyScope)this.scope).enclosesFineGrained(type)) { // Create the match final JavaSearchTypeNameMatch match = new JavaSearchTypeNameMatch(type, modifiers); // Update match accessibility if(access != null) { switch (access.getProblemId()) { case IProblem.ForbiddenReference: match.setAccessibility(IAccessRule.K_NON_ACCESSIBLE); break; case IProblem.DiscouragedReference: match.setAccessibility(IAccessRule.K_DISCOURAGED); break; } } // Accept match this.requestor.acceptTypeNameMatch(match); } } } catch (JavaModelException e) { // skip } } private IType createTypeFromJar(String resourcePath, int separatorIndex) throws JavaModelException { // path to a class file inside a jar // Optimization: cache package fragment root handle and package handles if (this.lastPkgFragmentRootPath == null || this.lastPkgFragmentRootPath.length() > resourcePath.length() || !resourcePath.startsWith(this.lastPkgFragmentRootPath)) { String jarPath= resourcePath.substring(0, separatorIndex); IPackageFragmentRoot root= ((AbstractJavaSearchScope)this.scope).packageFragmentRoot(resourcePath, separatorIndex, jarPath); if (root == null) return null; this.lastPkgFragmentRootPath= jarPath; this.lastPkgFragmentRoot= root; this.packageHandles= new HashtableOfArrayToObject(5); } // create handle String classFilePath= resourcePath.substring(separatorIndex + 1); String[] simpleNames = new Path(classFilePath).segments(); String[] pkgName; int length = simpleNames.length-1; if (length > 0) { pkgName = new String[length]; System.arraycopy(simpleNames, 0, pkgName, 0, length); } else { pkgName = CharOperation.NO_STRINGS; } IPackageFragment pkgFragment= (IPackageFragment) this.packageHandles.get(pkgName); if (pkgFragment == null) { pkgFragment= ((PackageFragmentRoot) this.lastPkgFragmentRoot).getPackageFragment(pkgName); // filter org.apache.commons.lang.enum package for projects above 1.5 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=317264 if (length == 5 && pkgName[4].equals("enum")) { //$NON-NLS-1$ IJavaProject proj = (IJavaProject)pkgFragment.getAncestor(IJavaElement.JAVA_PROJECT); if (!proj.equals(this.lastProject)) { String complianceStr = proj.getOption(CompilerOptions.OPTION_Source, true); this.complianceValue = CompilerOptions.versionToJdkLevel(complianceStr); this.lastProject = proj; } if (this.complianceValue >= ClassFileConstants.JDK1_5) return null; } this.packageHandles.put(pkgName, pkgFragment); } return pkgFragment.getClassFile(simpleNames[length]).getType(); } private IType createTypeFromPath(String resourcePath, String simpleTypeName, char[][] enclosingTypeNames) throws JavaModelException { // path to a file in a directory // Optimization: cache package fragment root handle and package handles int rootPathLength = -1; if (this.lastPkgFragmentRootPath == null || !(resourcePath.startsWith(this.lastPkgFragmentRootPath) && (rootPathLength = this.lastPkgFragmentRootPath.length()) > 0 && resourcePath.charAt(rootPathLength) == '/')) { PackageFragmentRoot root = (PackageFragmentRoot) ((AbstractJavaSearchScope)this.scope).packageFragmentRoot(resourcePath, -1/*not a jar*/, null/*no jar path*/); if (root == null) return null; this.lastPkgFragmentRoot = root; this.lastPkgFragmentRootPath = root.internalPath().toString(); this.packageHandles = new HashtableOfArrayToObject(5); } // create handle resourcePath = resourcePath.substring(this.lastPkgFragmentRootPath.length() + 1); String[] simpleNames = new Path(resourcePath).segments(); String[] pkgName; int length = simpleNames.length-1; if (length > 0) { pkgName = new String[length]; System.arraycopy(simpleNames, 0, pkgName, 0, length); } else { pkgName = CharOperation.NO_STRINGS; } IPackageFragment pkgFragment= (IPackageFragment) this.packageHandles.get(pkgName); if (pkgFragment == null) { pkgFragment= ((PackageFragmentRoot) this.lastPkgFragmentRoot).getPackageFragment(pkgName); this.packageHandles.put(pkgName, pkgFragment); } String simpleName= simpleNames[length]; if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(simpleName)) { ICompilationUnit unit= pkgFragment.getCompilationUnit(simpleName); int etnLength = enclosingTypeNames == null ? 0 : enclosingTypeNames.length; IType type = (etnLength == 0) ? unit.getType(simpleTypeName) : unit.getType(new String(enclosingTypeNames[0])); if (etnLength > 0) { for (int i=1; i<etnLength; i++) { type = type.getType(new String(enclosingTypeNames[i])); } type = type.getType(simpleTypeName); } return type; } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(simpleName)){ IClassFile classFile= pkgFragment.getClassFile(simpleName); return classFile.getType(); } return null; } }