/******************************************************************************* * Copyright (c) 2000, 2016 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 * *******************************************************************************/ package org.eclipse.dltk.internal.core.search; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.dltk.compiler.CharOperation; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IModelElementVisitor; import org.eclipse.dltk.core.IProjectFragment; import org.eclipse.dltk.core.IScriptFolder; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.ScriptModelUtil; import org.eclipse.dltk.core.search.BasicSearchEngine; import org.eclipse.dltk.core.search.IDLTKSearchScope; import org.eclipse.dltk.core.search.TypeNameMatchRequestor; import org.eclipse.dltk.core.search.TypeNameRequestor; import org.eclipse.dltk.core.search.indexing.IIndexConstants; import org.eclipse.dltk.internal.compiler.env.AccessRestriction; import org.eclipse.dltk.internal.core.Openable; import org.eclipse.dltk.internal.core.util.HandleFactory; import org.eclipse.dltk.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; // scope is needed to retrieve project path for external resource private final IDLTKSearchScope scope; // in case of IJavaSearchScope defined by clients, use an HandleFactory // instead private HandleFactory handleFactory; /** * Cache package fragment root information to optimize speed performance. */ private String lastPkgFragmentRootPath; private IProjectFragment lastProjectFragment; /** * Cache package handles to optimize memory. */ private HashtableOfArrayToObject packageHandles; public TypeNameMatchRequestorWrapper(TypeNameMatchRequestor requestor, IDLTKSearchScope scope) { this.requestor = requestor; this.scope = scope; if (!(scope instanceof DLTKSearchScope)) { this.handleFactory = new HandleFactory(); } } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor# * acceptType(int, char[], char[], char[][], java.lang.String, * org.eclipse.jdt.internal.compiler.env.AccessRestriction) */ @Override public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, char[][] superTypes, String path, AccessRestriction access) { try { IType type = null; if (this.handleFactory != null) { Openable openable = this.handleFactory.createOpenable(path, this.scope); if (openable == null) return; switch (openable.getElementType()) { case IModelElement.SOURCE_MODULE: ISourceModule cu = (ISourceModule) 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])); } } else { type = cu.getType(new String(simpleTypeName)); } break; } } else { int separatorIndex = path .indexOf(IDLTKSearchScope.FILE_ENTRY_SEPARATOR); type = separatorIndex == -1 ? createTypeFromPath(path, new String(simpleTypeName), enclosingTypeNames) : createTypeFromZip(path, separatorIndex, new String( simpleTypeName), enclosingTypeNames); // if (DLTKCore.DEBUG) { // System.err.println("TO DO: Add types from zips..."); //$NON-NLS-1$ // } } if (type != null) { this.requestor.acceptTypeNameMatch(new DLTKSearchTypeNameMatch( type, modifiers)); } } catch (ModelException e) { // skip } } private IType createTypeFromZip(String resourcePath, int separatorIndex, String simpleTypeName, char[][] enclosingTypeNames) throws ModelException { // 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)) { final String archivePath = resourcePath .substring(0, separatorIndex); final IProjectFragment root = ((DLTKSearchScope) this.scope) .projectFragment(archivePath); if (root == null) return null; this.lastPkgFragmentRootPath = resourcePath.substring(0, separatorIndex); this.lastProjectFragment = 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; } IScriptFolder pkgFragment = (IScriptFolder) this.packageHandles .get(pkgName); if (pkgFragment == null) { pkgFragment = this.lastProjectFragment .getScriptFolder(ScriptModelUtil.toPath(pkgName)); this.packageHandles.put(pkgName, pkgFragment); } ISourceModule unit = pkgFragment.getSourceModule(simpleNames[length]); if (enclosingTypeNames == IIndexConstants.ONE_ZERO_CHAR) { final TypeFinder finder = new TypeFinder(simpleTypeName); unit.accept(finder); return finder.type; } int etnLength = enclosingTypeNames == null ? 0 : enclosingTypeNames.length; String containerTypeName = (etnLength == 0) ? simpleTypeName : new String(enclosingTypeNames[0]); IType type = null; IType[] containerTypes = unit.getTypes(); for (int cnt = 0, max = containerTypes.length; cnt < max; cnt++) { if (containerTypeName.equals(containerTypes[cnt].getElementName())) { if (etnLength > 1) { type = resolveType(containerTypes[cnt], enclosingTypeNames, 1); if (type != null) { type = type.getType(simpleTypeName); } } else if (etnLength == 1) { type = containerTypes[cnt].getType(simpleTypeName); } else { type = containerTypes[cnt]; } if (type != null && type.exists()) { break; } } } return type; } private IType createTypeFromPath(String resourcePath, String simpleTypeName, char[][] enclosingTypeNames) throws ModelException { // 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) == '/')) { IProjectFragment root = ((DLTKSearchScope) this.scope) .projectFragment(resourcePath); if (root == null) return null; this.lastProjectFragment = root; this.lastPkgFragmentRootPath = this.lastProjectFragment.getPath() .toString(); this.packageHandles = new HashtableOfArrayToObject(5); } IPath resourcePath2 = new Path(resourcePath); if (!resourcePath2.toString().startsWith(this.lastPkgFragmentRootPath)) { return null; } // 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; } IScriptFolder pkgFragment = (IScriptFolder) this.packageHandles .get(pkgName); if (pkgFragment == null) { pkgFragment = this.lastProjectFragment .getScriptFolder(ScriptModelUtil.toPath(pkgName)); this.packageHandles.put(pkgName, pkgFragment); } String simpleName = simpleNames[length]; ISourceModule unit = pkgFragment.getSourceModule(simpleName); if (enclosingTypeNames == IIndexConstants.ONE_ZERO_CHAR) { final TypeFinder finder = new TypeFinder(simpleTypeName); unit.accept(finder); return finder.type; } int etnLength = enclosingTypeNames == null ? 0 : enclosingTypeNames.length; String containerTypeName = (etnLength == 0) ? simpleTypeName : new String(enclosingTypeNames[0]); IType type = null; IType[] containerTypes = unit.getTypes(); for (int cnt = 0, max = containerTypes.length; cnt < max; cnt++) { if (containerTypeName.equals(containerTypes[cnt].getElementName())) { if (etnLength > 1) { type = resolveType(containerTypes[cnt], enclosingTypeNames, 1); if (type != null) { type = type.getType(simpleTypeName); } } else if (etnLength == 1) { type = containerTypes[cnt].getType(simpleTypeName); } else { type = containerTypes[cnt]; } if (type != null && type.exists()) { break; } } } return type; } static class TypeFinder implements IModelElementVisitor { IType type; final String simpleTypeName; public TypeFinder(String simpleTypeName) { this.simpleTypeName = simpleTypeName; } @Override public boolean visit(IModelElement element) { if (type == null) { if (element.getElementType() == IModelElement.TYPE && simpleTypeName.equals(element.getElementName())) { type = (IType) element; return false; } return true; } else { return false; } } } private IType resolveType(IType parentType, char[][] enclosingTypeNames, int index) throws ModelException { IType resolvedType = null; IType[] childTypes = parentType.getTypes(); for (int cnt = 0, max = childTypes.length; cnt < max; cnt++) { if (childTypes[cnt].getElementName().equals( new String(enclosingTypeNames[index]))) { if (index != (enclosingTypeNames.length - 1)) { resolvedType = resolveType(childTypes[cnt], enclosingTypeNames, (index + 1)); } else if (childTypes[cnt].exists()) { resolvedType = childTypes[cnt]; break; } } } return resolvedType; } }