blob: f0a368cb0de1f2c391ddb13ebc18a687eb471f82 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
* Kelly Campbell <kellyc@google.com> - Hangs in SourceMapper during java proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=281575
* Stephan Herrmann - Contribution for Bug 380048 - error popup when navigating to source files
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IModuleDescription;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
import org.eclipse.jdt.internal.compiler.SourceElementParser;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.compiler.util.Util;
import org.eclipse.jdt.internal.core.util.ReferenceInfoAdapter;
/**
* A SourceMapper maps source code in a ZIP file to binary types or
* binary modules in a JAR. The SourceMapper uses the fuzzy parser
* to identify source fragments in a .java file, and attempts to match
* the source code with children in a binary type.
* Since a module has no children in the Java Model no such matching
* happens in that case.
* A SourceMapper is associated with a JarPackageFragment by an AttachSourceOperation.
*
* @see org.eclipse.jdt.internal.core.JarPackageFragment
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SourceMapper
extends ReferenceInfoAdapter
implements ISourceElementRequestor, SuffixConstants {
public static class LocalVariableElementKey {
String parent;
String name;
public LocalVariableElementKey(IJavaElement method, String name) {
StringBuffer buffer = new StringBuffer();
buffer
.append(method.getParent().getHandleIdentifier())
.append('#')
.append(method.getElementName())
.append('(');
if (method.getElementType() == IJavaElement.METHOD) {
String[] parameterTypes = ((IMethod) method).getParameterTypes();
for (int i = 0, max = parameterTypes.length; i < max; i++) {
if (i > 0) {
buffer.append(',');
}
buffer.append(Signature.getSignatureSimpleName(parameterTypes[i]));
}
}
buffer.append(')');
this.parent = String.valueOf(buffer);
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
result = prime * result + ((this.parent == null) ? 0 : this.parent.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LocalVariableElementKey other = (LocalVariableElementKey) obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name))
return false;
if (this.parent == null) {
if (other.parent != null)
return false;
} else if (!this.parent.equals(other.parent))
return false;
return true;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append('(').append(this.parent).append('.').append(this.name).append(')');
return String.valueOf(buffer);
}
}
public static boolean VERBOSE = false;
/**
* Specifies the location of the package fragment roots within
* the zip (empty specifies the default root). <code>null</code> is
* not a valid root path.
*/
protected ArrayList rootPaths;
/**
* The binary type or module source is being mapped for
*/
protected NamedMember binaryTypeOrModule;
/**
* The location of the zip file containing source.
*/
protected IPath sourcePath;
/**
* Specifies the location of the package fragment root within
* the zip (empty specifies the default root). <code>null</code> is
* not a valid root path.
*/
protected String rootPath = ""; //$NON-NLS-1$
/**
* Table that maps a binary method to its parameter names.
* Keys are the method handles, entries are <code>char[][]</code>.
*/
protected HashMap parameterNames;
/**
* Table that maps a binary element to its <code>SourceRange</code>s.
* Keys are the element handles, entries are <code>SourceRange[]</code> which
* is a two element array; the first being source range, the second
* being name range.
*/
protected HashMap sourceRanges;
/*
* A map from IJavaElement to String[]
*/
protected HashMap categories;
/**
* Table that contains all source ranges for local variables.
* Keys are the special local variable elements, entries are <code>char[][]</code>.
*/
protected HashMap parametersRanges;
/**
* Set that contains all final local variables.
*/
protected HashSet finalParameters;
/**
* The unknown source range {-1, 0}
*/
public static final SourceRange UNKNOWN_RANGE = new SourceRange(-1, 0);
/**
* The position within the source of the start of the
* current member element, or -1 if we are outside a member.
*/
protected int[] memberDeclarationStart;
/**
* The <code>SourceRange</code> of the name of the current member element.
*/
protected SourceRange[] memberNameRange;
/**
* The name of the current member element.
*/
protected String[] memberName;
/**
* The parameter names for the current member method element.
*/
protected char[][][] methodParameterNames;
/**
* The parameter types for the current member method element.
*/
protected char[][][] methodParameterTypes;
/**
* The element searched for
*/
protected IJavaElement searchedElement;
/**
* imports references (keyed by binaryTypeOrModule)
*/
private HashMap importsTable;
private HashMap importsCounterTable;
/**
* Enclosing type information
*/
IType[] types;
int[] typeDeclarationStarts;
SourceRange[] typeNameRanges;
int[] typeModifiers;
int typeDepth;
/**
* Module information
*/
SourceRange moduleNameRange;
int moduleDeclarationStart;
int moduleModifiers;
/**
* Anonymous counter in case we want to map the source of an anonymous class.
*/
int anonymousCounter;
int anonymousClassName;
String encoding;
String defaultEncoding;
/**
*Options to be used
*/
Map options;
/**
* Use to handle root paths inference
*/
private boolean areRootPathsComputed;
public SourceMapper() {
this.areRootPathsComputed = false;
}
public SourceMapper(IPath sourcePath, String rootPath, Map options) {
this(sourcePath, rootPath, options, null);
}
/**
* Creates a <code>SourceMapper</code> that locates source in the zip file
* at the given location in the specified package fragment root.
*/
public SourceMapper(IPath sourcePath, String rootPath, Map options, String encoding) {
this.areRootPathsComputed = false;
this.options = options;
this.encoding = encoding;
try {
this.defaultEncoding = ResourcesPlugin.getWorkspace().getRoot().getDefaultCharset();
} catch (CoreException e) {
// use no encoding
}
if (rootPath != null) {
this.rootPath = rootPath;
this.rootPaths = new ArrayList();
this.rootPaths.add(rootPath);
}
this.sourcePath = sourcePath;
this.sourceRanges = new HashMap();
this.parametersRanges = new HashMap();
this.parameterNames = new HashMap();
this.importsTable = new HashMap();
this.importsCounterTable = new HashMap();
}
/**
* @see ISourceElementRequestor
*/
@Override
public void acceptImport(
int declarationStart,
int declarationEnd,
int nameStart,
int nameEnd,
char[][] tokens,
boolean onDemand,
int modifiers) {
char[][] imports = (char[][]) this.importsTable.get(this.binaryTypeOrModule);
int importsCounter;
if (imports == null) {
imports = new char[5][];
importsCounter = 0;
} else {
importsCounter = ((Integer) this.importsCounterTable.get(this.binaryTypeOrModule)).intValue();
}
if (imports.length == importsCounter) {
System.arraycopy(
imports,
0,
(imports = new char[importsCounter * 2][]),
0,
importsCounter);
}
char[] name = CharOperation.concatWith(tokens, '.');
if (onDemand) {
int nameLength = name.length;
System.arraycopy(name, 0, (name = new char[nameLength + 2]), 0, nameLength);
name[nameLength] = '.';
name[nameLength + 1] = '*';
}
imports[importsCounter++] = name;
this.importsTable.put(this.binaryTypeOrModule, imports);
this.importsCounterTable.put(this.binaryTypeOrModule, Integer.valueOf(importsCounter));
}
/**
* @see ISourceElementRequestor
*/
@Override
public void acceptLineSeparatorPositions(int[] positions) {
//do nothing
}
/**
* @see ISourceElementRequestor
*/
@Override
public void acceptPackage(ImportReference importReference) {
//do nothing
}
/**
* @see ISourceElementRequestor
*/
@Override
public void acceptProblem(CategorizedProblem problem) {
//do nothing
}
private void addCategories(IJavaElement element, char[][] elementCategories) {
if (elementCategories == null) return;
if (this.categories == null)
this.categories = new HashMap();
this.categories.put(element, CharOperation.toStrings(elementCategories));
}
/**
* Closes this <code>SourceMapper</code>'s zip file. Once this is done, this
* <code>SourceMapper</code> cannot be used again.
*/
public void close() {
this.sourceRanges = null;
this.parameterNames = null;
this.parametersRanges = null;
this.finalParameters = null;
}
/**
* NOT API, public only for access by Unit tests.
* Converts these type names to unqualified signatures. This needs to be done in order to be consistent
* with the way the source range is retrieved.
* @see SourceMapper#getUnqualifiedMethodHandle
* @see Signature
*/
public String[] convertTypeNamesToSigs(char[][] typeNames) {
if (typeNames == null)
return CharOperation.NO_STRINGS;
int n = typeNames.length;
if (n == 0)
return CharOperation.NO_STRINGS;
String[] typeSigs = new String[n];
for (int i = 0; i < n; ++i) {
char[] typeSig = Signature.createCharArrayTypeSignature(typeNames[i], false);
// transforms signatures that contains a qualification into unqualified signatures
// e.g. "QX<+QMap.Entry;>;" becomes "QX<+QEntry;>;"
StringBuffer simpleTypeSig = null;
int start = 0;
int dot = -1;
int length = typeSig.length;
for (int j = 0; j < length; j++) {
switch (typeSig[j]) {
case Signature.C_UNRESOLVED:
if (simpleTypeSig != null)
simpleTypeSig.append(typeSig, start, j-start);
start = j;
break;
case Signature.C_DOT:
dot = j;
break;
case Signature.C_GENERIC_START:
int matchingEnd = findMatchingGenericEnd(typeSig, j+1);
if (matchingEnd > 0 && matchingEnd+1 < length && typeSig[matchingEnd+1] == Signature.C_DOT) {
// found Head<Param>.Tail -> discard everything except Tail
if (simpleTypeSig == null)
simpleTypeSig = new StringBuffer().append(typeSig, 0, start);
simpleTypeSig.append(Signature.C_UNRESOLVED);
start = j = matchingEnd+2;
break;
}
//$FALL-THROUGH$
case Signature.C_NAME_END:
if (dot > start) {
if (simpleTypeSig == null)
simpleTypeSig = new StringBuffer().append(typeSig, 0, start);
simpleTypeSig.append(Signature.C_UNRESOLVED);
simpleTypeSig.append(typeSig, dot+1, j-dot-1);
start = j;
}
break;
}
}
if (simpleTypeSig == null) {
typeSigs[i] = new String(typeSig);
} else {
simpleTypeSig.append(typeSig, start, length-start);
typeSigs[i] = simpleTypeSig.toString();
}
}
return typeSigs;
}
private int findMatchingGenericEnd(char[] sig, int start) {
int nesting = 0;
int length = sig.length;
for (int i=start; i < length; i++) {
switch (sig[i]) {
case Signature.C_GENERIC_START:
nesting++;
break;
case Signature.C_GENERIC_END:
if (nesting == 0)
return i;
nesting--;
break;
}
}
return -1;
}
class JrtPackageNamesAdderVisitor implements JRTUtil.JrtFileVisitor<java.nio.file.Path> {
public final HashSet firstLevelPackageNames;
final IPackageFragmentRoot root;
public String sourceLevel = null;
public String complianceLevel = null;
public boolean containsADefaultPackage;
public boolean containsJavaSource;
JrtPackageNamesAdderVisitor(HashSet firstLevelPackageNames, String sourceLevel, String complianceLevel,
boolean containsADefaultPackage, boolean containsJavaSource, IPackageFragmentRoot root) {
this.firstLevelPackageNames = firstLevelPackageNames;
this.root = root;
this.sourceLevel = sourceLevel;
this.complianceLevel = complianceLevel;
this.containsADefaultPackage = containsADefaultPackage;
this.containsJavaSource = containsJavaSource;
}
@Override
public FileVisitResult visitPackage(java.nio.file.Path dir, java.nio.file.Path mod, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(java.nio.file.Path file, java.nio.file.Path mod, BasicFileAttributes attrs) throws IOException {
String entryName = file.toString();
if (Util.isClassFileName(entryName)) {
int index = entryName.indexOf('/');
if (index != -1) {
String firstLevelPackageName = entryName.substring(0, index);
if (!this.firstLevelPackageNames.contains(firstLevelPackageName)) {
if (this.sourceLevel == null) {
IJavaProject project = this.root.getJavaProject();
this.sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
this.complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
}
IStatus status = JavaConventions.validatePackageName(firstLevelPackageName, this.sourceLevel, this.complianceLevel);
if (status.isOK() || status.getSeverity() == IStatus.WARNING) {
this.firstLevelPackageNames.add(firstLevelPackageName);
}
}
} else {
this.containsADefaultPackage = true;
}
} else if (!this.containsJavaSource && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(entryName)) {
this.containsJavaSource = true;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitModule(java.nio.file.Path path, String name) throws IOException {
return FileVisitResult.CONTINUE;
}
}
private synchronized void computeAllRootPaths(IJavaElement typeOrModule) {
if (this.areRootPathsComputed) {
return;
}
IPackageFragmentRoot root = (IPackageFragmentRoot) typeOrModule.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
IPath pkgFragmentRootPath = root.getPath();
final HashSet tempRoots = new HashSet();
long time = 0;
if (VERBOSE) {
System.out.println("compute all root paths for " + root.getElementName()); //$NON-NLS-1$
time = System.currentTimeMillis();
}
final HashSet firstLevelPackageNames = new HashSet();
boolean containsADefaultPackage = false;
boolean containsJavaSource = !pkgFragmentRootPath.equals(this.sourcePath); // used to optimize zip file reading only if source path and root path are equals, otherwise assume that attachment contains Java source
String sourceLevel = null;
String complianceLevel = null;
if (Util.isJrt(pkgFragmentRootPath.toOSString())) {
try {
JrtPackageNamesAdderVisitor jrtPackageNamesAdderVisitor = new JrtPackageNamesAdderVisitor(firstLevelPackageNames,
sourceLevel, complianceLevel, containsADefaultPackage, containsJavaSource, root);
org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(root.getPath().toFile(), jrtPackageNamesAdderVisitor, JRTUtil.NOTIFY_FILES);
sourceLevel = jrtPackageNamesAdderVisitor.sourceLevel;
complianceLevel = jrtPackageNamesAdderVisitor.complianceLevel;
containsADefaultPackage = jrtPackageNamesAdderVisitor.containsADefaultPackage;
containsJavaSource = jrtPackageNamesAdderVisitor.containsJavaSource;
} catch (IOException e) {
// We are not reading any specific file, so, move on for now
if (VERBOSE) {
e.printStackTrace();
}
}
} else if (root.isArchive()) {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
ZipFile zip = null;
try {
zip = manager.getZipFile(pkgFragmentRootPath);
for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName = entry.getName();
if (!entry.isDirectory()) {
if (Util.isClassFileName(entryName)) {
int index = entryName.indexOf('/');
if (index != -1) {
String firstLevelPackageName = entryName.substring(0, index);
if (!firstLevelPackageNames.contains(firstLevelPackageName)) {
if (sourceLevel == null) {
IJavaProject project = root.getJavaProject();
sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
}
IStatus status = JavaConventions.validatePackageName(firstLevelPackageName, sourceLevel, complianceLevel);
if (status.isOK() || status.getSeverity() == IStatus.WARNING) {
firstLevelPackageNames.add(firstLevelPackageName);
}
}
} else {
containsADefaultPackage = true;
}
} else if (!containsJavaSource && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(entryName)) {
containsJavaSource = true;
}
}
}
} catch (CoreException e) {
// ignore
} finally {
manager.closeZipFile(zip); // handle null case
}
} else {
Object target = JavaModel.getTarget(root.getPath(), true);
if (target instanceof IResource) {
IResource resource = (IResource) target;
if (resource instanceof IContainer) {
try {
IResource[] members = ((IContainer) resource).members();
for (int i = 0, max = members.length; i < max; i++) {
IResource member = members[i];
String resourceName = member.getName();
if (member.getType() == IResource.FOLDER) {
if (sourceLevel == null) {
IJavaProject project = root.getJavaProject();
sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
}
IStatus status = JavaConventions.validatePackageName(resourceName, sourceLevel, complianceLevel);
if (status.isOK() || status.getSeverity() == IStatus.WARNING) {
firstLevelPackageNames.add(resourceName);
}
} else if (Util.isClassFileName(resourceName)) {
containsADefaultPackage = true;
} else if (!containsJavaSource && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName)) {
containsJavaSource = true;
}
}
} catch (CoreException e) {
// ignore
}
}
}
}
if (containsJavaSource) { // no need to read source attachment if it contains no Java source (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=190840 )
Object target = JavaModel.getTarget(this.sourcePath, true);
if (target instanceof IContainer) {
IContainer folder = (IContainer)target;
computeRootPath(folder, firstLevelPackageNames, containsADefaultPackage, tempRoots, folder.getFullPath().segmentCount()/*if external folder, this is the linked folder path*/);
} else {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
ZipFile zip = null;
try {
zip = manager.getZipFile(this.sourcePath);
for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName;
if (!entry.isDirectory() && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(entryName = entry.getName())) {
IPath path = new Path(entryName);
int segmentCount = path.segmentCount();
if (segmentCount > 1) {
for (int i = 0, max = path.segmentCount() - 1; i < max; i++) {
if (firstLevelPackageNames.contains(path.segment(i))) {
tempRoots.add(path.uptoSegment(i));
// don't break here as this path could contain other first level package names (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=74014)
}
if (i == max - 1 && containsADefaultPackage) {
tempRoots.add(path.uptoSegment(max));
}
}
} else if (containsADefaultPackage) {
tempRoots.add(new Path("")); //$NON-NLS-1$
}
}
}
} catch (CoreException e) {
// ignore
} finally {
manager.closeZipFile(zip); // handle null case
}
}
}
int size = tempRoots.size();
if (this.rootPaths != null) {
for (Iterator iterator = this.rootPaths.iterator(); iterator.hasNext(); ) {
tempRoots.add(new Path((String) iterator.next()));
}
this.rootPaths.clear();
} else {
this.rootPaths = new ArrayList(size);
}
size = tempRoots.size();
if (size > 0) {
ArrayList sortedRoots = new ArrayList(tempRoots);
if (size > 1) {
Collections.sort(sortedRoots, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
IPath path1 = (IPath) o1;
IPath path2 = (IPath) o2;
return path1.segmentCount() - path2.segmentCount();
}
});
}
for (Iterator iter = sortedRoots.iterator(); iter.hasNext();) {
IPath path = (IPath) iter.next();
this.rootPaths.add(path.toString());
}
}
this.areRootPathsComputed = true;
if (VERBOSE) {
System.out.println("Spent " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println("Found " + size + " root paths"); //$NON-NLS-1$ //$NON-NLS-2$
int i = 0;
for (Iterator iterator = this.rootPaths.iterator(); iterator.hasNext();) {
System.out.println("root[" + i + "]=" + ((String) iterator.next()));//$NON-NLS-1$ //$NON-NLS-2$
i++;
}
}
}
private void computeRootPath(IContainer container, HashSet firstLevelPackageNames, boolean hasDefaultPackage, Set set, int sourcePathSegmentCount) {
try {
IResource[] resources = container.members();
for (int i = 0, max = resources.length; i < max; i++) {
IResource resource = resources[i];
if (resource.getType() == IResource.FOLDER) {
if (firstLevelPackageNames.contains(resource.getName())) {
IPath fullPath = container.getFullPath();
IPath rootPathEntry = fullPath.removeFirstSegments(sourcePathSegmentCount).setDevice(null);
if (rootPathEntry.segmentCount() >= 1) {
set.add(rootPathEntry);
}
computeRootPath((IFolder) resource, firstLevelPackageNames, hasDefaultPackage, set, sourcePathSegmentCount);
} else {
computeRootPath((IFolder) resource, firstLevelPackageNames, hasDefaultPackage, set, sourcePathSegmentCount);
}
}
if (i == max - 1 && hasDefaultPackage) {
// check if one member is a .java file
boolean hasJavaSourceFile = false;
for (int j = 0; j < max; j++) {
if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resources[i].getName())) {
hasJavaSourceFile = true;
break;
}
}
if (hasJavaSourceFile) {
IPath fullPath = container.getFullPath();
IPath rootPathEntry = fullPath.removeFirstSegments(sourcePathSegmentCount).setDevice(null);
set.add(rootPathEntry);
}
}
}
} catch (CoreException e) {
// ignore
e.printStackTrace();
}
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterType(TypeInfo typeInfo) {
this.typeDepth++;
if (this.typeDepth == this.types.length) { // need to grow
System.arraycopy(
this.types,
0,
this.types = new IType[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.typeNameRanges,
0,
this.typeNameRanges = new SourceRange[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.typeDeclarationStarts,
0,
this.typeDeclarationStarts = new int[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.memberName,
0,
this.memberName = new String[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.memberDeclarationStart,
0,
this.memberDeclarationStart = new int[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.memberNameRange,
0,
this.memberNameRange = new SourceRange[this.typeDepth * 2],
0,
this.typeDepth);
System.arraycopy(
this.methodParameterTypes,
0,
this.methodParameterTypes = new char[this.typeDepth * 2][][],
0,
this.typeDepth);
System.arraycopy(
this.methodParameterNames,
0,
this.methodParameterNames = new char[this.typeDepth * 2][][],
0,
this.typeDepth);
System.arraycopy(
this.typeModifiers,
0,
this.typeModifiers = new int[this.typeDepth * 2],
0,
this.typeDepth);
}
if (typeInfo.name.length == 0) {
this.anonymousCounter++;
if (this.anonymousCounter == this.anonymousClassName) {
this.types[this.typeDepth] = getType(this.binaryTypeOrModule.getElementName());
} else {
this.types[this.typeDepth] = getType(new String(typeInfo.name));
}
} else {
this.types[this.typeDepth] = getType(new String(typeInfo.name));
}
this.typeNameRanges[this.typeDepth] =
new SourceRange(typeInfo.nameSourceStart, typeInfo.nameSourceEnd - typeInfo.nameSourceStart + 1);
this.typeDeclarationStarts[this.typeDepth] = typeInfo.declarationStart;
IType currentType = this.types[this.typeDepth];
// type parameters
if (typeInfo.typeParameters != null) {
for (int i = 0, length = typeInfo.typeParameters.length; i < length; i++) {
TypeParameterInfo typeParameterInfo = typeInfo.typeParameters[i];
ITypeParameter typeParameter = currentType.getTypeParameter(new String(typeParameterInfo.name));
setSourceRange(
typeParameter,
new SourceRange(
typeParameterInfo.declarationStart,
typeParameterInfo.declarationEnd - typeParameterInfo.declarationStart + 1),
new SourceRange(
typeParameterInfo.nameSourceStart,
typeParameterInfo.nameSourceEnd - typeParameterInfo.nameSourceStart + 1));
}
}
// type modifiers
this.typeModifiers[this.typeDepth] = typeInfo.modifiers;
// categories
addCategories(currentType, typeInfo.categories);
}
@Override
public void enterModule(ModuleInfo moduleInfo) {
this.moduleNameRange =
new SourceRange(moduleInfo.nameSourceStart, moduleInfo.nameSourceEnd - moduleInfo.nameSourceStart + 1);
this.moduleDeclarationStart = moduleInfo.declarationStart;
// module type modifiers
this.moduleModifiers = moduleInfo.modifiers;
if (this.binaryTypeOrModule instanceof IModuleDescription) {
// categories
addCategories(this.binaryTypeOrModule, moduleInfo.categories);
}
}
@Override
public void exitModule(int declarationEnd) {
setSourceRange(
this.binaryTypeOrModule,
new SourceRange(
this.moduleDeclarationStart,
declarationEnd - this.moduleDeclarationStart + 1),
this.moduleNameRange);
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterCompilationUnit() {
// do nothing
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterConstructor(MethodInfo methodInfo) {
enterAbstractMethod(methodInfo);
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterField(FieldInfo fieldInfo) {
if (this.typeDepth >= 0) {
this.memberDeclarationStart[this.typeDepth] = fieldInfo.declarationStart;
this.memberNameRange[this.typeDepth] =
new SourceRange(fieldInfo.nameSourceStart, fieldInfo.nameSourceEnd - fieldInfo.nameSourceStart + 1);
String fieldName = new String(fieldInfo.name);
this.memberName[this.typeDepth] = fieldName;
// categories
IType currentType = this.types[this.typeDepth];
IField field = currentType.getField(fieldName);
addCategories(field, fieldInfo.categories);
}
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterInitializer(
int declarationSourceStart,
int modifiers) {
//do nothing
}
/**
* @see ISourceElementRequestor
*/
@Override
public void enterMethod(MethodInfo methodInfo) {
enterAbstractMethod(methodInfo);
}
private void enterAbstractMethod(MethodInfo methodInfo) {
if (this.typeDepth >= 0) {
this.memberName[this.typeDepth] = new String(methodInfo.name);
this.memberNameRange[this.typeDepth] =
new SourceRange(methodInfo.nameSourceStart, methodInfo.nameSourceEnd - methodInfo.nameSourceStart + 1);
this.memberDeclarationStart[this.typeDepth] = methodInfo.declarationStart;
IType currentType = this.types[this.typeDepth];
int currenTypeModifiers = this.typeModifiers[this.typeDepth];
char[][] parameterTypes = methodInfo.parameterTypes;
if (methodInfo.isConstructor && currentType.getDeclaringType() != null && !Flags.isStatic(currenTypeModifiers)) {
IType declaringType = currentType.getDeclaringType();
String declaringTypeName = declaringType.getElementName();
if (declaringTypeName.length() == 0) {
IOrdinaryClassFile classFile = declaringType.getClassFile();
int length = parameterTypes != null ? parameterTypes.length : 0;
char[][] newParameterTypes = new char[length+1][];
declaringTypeName = classFile.getElementName();
declaringTypeName = declaringTypeName.substring(0, declaringTypeName.indexOf('.'));
newParameterTypes[0] = declaringTypeName.toCharArray();
if (length != 0) {
System.arraycopy(parameterTypes, 0, newParameterTypes, 1, length);
}
this.methodParameterTypes[this.typeDepth] = newParameterTypes;
} else {
int length = parameterTypes != null ? parameterTypes.length : 0;
char[][] newParameterTypes = new char[length+1][];
newParameterTypes[0] = declaringTypeName.toCharArray();
if (length != 0) {
System.arraycopy(parameterTypes, 0, newParameterTypes, 1, length);
}
this.methodParameterTypes[this.typeDepth] = newParameterTypes;
}
} else {
this.methodParameterTypes[this.typeDepth] = parameterTypes;
}
this.methodParameterNames[this.typeDepth] = methodInfo.parameterNames;
IMethod method = currentType.getMethod(
this.memberName[this.typeDepth],
convertTypeNamesToSigs(this.methodParameterTypes[this.typeDepth]));
// type parameters
if (methodInfo.typeParameters != null) {
for (int i = 0, length = methodInfo.typeParameters.length; i < length; i++) {
TypeParameterInfo typeParameterInfo = methodInfo.typeParameters[i];
ITypeParameter typeParameter = method.getTypeParameter(new String(typeParameterInfo.name));
setSourceRange(
typeParameter,
new SourceRange(
typeParameterInfo.declarationStart,
typeParameterInfo.declarationEnd - typeParameterInfo.declarationStart + 1),
new SourceRange(
typeParameterInfo.nameSourceStart,
typeParameterInfo.nameSourceEnd - typeParameterInfo.nameSourceStart + 1));
}
}
// parameters infos
if (methodInfo.parameterInfos != null) {
for (int i = 0, length = methodInfo.parameterInfos.length; i < length; i++) {
ParameterInfo parameterInfo = methodInfo.parameterInfos[i];
LocalVariableElementKey key = new LocalVariableElementKey(method, new String(parameterInfo.name));
SourceRange[] allRanges = new SourceRange[] {
new SourceRange(
parameterInfo.declarationStart,
parameterInfo.declarationEnd - parameterInfo.declarationStart + 1),
new SourceRange(
parameterInfo.nameSourceStart,
parameterInfo.nameSourceEnd - parameterInfo.nameSourceStart + 1)
};
this.parametersRanges.put(
key,
allRanges);
if (parameterInfo.modifiers != 0) {
if (this.finalParameters == null) {
this.finalParameters = new HashSet();
}
this.finalParameters.add(key);
}
}
}
// categories
addCategories(method, methodInfo.categories);
}
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitType(int declarationEnd) {
if (this.typeDepth >= 0) {
IType currentType = this.types[this.typeDepth];
setSourceRange(
currentType,
new SourceRange(
this.typeDeclarationStarts[this.typeDepth],
declarationEnd - this.typeDeclarationStarts[this.typeDepth] + 1),
this.typeNameRanges[this.typeDepth]);
this.typeDepth--;
}
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitCompilationUnit(int declarationEnd) {
//do nothing
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitConstructor(int declarationEnd) {
exitAbstractMethod(declarationEnd);
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitField(int initializationStart, int declarationEnd, int declarationSourceEnd) {
if (this.typeDepth >= 0) {
IType currentType = this.types[this.typeDepth];
setSourceRange(
currentType.getField(this.memberName[this.typeDepth]),
new SourceRange(
this.memberDeclarationStart[this.typeDepth],
declarationEnd - this.memberDeclarationStart[this.typeDepth] + 1),
this.memberNameRange[this.typeDepth]);
}
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitInitializer(int declarationEnd) {
// implements abstract method
}
/**
* @see ISourceElementRequestor
*/
@Override
public void exitMethod(int declarationEnd, Expression defaultValue) {
exitAbstractMethod(declarationEnd);
}
private void exitAbstractMethod(int declarationEnd) {
if (this.typeDepth >= 0) {
IType currentType = this.types[this.typeDepth];
SourceRange sourceRange =
new SourceRange(
this.memberDeclarationStart[this.typeDepth],
declarationEnd - this.memberDeclarationStart[this.typeDepth] + 1);
IMethod method = currentType.getMethod(
this.memberName[this.typeDepth],
convertTypeNamesToSigs(this.methodParameterTypes[this.typeDepth]));
setSourceRange(
method,
sourceRange,
this.memberNameRange[this.typeDepth]);
setMethodParameterNames(
method,
this.methodParameterNames[this.typeDepth]);
}
}
/**
* Locates and returns source code for the given (binary) type, in this
* SourceMapper's ZIP file, or returns <code>null</code> if source
* code cannot be found.
*/
public char[] findSource(IType type, IBinaryType info) {
if (!type.isBinary()) {
return null;
}
String simpleSourceFileName = ((BinaryType) type).getSourceFileName(info);
if (simpleSourceFileName == null) {
return null;
}
return findSource(type, simpleSourceFileName);
}
/**
* Locates and returns source code for the given (binary) type, in this
* SourceMapper's ZIP file, or returns <code>null</code> if source
* code cannot be found.
* The given simpleSourceFileName is the .java file name (without the enclosing
* folder) used to create the given type (e.g. "A.java" for x/y/A$Inner.class)
*/
public char[] findSource(IType type, String simpleSourceFileName) {
PackageFragment pkgFrag = (PackageFragment) type.getPackageFragment();
String name = org.eclipse.jdt.internal.core.util.Util.concatWith(pkgFrag.names, simpleSourceFileName, '/');
return internalFindSource((NamedMember) type, name);
}
/**
* Locates and returns source code for the given (binary) module, in this
* SourceMapper's ZIP file, or returns <code>null</code> if source
* code cannot be found.
*/
public char[] findSource(IModuleDescription module) {
if (!module.isBinary()) {
return null;
}
return internalFindSource((NamedMember) module, TypeConstants.MODULE_INFO_FILE_NAME_STRING);
}
private char[] internalFindSource(NamedMember typeOrModule, String name) {
long time = 0;
if (VERBOSE) {
time = System.currentTimeMillis();
}
char[] source = null;
JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
try {
javaModelManager.cacheZipFiles(this); // Cache any zip files we open during this operation
if (this.rootPath != null) {
source = getSourceForRootPath(this.rootPath, name);
if (source == null) {
source = getSourceForRootPath("", name); //$NON-NLS-1$
}
}
if (source == null) { // proceed with automatic root path detection ...
// ... but not for multi-module roots
if (!(typeOrModule.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT) instanceof JrtPackageFragmentRoot)) {
computeAllRootPaths(typeOrModule);
if (this.rootPaths != null) {
loop: for (Iterator iterator = this.rootPaths.iterator(); iterator.hasNext(); ) {
String currentRootPath = (String) iterator.next();
if (!currentRootPath.equals(this.rootPath)) {
source = getSourceForRootPath(currentRootPath, name);
if (source != null) {
// remember right root path
this.rootPath = currentRootPath;
break loop;
}
}
}
}
}
}
} finally {
javaModelManager.flushZipFiles(this); // clean up cached zip files.
}
if (VERBOSE) {
System.out.println("spent " + (System.currentTimeMillis() - time) + "ms for " + typeOrModule.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
}
return source;
}
private char[] getSourceForRootPath(String currentRootPath, String name) {
String newFullName;
if (!currentRootPath.equals(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH)) {
if (currentRootPath.endsWith("/")) { //$NON-NLS-1$
newFullName = currentRootPath + name;
} else {
newFullName = currentRootPath + '/' + name;
}
} else {
newFullName = name;
}
return this.findSource(newFullName);
}
public char[] findSource(String fullName) {
char[] source = null;
Object target = JavaModel.getTarget(this.sourcePath, true);
String charSet = null;
if (target instanceof IContainer) {
IResource res = ((IContainer)target).findMember(fullName);
if (res instanceof IFile) {
try {
// Order of preference: charSet supplied, this.encoding or this.defaultEncoding in that order
try {
// Use the implicit encoding only when the source attachment's encoding hasn't been explicitly set.
charSet = ((IFile) res).getCharset(this.encoding == null);
} catch (CoreException e) {
// Ignore
}
source = org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray((IFile) res,
charSet == null ? (this.encoding == null ? this.defaultEncoding : this.encoding) : charSet);
} catch (JavaModelException e) {
// Ignore
}
}
} else {
try {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=303511
// For a resource inside the workspace, use the encoding set on the resource
if (target instanceof IFile)
charSet = ((IFile)target).getCharset(this.encoding == null);
} catch (CoreException e) {
// Ignore
}
// try to get the entry
ZipEntry entry = null;
ZipFile zip = null;
JavaModelManager manager = JavaModelManager.getJavaModelManager();
try {
zip = manager.getZipFile(this.sourcePath);
entry = zip.getEntry(fullName);
if (entry != null) {
// now read the source code
source = readSource(entry, zip, charSet);
}
} catch (CoreException e) {
return null;
} finally {
manager.closeZipFile(zip); // handle null case
}
}
return source;
}
public int getFlags(IJavaElement element) {
switch(element.getElementType()) {
case IJavaElement.LOCAL_VARIABLE :
LocalVariableElementKey key = new LocalVariableElementKey(element.getParent(), element.getElementName());
if (this.finalParameters != null && this.finalParameters.contains(key)) {
return Flags.AccFinal;
}
}
return 0;
}
/**
* Returns the SourceRange for the name of the given element, or
* {-1, -1} if no source range is known for the name of the element.
*/
public SourceRange getNameRange(IJavaElement element) {
switch(element.getElementType()) {
case IJavaElement.METHOD :
if (((IMember) element).isBinary()) {
IJavaElement[] el = getUnqualifiedMethodHandle((IMethod) element, false);
if(el[1] != null && this.sourceRanges.get(el[0]) == null) {
element = getUnqualifiedMethodHandle((IMethod) element, true)[0];
} else {
element = el[0];
}
}
break;
case IJavaElement.TYPE_PARAMETER :
IJavaElement parent = element.getParent();
if (parent.getElementType() == IJavaElement.METHOD) {
IMethod method = (IMethod) parent;
if (method.isBinary()) {
IJavaElement[] el = getUnqualifiedMethodHandle(method, false);
if(el[1] != null && this.sourceRanges.get(el[0]) == null) {
method = (IMethod) getUnqualifiedMethodHandle(method, true)[0];
} else {
method = (IMethod) el[0];
}
element = method.getTypeParameter(element.getElementName());
}
}
break;
case IJavaElement.LOCAL_VARIABLE :
LocalVariableElementKey key = new LocalVariableElementKey(element.getParent(), element.getElementName());
SourceRange[] ranges = (SourceRange[]) this.parametersRanges.get(key);
if (ranges == null) {
return UNKNOWN_RANGE;
} else {
return ranges[1];
}
}
SourceRange[] ranges = (SourceRange[]) this.sourceRanges.get(element);
if (ranges == null) {
return UNKNOWN_RANGE;
} else {
return ranges[1];
}
}
/**
* Returns parameters names for the given method, or
* null if no parameter names are known for the method.
*/
public char[][] getMethodParameterNames(IMethod method) {
if (method.isBinary()) {
IJavaElement[] el = getUnqualifiedMethodHandle(method, false);
if(el[1] != null && this.parameterNames.get(el[0]) == null) {
method = (IMethod) getUnqualifiedMethodHandle(method, true)[0];
} else {
method = (IMethod) el[0];
}
}
char[][] parameters = (char[][]) this.parameterNames.get(method);
if (parameters == null) {
return null;
} else {
return parameters;
}
}
/**
* Returns the <code>SourceRange</code> for the given element, or
* {-1, -1} if no source range is known for the element.
*/
public SourceRange getSourceRange(IJavaElement element) {
switch(element.getElementType()) {
case IJavaElement.METHOD :
if (((IMember) element).isBinary()) {
IJavaElement[] el = getUnqualifiedMethodHandle((IMethod) element, false);
if(el[1] != null && this.sourceRanges.get(el[0]) == null) {
element = getUnqualifiedMethodHandle((IMethod) element, true)[0];
} else {
element = el[0];
}
}
break;
case IJavaElement.TYPE_PARAMETER :
IJavaElement parent = element.getParent();
if (parent.getElementType() == IJavaElement.METHOD) {
IMethod method = (IMethod) parent;
if (method.isBinary()) {
IJavaElement[] el = getUnqualifiedMethodHandle(method, false);
if(el[1] != null && this.sourceRanges.get(el[0]) == null) {
method = (IMethod) getUnqualifiedMethodHandle(method, true)[0];
} else {
method = (IMethod) el[0];
}
element = method.getTypeParameter(element.getElementName());
}
}
break;
case IJavaElement.LOCAL_VARIABLE :
LocalVariableElementKey key = new LocalVariableElementKey(element.getParent(), element.getElementName());
SourceRange[] ranges = (SourceRange[]) this.parametersRanges.get(key);
if (ranges == null) {
return UNKNOWN_RANGE;
} else {
return ranges[0];
}
}
SourceRange[] ranges = (SourceRange[]) this.sourceRanges.get(element);
if (ranges == null) {
return UNKNOWN_RANGE;
} else {
return ranges[0];
}
}
/**
* Returns the type with the given <code>typeName</code>. Returns inner classes
* as well.
*/
protected IType getType(String typeName) {
if (!(this.binaryTypeOrModule instanceof IType))
return null;
IType type = (IType) this.binaryTypeOrModule;
if (typeName.length() == 0) {
IJavaElement classFile = type.getParent();
String classFileName = classFile.getElementName();
StringBuffer newClassFileName = new StringBuffer();
int lastDollar = classFileName.lastIndexOf('$');
for (int i = 0; i <= lastDollar; i++)
newClassFileName.append(classFileName.charAt(i));
newClassFileName.append(Integer.toString(this.anonymousCounter));
PackageFragment pkg = (PackageFragment) classFile.getParent();
return new BinaryType(new ClassFile(pkg, newClassFileName.toString()), typeName);
} else if (type.getElementName().equals(typeName))
return type;
else
return ((this.typeDepth <= 1) ? type : this.types[this.typeDepth - 1]).getType(typeName);
}
/**
* Creates a handle that has parameter types that are not
* fully qualified so that the correct source is found.
*/
protected IJavaElement[] getUnqualifiedMethodHandle(IMethod method, boolean noDollar) {
boolean hasDollar = false;
String[] qualifiedParameterTypes = method.getParameterTypes();
String[] unqualifiedParameterTypes = new String[qualifiedParameterTypes.length];
for (int i = 0; i < qualifiedParameterTypes.length; i++) {
StringBuffer unqualifiedTypeSig = new StringBuffer();
getUnqualifiedTypeSignature(qualifiedParameterTypes[i], 0/*start*/, qualifiedParameterTypes[i].length(), unqualifiedTypeSig, noDollar);
unqualifiedParameterTypes[i] = unqualifiedTypeSig.toString();
hasDollar |= unqualifiedParameterTypes[i].lastIndexOf('$') != -1;
}
IJavaElement[] result = new IJavaElement[2];
result[0] = ((IType) method.getParent()).getMethod(
method.getElementName(),
unqualifiedParameterTypes);
if(hasDollar) {
result[1] = result[0];
}
return result;
}
private int getUnqualifiedTypeSignature(String qualifiedTypeSig, int start, int length, StringBuffer unqualifiedTypeSig, boolean noDollar) {
char firstChar = qualifiedTypeSig.charAt(start);
int end = start + 1;
boolean sigStart = false;
firstPass: for (int i = start; i < length; i++) {
char current = qualifiedTypeSig.charAt(i);
switch (current) {
case Signature.C_ARRAY :
case Signature.C_SUPER:
case Signature.C_EXTENDS:
unqualifiedTypeSig.append(current);
start = i + 1;
end = start + 1;
firstChar = qualifiedTypeSig.charAt(start);
break;
case Signature.C_RESOLVED :
case Signature.C_UNRESOLVED :
case Signature.C_TYPE_VARIABLE :
if (!sigStart) {
start = ++i;
sigStart = true;
}
break;
case Signature.C_NAME_END:
case Signature.C_GENERIC_START :
end = i;
break firstPass;
case Signature.C_STAR :
unqualifiedTypeSig.append(current);
start = i + 1;
end = start + 1;
firstChar = qualifiedTypeSig.charAt(start);
break;
case Signature.C_GENERIC_END :
return i;
case Signature.C_DOT:
start = ++i;
break;
case Signature.C_BOOLEAN :
case Signature.C_BYTE :
case Signature.C_CHAR :
case Signature.C_DOUBLE :
case Signature.C_FLOAT :
case Signature.C_INT :
case Signature.C_LONG :
case Signature.C_SHORT :
if (!sigStart) {
unqualifiedTypeSig.append(current);
return i+1;
}
}
}
switch (firstChar) {
case Signature.C_RESOLVED :
case Signature.C_UNRESOLVED :
case Signature.C_TYPE_VARIABLE :
unqualifiedTypeSig.append(Signature.C_UNRESOLVED);
if (noDollar) {
int lastDollar = qualifiedTypeSig.lastIndexOf('$', end);
if (lastDollar > start)
start = lastDollar + 1;
}
for (int i = start; i < length; i++) {
char current = qualifiedTypeSig.charAt(i);
switch (current) {
case Signature.C_GENERIC_START:
unqualifiedTypeSig.append(current);
i++;
do {
i = getUnqualifiedTypeSignature(qualifiedTypeSig, i, length, unqualifiedTypeSig, noDollar);
} while (qualifiedTypeSig.charAt(i) != Signature.C_GENERIC_END);
unqualifiedTypeSig.append(Signature.C_GENERIC_END);
break;
case Signature.C_NAME_END:
unqualifiedTypeSig.append(current);
return i + 1;
default:
unqualifiedTypeSig.append(current);
break;
}
}
return length;
default :
// primitive type or wildcard
unqualifiedTypeSig.append(qualifiedTypeSig.substring(start, end));
return end;
}
}
/**
* Maps the given source code to the given binary type or module and its children.
*/
public void mapSource(NamedMember typeOrModule, char[] contents, IBinaryType info) {
this.mapSource(typeOrModule, contents, info, null);
}
/**
* Maps the given source code to the given binary type and its children.
* If a non-null java element is passed, finds the name range for the
* given java element without storing it.
*/
public synchronized ISourceRange mapSource(
NamedMember typeOrModule,
char[] contents,
IBinaryType info, // null for modules
IJavaElement elementToFind) {
this.binaryTypeOrModule = typeOrModule;
// check whether it is already mapped
if (this.sourceRanges.get(typeOrModule) != null) return (elementToFind != null) ? getNameRange(elementToFind) : null;
this.importsTable.remove(this.binaryTypeOrModule);
this.importsCounterTable.remove(this.binaryTypeOrModule);
this.searchedElement = elementToFind;
this.types = new IType[1];
this.typeDeclarationStarts = new int[1];
this.typeNameRanges = new SourceRange[1];
this.typeModifiers = new int[1];
this.typeDepth = -1;
this.memberDeclarationStart = new int[1];
this.memberName = new String[1];
this.memberNameRange = new SourceRange[1];
this.methodParameterTypes = new char[1][][];
this.methodParameterNames = new char[1][][];
this.anonymousCounter = 0;
HashMap oldSourceRanges = null;
if (elementToFind != null) {
oldSourceRanges = (HashMap) this.sourceRanges.clone();
}
try {
IProblemFactory factory = new DefaultProblemFactory();
SourceElementParser parser = null;
boolean doFullParse = false;
this.anonymousClassName = 0;
String sourceFileName;
if (this.binaryTypeOrModule instanceof BinaryType) {
if (info == null) {
try {
info = (IBinaryType) this.binaryTypeOrModule.getElementInfo();
} catch(JavaModelException e) {
return null;
}
}
sourceFileName = ((BinaryType) this.binaryTypeOrModule).sourceFileName(info);
boolean isAnonymousClass = info.isAnonymous();
char[] fullName = info.getName();
if (isAnonymousClass) {
String eltName = this.binaryTypeOrModule.getParent().getElementName();
eltName = eltName.substring(eltName.lastIndexOf('$') + 1, eltName.length());
try {
this.anonymousClassName = Integer.parseInt(eltName);
} catch(NumberFormatException e) {
// ignore
}
}
doFullParse = hasToRetrieveSourceRangesForLocalClass(fullName);
} else {
sourceFileName = TypeConstants.MODULE_INFO_CLASS_NAME_STRING;
}
parser = new SourceElementParser(this, factory, new CompilerOptions(this.options), doFullParse, true/*optimize string literals*/);
parser.javadocParser.checkDocComment = false; // disable javadoc parsing
IJavaElement javaElement = this.binaryTypeOrModule.getCompilationUnit();
if (javaElement == null) javaElement = this.binaryTypeOrModule.getParent();
parser.parseCompilationUnit(
new BasicCompilationUnit(contents, null, sourceFileName, javaElement),
doFullParse,
null/*no progress*/);
if (elementToFind != null) {
ISourceRange range = getNameRange(elementToFind);
return range;
} else {
return null;
}
} finally {
if (elementToFind != null) {
this.sourceRanges = oldSourceRanges;
}
this.binaryTypeOrModule = null;
this.searchedElement = null;
this.types = null;
this.typeDeclarationStarts = null;
this.typeNameRanges = null;
this.typeDepth = -1;
}
}
private char[] readSource(ZipEntry entry, ZipFile zip, String charSet) {
try {
byte[] bytes = Util.getZipEntryByteContent(entry, zip);
if (bytes != null) {
// Order of preference: charSet supplied, this.encoding or this.defaultEncoding in that order
return Util.bytesToChar(bytes, charSet == null ? (this.encoding == null ? this.defaultEncoding : this.encoding) : charSet);
}
} catch (IOException e) {
// ignore
}
return null;
}
/**
* Sets the mapping for this method to its parameter names.
*
* @see #parameterNames
*/
protected void setMethodParameterNames(
IMethod method,
char[][] parameterNames) {
if (parameterNames == null) {
parameterNames = CharOperation.NO_CHAR_CHAR;
}
this.parameterNames.put(method, parameterNames);
}
/**
* Sets the mapping for this element to its source ranges for its source range
* and name range.
*
* @see #sourceRanges
*/
protected void setSourceRange(
IJavaElement element,
SourceRange sourceRange,
SourceRange nameRange) {
this.sourceRanges.put(element, new SourceRange[] { sourceRange, nameRange });
}
/**
* Return a char[][] array containing the imports of the attached source for the binary type
*/
public char[][] getImports(Member typeOrModule) {
char[][] imports = (char[][]) this.importsTable.get(typeOrModule);
if (imports != null) {
int importsCounter = ((Integer) this.importsCounterTable.get(typeOrModule)).intValue();
if (imports.length != importsCounter) {
System.arraycopy(
imports,
0,
(imports = new char[importsCounter][]),
0,
importsCounter);
}
this.importsTable.put(typeOrModule, imports);
}
return imports;
}
private boolean hasToRetrieveSourceRangesForLocalClass(char[] eltName) {
/*
* A$1$B$2 : true
* A$B$B$2 : true
* A$C$B$D : false
* A$F$B$D$1$F : true
* A$F$B$D$1F : true
* A$1 : true
* A$B : false
*/
if (eltName == null) return false;
int length = eltName.length;
int dollarIndex = CharOperation.indexOf('$', eltName, 0);
while (dollarIndex != -1) {
int nameStart = dollarIndex+1;
if (nameStart == length) return false;
if (Character.isDigit(eltName[nameStart]))
return true;
dollarIndex = CharOperation.indexOf('$', eltName, nameStart);
}
return false;
}
// {OTDTUI: added default implementation to corresponding extension in ISourceElementRequestor
@Override
public void enterCalloutMapping(CalloutInfo calloutInfo)
{
// TODO(jwl): Not implemented yet
}
@Override
public void enterCalloutToFieldMapping(CalloutToFieldInfo calloutInfo)
{
// TODO Auto-generated method stub
}
@Override
public void enterCallinMapping(CallinInfo callinInfo)
{
// TODO(jwl): Not implemented yet
}
@Override
public void exitCallinMapping(int sourceEnd, int declarationSourceEnd)
{
// TODO(jwl): Not implemented yet
}
@Override
public void exitCalloutMapping(int sourceEnd, int declarationSourceEnd)
{
// TODO(jwl): Not implemented yet
}
@Override
public void exitCalloutToFieldMapping(int sourceEnd, int declarationSourceEnd)
{
// TODO Auto-generated method stub
}
//(ak)}
// {ObjectTeams: default implementation, does nothing
@Override
public void acceptBaseReference(char[][] typeName, int sourceStart, int sourceEnd){
// Do Nothing
}
// haebor}
}