/*
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.core.builder;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
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.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.internal.core.IJavaElementRequestor;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.PackageFragmentRoot;
import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
import org.eclipse.jdt.internal.core.util.Util;
/**
* @author Andrew Eisenberg
* @created Aug 18, 2009
*
* A NameLookup for Groovy classes to help look for non-tope level types and member types
*/
public class GroovyNameLookup extends NameLookup {
@SuppressWarnings("unchecked")
public GroovyNameLookup(NameLookup other) {
this(new IPackageFragmentRoot[0], new HashtableOfArrayToObject(), new ICompilationUnit[0], new HashMap<PackageFragmentRoot, ClasspathEntry>());
this.packageFragmentRoots = (IPackageFragmentRoot[]) ReflectionUtils.getPrivateField(NameLookup.class, "packageFragmentRoots", other);
this.packageFragments = (HashtableOfArrayToObject) ReflectionUtils.getPrivateField(NameLookup.class, "packageFragments", other);
this.typesInWorkingCopies = (HashMap<PackageFragmentRoot, ?>) ReflectionUtils.getPrivateField(NameLookup.class, "typesInWorkingCopies", other);
this.rootToResolvedEntries = (Map<PackageFragmentRoot, ClasspathEntry>) ReflectionUtils.getPrivateField(NameLookup.class, "rootToResolvedEntries", other);
}
public GroovyNameLookup(IPackageFragmentRoot[] packageFragmentRoots, HashtableOfArrayToObject packageFragments, ICompilationUnit[] workingCopies, Map<PackageFragmentRoot, ClasspathEntry> rootToResolvedEntries) {
super(packageFragmentRoots, packageFragments, workingCopies, rootToResolvedEntries);
}
/**
* Copied from parent class
* Changes marked with // GROOVY begin and // GROOVY end
*/
@Override
protected void seekTypesInSourcePackage(
String name,
IPackageFragment pkg,
int firstDot,
boolean partialMatch,
String topLevelTypeName,
int acceptFlags,
IJavaElementRequestor requestor) {
long start = -1;
if (VERBOSE)
start = System.currentTimeMillis();
try {
if (!partialMatch) {
try {
IJavaElement[] compilationUnits = pkg.getChildren();
for (int i = 0, length = compilationUnits.length; i < length; i++) {
if (requestor.isCanceled())
return;
// GROOVY begin
// removed statements that continue if type is not the same name as the compilation unit
ICompilationUnit cu = (ICompilationUnit) compilationUnits[i];
IType type = cu.getType(name);
if (
// GROOVY begin
type.exists() &&
// GROOVY end
acceptType(type, acceptFlags, true/*a source type*/)) { // accept type checks for existence
requestor.acceptType(type);
break; // since an exact match was requested, no other matching type can exist
}
// now look for member types
String mainType = cu.getElementName();
int dotIndex = mainType.indexOf('.');
mainType = mainType.substring(0, dotIndex);
type = cu.getType(mainType);
if (type.exists()) {
type = getMemberType(type, name, firstDot);
if (
// GROOVY begin
type.exists() &&
// GROOVY end
acceptType(type, acceptFlags, true/*a source type*/)) { // accept type checks for existence
requestor.acceptType(type);
break; // since an exact match was requested, no other matching type can exist
}
}
// GROOVY end
}
} catch (JavaModelException e) {
// package doesn't exist -> ignore
}
} else {
try {
String cuPrefix = firstDot == -1 ? name : name.substring(0, firstDot);
IJavaElement[] compilationUnits = pkg.getChildren();
for (int i = 0, length = compilationUnits.length; i < length; i++) {
if (requestor.isCanceled())
return;
IJavaElement cu = compilationUnits[i];
if (!cu.getElementName().toLowerCase().startsWith(cuPrefix))
continue;
try {
IType[] types = ((ICompilationUnit) cu).getTypes();
for (int j = 0, typeLength = types.length; j < typeLength; j++)
seekTypesInTopLevelType(name, firstDot, types[j], requestor, acceptFlags);
} catch (JavaModelException e) {
// cu doesn't exist -> ignore
}
}
} catch (JavaModelException e) {
// package doesn't exist -> ignore
}
}
} finally {
if (VERBOSE)
this.timeSpentInSeekTypesInSourcePackage += System.currentTimeMillis()-start;
}
}
private IType getMemberType(IType type, String name, int dot) {
type = type.getType(name);
return type;
}
/**
* Copied from parent class
* Changes marked with // GROOVY begin and // GROOVY end
*/
@Override
protected void seekTypesInBinaryPackage(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) {
long start = -1;
if (VERBOSE)
start = System.currentTimeMillis();
try {
// GROOVY begin
// ensure ends with .class
if (!name.endsWith(".class")) {
name += ".class";
}
// GROOVY end
if (!partialMatch) {
// exact match
if (requestor.isCanceled()) return;
ClassFile classFile = (ClassFile) pkg.getClassFile(name);
if (classFile.existsUsingJarTypeCache()) {
IType type = classFile.getType();
if (acceptType(type, acceptFlags, false/*not a source type*/)) {
requestor.acceptType(type);
}
}
// GROOVY begin
// class file may still exist as an inner type
IJavaElement[] classFiles= null;
try {
classFiles= pkg.getChildren();
} catch (JavaModelException npe) {
return; // the package is not present
}
for (IJavaElement elt : classFiles) {
classFile = (ClassFile) elt;
if (classFile.getElementName().endsWith("$" + name)) {
IType type = classFile.getType();
if (acceptType(type, acceptFlags, false/*not a source type*/)) {
requestor.acceptType(type);
}
}
}
// GROOVY end
} else {
IJavaElement[] classFiles= null;
try {
classFiles= pkg.getChildren();
} catch (JavaModelException npe) {
return; // the package is not present
}
int length= classFiles.length;
String unqualifiedName = name;
int index = name.lastIndexOf('$');
if (index != -1) {
//the type name of the inner type
unqualifiedName = Util.localTypeName(name, index, name.length());
// unqualifiedName is empty if the name ends with a '$' sign.
// See http://dev.eclipse.org/bugs/show_bug.cgi?id=14642
}
int matchLength = name.length();
for (int i = 0; i < length; i++) {
if (requestor.isCanceled())
return;
IJavaElement classFile= classFiles[i];
// MatchName will never have the extension ".class" and the elementName always will.
String elementName = classFile.getElementName();
if (elementName.regionMatches(true /*ignore case*/, 0, name, 0, matchLength)) {
IType type = ((ClassFile) classFile).getType();
String typeName = type.getElementName();
if (typeName.length() > 0 && !Character.isDigit(typeName.charAt(0))) { //not an anonymous type
if (nameMatches(unqualifiedName, type, true/*partial match*/) && acceptType(type, acceptFlags, false/*not a source type*/))
requestor.acceptType(type);
}
}
}
}
} finally {
if (VERBOSE)
this.timeSpentInSeekTypesInBinaryPackage += System.currentTimeMillis()-start;
}
}
}