/*******************************************************************************
* 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;
}
}