0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.core;
0011:
0012: import java.io.File;
0013: import java.io.FilenameFilter;
0014: import java.io.IOException;
0015: import java.util.ArrayList;
0016: import java.util.Collections;
0017: import java.util.Comparator;
0018: import java.util.Enumeration;
0019: import java.util.HashMap;
0020: import java.util.HashSet;
0021: import java.util.Iterator;
0022: import java.util.Map;
0023: import java.util.Set;
0024: import java.util.zip.ZipEntry;
0025: import java.util.zip.ZipFile;
0026:
0027: import org.eclipse.core.resources.IContainer;
0028: import org.eclipse.core.resources.IFile;
0029: import org.eclipse.core.resources.IFolder;
0030: import org.eclipse.core.resources.IResource;
0031: import org.eclipse.core.resources.ResourcesPlugin;
0032: import org.eclipse.core.runtime.CoreException;
0033: import org.eclipse.core.runtime.IPath;
0034: import org.eclipse.core.runtime.IStatus;
0035: import org.eclipse.core.runtime.Path;
0036: import org.eclipse.jdt.core.Flags;
0037: import org.eclipse.jdt.core.IClassFile;
0038: import org.eclipse.jdt.core.IField;
0039: import org.eclipse.jdt.core.IJavaElement;
0040: import org.eclipse.jdt.core.IJavaProject;
0041: import org.eclipse.jdt.core.IMember;
0042: import org.eclipse.jdt.core.IMethod;
0043: import org.eclipse.jdt.core.IPackageFragmentRoot;
0044: import org.eclipse.jdt.core.ISourceRange;
0045: import org.eclipse.jdt.core.IType;
0046: import org.eclipse.jdt.core.ITypeParameter;
0047: import org.eclipse.jdt.core.JavaConventions;
0048: import org.eclipse.jdt.core.JavaCore;
0049: import org.eclipse.jdt.core.JavaModelException;
0050: import org.eclipse.jdt.core.Signature;
0051: import org.eclipse.jdt.core.compiler.CategorizedProblem;
0052: import org.eclipse.jdt.core.compiler.CharOperation;
0053: import org.eclipse.jdt.internal.compiler.IProblemFactory;
0054: import org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
0055: import org.eclipse.jdt.internal.compiler.SourceElementParser;
0056: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
0057: import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
0058: import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
0059: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
0060: import org.eclipse.jdt.internal.compiler.util.Util;
0061: import org.eclipse.jdt.internal.core.util.ReferenceInfoAdapter;
0062:
0063: /**
0064: * A SourceMapper maps source code in a ZIP file to binary types in
0065: * a JAR. The SourceMapper uses the fuzzy parser to identify source
0066: * fragments in a .java file, and attempts to match the source code
0067: * with children in a binary type. A SourceMapper is associated
0068: * with a JarPackageFragment by an AttachSourceOperation.
0069: *
0070: * @see org.eclipse.jdt.internal.core.JarPackageFragment
0071: */
0072: public class SourceMapper extends ReferenceInfoAdapter implements
0073: ISourceElementRequestor, SuffixConstants {
0074:
0075: public static boolean VERBOSE = false;
0076: /**
0077: * Specifies the file name filter use to compute the root paths.
0078: */
0079: private static final FilenameFilter FILENAME_FILTER = new FilenameFilter() {
0080: public boolean accept(File dir, String name) {
0081: return org.eclipse.jdt.internal.core.util.Util
0082: .isJavaLikeFileName(name);
0083: }
0084: };
0085: /**
0086: * Specifies the location of the package fragment roots within
0087: * the zip (empty specifies the default root). <code>null</code> is
0088: * not a valid root path.
0089: */
0090: protected ArrayList rootPaths;
0091:
0092: /**
0093: * The binary type source is being mapped for
0094: */
0095: protected BinaryType binaryType;
0096:
0097: /**
0098: * The location of the zip file containing source.
0099: */
0100: protected IPath sourcePath;
0101: /**
0102: * Specifies the location of the package fragment root within
0103: * the zip (empty specifies the default root). <code>null</code> is
0104: * not a valid root path.
0105: */
0106: protected String rootPath = ""; //$NON-NLS-1$
0107:
0108: /**
0109: * Table that maps a binary method to its parameter names.
0110: * Keys are the method handles, entries are <code>char[][]</code>.
0111: */
0112: protected HashMap parameterNames;
0113:
0114: /**
0115: * Table that maps a binary element to its <code>SourceRange</code>s.
0116: * Keys are the element handles, entries are <code>SourceRange[]</code> which
0117: * is a two element array; the first being source range, the second
0118: * being name range.
0119: */
0120: protected HashMap sourceRanges;
0121:
0122: /*
0123: * A map from IJavaElement to String[]
0124: */
0125: protected HashMap categories;
0126:
0127: /**
0128: * The unknown source range {-1, 0}
0129: */
0130: public static final SourceRange UNKNOWN_RANGE = new SourceRange(-1,
0131: 0);
0132:
0133: /**
0134: * The position within the source of the start of the
0135: * current member element, or -1 if we are outside a member.
0136: */
0137: protected int[] memberDeclarationStart;
0138: /**
0139: * The <code>SourceRange</code> of the name of the current member element.
0140: */
0141: protected SourceRange[] memberNameRange;
0142: /**
0143: * The name of the current member element.
0144: */
0145: protected String[] memberName;
0146:
0147: /**
0148: * The parameter names for the current member method element.
0149: */
0150: protected char[][][] methodParameterNames;
0151:
0152: /**
0153: * The parameter types for the current member method element.
0154: */
0155: protected char[][][] methodParameterTypes;
0156:
0157: /**
0158: * The element searched for
0159: */
0160: protected IJavaElement searchedElement;
0161:
0162: /**
0163: * imports references
0164: */
0165: private HashMap importsTable;
0166: private HashMap importsCounterTable;
0167:
0168: /**
0169: * Enclosing type information
0170: */
0171: IType[] types;
0172: int[] typeDeclarationStarts;
0173: SourceRange[] typeNameRanges;
0174: int[] typeModifiers;
0175: int typeDepth;
0176:
0177: /**
0178: * Anonymous counter in case we want to map the source of an anonymous class.
0179: */
0180: int anonymousCounter;
0181: int anonymousClassName;
0182:
0183: /**
0184: *Options to be used
0185: */
0186: String encoding;
0187: Map options;
0188:
0189: /**
0190: * Use to handle root paths inference
0191: */
0192: private boolean areRootPathsComputed;
0193:
0194: public SourceMapper() {
0195: this .areRootPathsComputed = false;
0196: }
0197:
0198: /**
0199: * Creates a <code>SourceMapper</code> that locates source in the zip file
0200: * at the given location in the specified package fragment root.
0201: */
0202: public SourceMapper(IPath sourcePath, String rootPath, Map options) {
0203: this .areRootPathsComputed = false;
0204: this .options = options;
0205: try {
0206: this .encoding = ResourcesPlugin.getWorkspace().getRoot()
0207: .getDefaultCharset();
0208: } catch (CoreException e) {
0209: // use no encoding
0210: }
0211: if (rootPath != null) {
0212: this .rootPaths = new ArrayList();
0213: this .rootPaths.add(rootPath);
0214: }
0215: this .sourcePath = sourcePath;
0216: this .sourceRanges = new HashMap();
0217: this .parameterNames = new HashMap();
0218: this .importsTable = new HashMap();
0219: this .importsCounterTable = new HashMap();
0220: }
0221:
0222: /**
0223: * @see ISourceElementRequestor
0224: */
0225: public void acceptImport(int declarationStart, int declarationEnd,
0226: char[][] tokens, boolean onDemand, int modifiers) {
0227: char[][] imports = (char[][]) this .importsTable
0228: .get(this .binaryType);
0229: int importsCounter;
0230: if (imports == null) {
0231: imports = new char[5][];
0232: importsCounter = 0;
0233: } else {
0234: importsCounter = ((Integer) this .importsCounterTable
0235: .get(this .binaryType)).intValue();
0236: }
0237: if (imports.length == importsCounter) {
0238: System.arraycopy(imports, 0,
0239: (imports = new char[importsCounter * 2][]), 0,
0240: importsCounter);
0241: }
0242: char[] name = CharOperation.concatWith(tokens, '.');
0243: if (onDemand) {
0244: int nameLength = name.length;
0245: System.arraycopy(name, 0,
0246: (name = new char[nameLength + 2]), 0, nameLength);
0247: name[nameLength] = '.';
0248: name[nameLength + 1] = '*';
0249: }
0250: imports[importsCounter++] = name;
0251: this .importsTable.put(this .binaryType, imports);
0252: this .importsCounterTable.put(this .binaryType, new Integer(
0253: importsCounter));
0254: }
0255:
0256: /**
0257: * @see ISourceElementRequestor
0258: */
0259: public void acceptLineSeparatorPositions(int[] positions) {
0260: //do nothing
0261: }
0262:
0263: /**
0264: * @see ISourceElementRequestor
0265: */
0266: public void acceptPackage(int declarationStart, int declarationEnd,
0267: char[] name) {
0268: //do nothing
0269: }
0270:
0271: /**
0272: * @see ISourceElementRequestor
0273: */
0274: public void acceptProblem(CategorizedProblem problem) {
0275: //do nothing
0276: }
0277:
0278: private void addCategories(IJavaElement element,
0279: char[][] elementCategories) {
0280: if (elementCategories == null)
0281: return;
0282: if (this .categories == null)
0283: this .categories = new HashMap();
0284: this .categories.put(element, CharOperation
0285: .toStrings(elementCategories));
0286: }
0287:
0288: /**
0289: * Closes this <code>SourceMapper</code>'s zip file. Once this is done, this
0290: * <code>SourceMapper</code> cannot be used again.
0291: */
0292: public void close() {
0293: this .sourceRanges = null;
0294: this .parameterNames = null;
0295: }
0296:
0297: /**
0298: * Converts these type names to unqualified signatures. This needs to be done in order to be consistent
0299: * with the way the source range is retrieved.
0300: * @see SourceMapper#getUnqualifiedMethodHandle
0301: * @see Signature
0302: */
0303: private String[] convertTypeNamesToSigs(char[][] typeNames) {
0304: if (typeNames == null)
0305: return CharOperation.NO_STRINGS;
0306: int n = typeNames.length;
0307: if (n == 0)
0308: return CharOperation.NO_STRINGS;
0309: String[] typeSigs = new String[n];
0310: for (int i = 0; i < n; ++i) {
0311: char[] typeSig = Signature.createCharArrayTypeSignature(
0312: typeNames[i], false);
0313:
0314: // transforms signatures that contains a qualification into unqualified signatures
0315: // e.g. "QX<+QMap.Entry;>;" becomes "QX<+QEntry;>;"
0316: StringBuffer simpleTypeSig = null;
0317: int start = 0;
0318: int dot = -1;
0319: int length = typeSig.length;
0320: for (int j = 0; j < length; j++) {
0321: switch (typeSig[j]) {
0322: case Signature.C_UNRESOLVED:
0323: if (simpleTypeSig != null)
0324: simpleTypeSig.append(typeSig, start, j - start);
0325: start = j;
0326: break;
0327: case Signature.C_DOT:
0328: dot = j;
0329: break;
0330: case Signature.C_GENERIC_START:
0331: case Signature.C_NAME_END:
0332: if (dot > start) {
0333: if (simpleTypeSig == null)
0334: simpleTypeSig = new StringBuffer().append(
0335: typeSig, 0, start);
0336: simpleTypeSig.append(Signature.C_UNRESOLVED);
0337: simpleTypeSig.append(typeSig, dot + 1, j - dot
0338: - 1);
0339: start = j;
0340: }
0341: break;
0342: }
0343: }
0344: if (simpleTypeSig == null) {
0345: typeSigs[i] = new String(typeSig);
0346: } else {
0347: simpleTypeSig.append(typeSig, start, length - start);
0348: typeSigs[i] = simpleTypeSig.toString();
0349: }
0350: }
0351: return typeSigs;
0352: }
0353:
0354: private synchronized void computeAllRootPaths(IType type) {
0355: if (this .areRootPathsComputed) {
0356: return;
0357: }
0358: IPackageFragmentRoot root = (IPackageFragmentRoot) type
0359: .getPackageFragment().getParent();
0360: final HashSet tempRoots = new HashSet();
0361: long time = 0;
0362: if (VERBOSE) {
0363: System.out
0364: .println("compute all root paths for " + root.getElementName()); //$NON-NLS-1$
0365: time = System.currentTimeMillis();
0366: }
0367: final HashSet firstLevelPackageNames = new HashSet();
0368: boolean containsADefaultPackage = false;
0369:
0370: if (root.isArchive()) {
0371: JarPackageFragmentRoot jarPackageFragmentRoot = (JarPackageFragmentRoot) root;
0372: IJavaProject project = jarPackageFragmentRoot
0373: .getJavaProject();
0374: String sourceLevel = null;
0375: String complianceLevel = null;
0376: JavaModelManager manager = JavaModelManager
0377: .getJavaModelManager();
0378: ZipFile zip = null;
0379: try {
0380: zip = manager.getZipFile(jarPackageFragmentRoot
0381: .getPath());
0382: for (Enumeration entries = zip.entries(); entries
0383: .hasMoreElements();) {
0384: ZipEntry entry = (ZipEntry) entries.nextElement();
0385: String entryName = entry.getName();
0386: if (!entry.isDirectory()) {
0387: int index = entryName.indexOf('/');
0388: if (index != -1
0389: && Util.isClassFileName(entryName)) {
0390: String firstLevelPackageName = entryName
0391: .substring(0, index);
0392: if (!firstLevelPackageNames
0393: .contains(firstLevelPackageName)) {
0394: if (sourceLevel == null) {
0395: sourceLevel = project.getOption(
0396: JavaCore.COMPILER_SOURCE,
0397: true);
0398: complianceLevel = project
0399: .getOption(
0400: JavaCore.COMPILER_COMPLIANCE,
0401: true);
0402: }
0403: IStatus status = JavaConventions
0404: .validatePackageName(
0405: firstLevelPackageName,
0406: sourceLevel,
0407: complianceLevel);
0408: if (status.isOK()
0409: || status.getSeverity() == IStatus.WARNING) {
0410: firstLevelPackageNames
0411: .add(firstLevelPackageName);
0412: }
0413: }
0414: } else if (Util.isClassFileName(entryName)) {
0415: containsADefaultPackage = true;
0416: }
0417: }
0418: }
0419: } catch (CoreException e) {
0420: // ignore
0421: } finally {
0422: manager.closeZipFile(zip); // handle null case
0423: }
0424: } else {
0425: Object target = JavaModel.getTarget(ResourcesPlugin
0426: .getWorkspace().getRoot(), root.getPath(), true);
0427: if (target instanceof IResource) {
0428: IResource resource = (IResource) target;
0429: if (resource instanceof IContainer) {
0430: try {
0431: IResource[] members = ((IContainer) resource)
0432: .members();
0433: for (int i = 0, max = members.length; i < max; i++) {
0434: IResource member = members[i];
0435: if (member.getType() == IResource.FOLDER) {
0436: firstLevelPackageNames.add(member
0437: .getName());
0438: } else if (Util.isClassFileName(member
0439: .getName())) {
0440: containsADefaultPackage = true;
0441: }
0442: }
0443: } catch (CoreException e) {
0444: // ignore
0445: }
0446: }
0447: } else if (target instanceof File) {
0448: File file = (File) target;
0449: if (file.isDirectory()) {
0450: File[] files = file.listFiles();
0451: for (int i = 0, max = files.length; i < max; i++) {
0452: File currentFile = files[i];
0453: if (currentFile.isDirectory()) {
0454: firstLevelPackageNames.add(currentFile
0455: .getName());
0456: } else if (Util.isClassFileName(currentFile
0457: .getName())) {
0458: containsADefaultPackage = true;
0459: }
0460: }
0461: }
0462: }
0463: }
0464:
0465: if (Util.isArchiveFileName(this .sourcePath.lastSegment())) {
0466: JavaModelManager manager = JavaModelManager
0467: .getJavaModelManager();
0468: ZipFile zip = null;
0469: try {
0470: zip = manager.getZipFile(this .sourcePath);
0471: for (Enumeration entries = zip.entries(); entries
0472: .hasMoreElements();) {
0473: ZipEntry entry = (ZipEntry) entries.nextElement();
0474: String entryName;
0475: if (!entry.isDirectory()
0476: && org.eclipse.jdt.internal.core.util.Util
0477: .isJavaLikeFileName(entryName = entry
0478: .getName())) {
0479: IPath path = new Path(entryName);
0480: int segmentCount = path.segmentCount();
0481: if (segmentCount > 1) {
0482: for (int i = 0, max = path.segmentCount() - 1; i < max; i++) {
0483: if (firstLevelPackageNames
0484: .contains(path.segment(i))) {
0485: tempRoots.add(path.uptoSegment(i));
0486: // 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)
0487: }
0488: if (i == max - 1
0489: && containsADefaultPackage) {
0490: tempRoots
0491: .add(path.uptoSegment(max));
0492: }
0493: }
0494: } else if (containsADefaultPackage) {
0495: tempRoots.add(new Path("")); //$NON-NLS-1$
0496: }
0497: }
0498: }
0499: } catch (CoreException e) {
0500: // ignore
0501: } finally {
0502: manager.closeZipFile(zip); // handle null case
0503: }
0504: } else {
0505: Object target = JavaModel.getTarget(ResourcesPlugin
0506: .getWorkspace().getRoot(), this .sourcePath, true);
0507: if (target instanceof IResource) {
0508: if (target instanceof IContainer) {
0509: computeRootPath((IContainer) target,
0510: firstLevelPackageNames,
0511: containsADefaultPackage, tempRoots);
0512: }
0513: } else if (target instanceof File) {
0514: File file = (File) target;
0515: if (file.isDirectory()) {
0516: computeRootPath(file, firstLevelPackageNames,
0517: containsADefaultPackage, tempRoots);
0518: }
0519: }
0520: }
0521: int size = tempRoots.size();
0522: if (this .rootPaths != null) {
0523: for (Iterator iterator = this .rootPaths.iterator(); iterator
0524: .hasNext();) {
0525: tempRoots.add(new Path((String) iterator.next()));
0526: }
0527: this .rootPaths.clear();
0528: } else {
0529: this .rootPaths = new ArrayList(size);
0530: }
0531: size = tempRoots.size();
0532: if (size > 0) {
0533: ArrayList sortedRoots = new ArrayList(tempRoots);
0534: if (size > 1) {
0535: Collections.sort(sortedRoots, new Comparator() {
0536: public int compare(Object o1, Object o2) {
0537: IPath path1 = (IPath) o1;
0538: IPath path2 = (IPath) o2;
0539: return path1.segmentCount()
0540: - path2.segmentCount();
0541: }
0542: });
0543: }
0544: for (Iterator iter = sortedRoots.iterator(); iter.hasNext();) {
0545: IPath path = (IPath) iter.next();
0546: this .rootPaths.add(path.toString());
0547: }
0548: }
0549: this .areRootPathsComputed = true;
0550: if (VERBOSE) {
0551: System.out
0552: .println("Spent " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
0553: System.out.println("Found " + size + " root paths"); //$NON-NLS-1$ //$NON-NLS-2$
0554: int i = 0;
0555: for (Iterator iterator = this .rootPaths.iterator(); iterator
0556: .hasNext();) {
0557: System.out
0558: .println("root[" + i + "]=" + ((String) iterator.next()));//$NON-NLS-1$ //$NON-NLS-2$
0559: i++;
0560: }
0561: }
0562: }
0563:
0564: private void computeRootPath(File directory,
0565: HashSet firstLevelPackageNames, boolean hasDefaultPackage,
0566: Set set) {
0567: File[] files = directory.listFiles();
0568: boolean hasSubDirectories = false;
0569: loop: for (int i = 0, max = files.length; i < max; i++) {
0570: File file = files[i];
0571: if (file.isDirectory()) {
0572: hasSubDirectories = true;
0573: if (firstLevelPackageNames.contains(file.getName())) {
0574: IPath fullPath = new Path(file.getParentFile()
0575: .getPath());
0576: IPath rootPathEntry = fullPath.removeFirstSegments(
0577: this .sourcePath.segmentCount()).setDevice(
0578: null);
0579: set.add(rootPathEntry);
0580: break loop;
0581: } else {
0582: computeRootPath(file, firstLevelPackageNames,
0583: hasDefaultPackage, set);
0584: }
0585: } else if (i == max - 1 && !hasSubDirectories
0586: && hasDefaultPackage) {
0587: File parentDir = file.getParentFile();
0588: if (parentDir.list(FILENAME_FILTER).length != 0) {
0589: IPath fullPath = new Path(parentDir.getPath());
0590: IPath rootPathEntry = fullPath.removeFirstSegments(
0591: this .sourcePath.segmentCount()).setDevice(
0592: null);
0593: set.add(rootPathEntry);
0594: }
0595: }
0596: }
0597: }
0598:
0599: private void computeRootPath(IContainer container,
0600: HashSet firstLevelPackageNames, boolean hasDefaultPackage,
0601: Set set) {
0602: try {
0603: IResource[] resources = container.members();
0604: boolean hasSubDirectories = false;
0605: loop: for (int i = 0, max = resources.length; i < max; i++) {
0606: IResource resource = resources[i];
0607: if (resource.getType() == IResource.FOLDER) {
0608: hasSubDirectories = true;
0609: if (firstLevelPackageNames.contains(resource
0610: .getName())) {
0611: IPath fullPath = container.getFullPath();
0612: IPath rootPathEntry = fullPath
0613: .removeFirstSegments(
0614: this .sourcePath.segmentCount())
0615: .setDevice(null);
0616: set.add(rootPathEntry);
0617: break loop;
0618: } else {
0619: computeRootPath((IFolder) resource,
0620: firstLevelPackageNames,
0621: hasDefaultPackage, set);
0622: }
0623: }
0624: if (i == max - 1 && !hasSubDirectories
0625: && hasDefaultPackage) {
0626: // check if one member is a .java file
0627: boolean hasJavaSourceFile = false;
0628: for (int j = 0; j < max; j++) {
0629: if (org.eclipse.jdt.internal.core.util.Util
0630: .isJavaLikeFileName(resources[i]
0631: .getName())) {
0632: hasJavaSourceFile = true;
0633: break;
0634: }
0635: }
0636: if (hasJavaSourceFile) {
0637: IPath fullPath = container.getFullPath();
0638: IPath rootPathEntry = fullPath
0639: .removeFirstSegments(
0640: this .sourcePath.segmentCount())
0641: .setDevice(null);
0642: set.add(rootPathEntry);
0643: }
0644: }
0645: }
0646: } catch (CoreException e) {
0647: // ignore
0648: }
0649: }
0650:
0651: /**
0652: * @see ISourceElementRequestor
0653: */
0654: public void enterType(TypeInfo typeInfo) {
0655:
0656: this .typeDepth++;
0657: if (this .typeDepth == this .types.length) { // need to grow
0658: System.arraycopy(this .types, 0,
0659: this .types = new IType[this .typeDepth * 2], 0,
0660: this .typeDepth);
0661: System
0662: .arraycopy(
0663: this .typeNameRanges,
0664: 0,
0665: this .typeNameRanges = new SourceRange[this .typeDepth * 2],
0666: 0, this .typeDepth);
0667: System
0668: .arraycopy(
0669: this .typeDeclarationStarts,
0670: 0,
0671: this .typeDeclarationStarts = new int[this .typeDepth * 2],
0672: 0, this .typeDepth);
0673: System.arraycopy(this .memberName, 0,
0674: this .memberName = new String[this .typeDepth * 2],
0675: 0, this .typeDepth);
0676: System
0677: .arraycopy(
0678: this .memberDeclarationStart,
0679: 0,
0680: this .memberDeclarationStart = new int[this .typeDepth * 2],
0681: 0, this .typeDepth);
0682: System
0683: .arraycopy(
0684: this .memberNameRange,
0685: 0,
0686: this .memberNameRange = new SourceRange[this .typeDepth * 2],
0687: 0, this .typeDepth);
0688: System
0689: .arraycopy(
0690: this .methodParameterTypes,
0691: 0,
0692: this .methodParameterTypes = new char[this .typeDepth * 2][][],
0693: 0, this .typeDepth);
0694: System
0695: .arraycopy(
0696: this .methodParameterNames,
0697: 0,
0698: this .methodParameterNames = new char[this .typeDepth * 2][][],
0699: 0, this .typeDepth);
0700: System.arraycopy(this .typeModifiers, 0,
0701: this .typeModifiers = new int[this .typeDepth * 2],
0702: 0, this .typeDepth);
0703: }
0704: if (typeInfo.name.length == 0) {
0705: this .anonymousCounter++;
0706: if (this .anonymousCounter == this .anonymousClassName) {
0707: this .types[typeDepth] = this .getType(this .binaryType
0708: .getElementName());
0709: } else {
0710: this .types[typeDepth] = this .getType(new String(
0711: typeInfo.name));
0712: }
0713: } else {
0714: this .types[typeDepth] = this .getType(new String(
0715: typeInfo.name));
0716: }
0717: this .typeNameRanges[typeDepth] = new SourceRange(
0718: typeInfo.nameSourceStart, typeInfo.nameSourceEnd
0719: - typeInfo.nameSourceStart + 1);
0720: this .typeDeclarationStarts[typeDepth] = typeInfo.declarationStart;
0721:
0722: IType currentType = this .types[typeDepth];
0723:
0724: // type parameters
0725: if (typeInfo.typeParameters != null) {
0726: for (int i = 0, length = typeInfo.typeParameters.length; i < length; i++) {
0727: TypeParameterInfo typeParameterInfo = typeInfo.typeParameters[i];
0728: ITypeParameter typeParameter = currentType
0729: .getTypeParameter(new String(
0730: typeParameterInfo.name));
0731: setSourceRange(
0732: typeParameter,
0733: new SourceRange(
0734: typeParameterInfo.declarationStart,
0735: typeParameterInfo.declarationEnd
0736: - typeParameterInfo.declarationStart
0737: + 1),
0738: new SourceRange(
0739: typeParameterInfo.nameSourceStart,
0740: typeParameterInfo.nameSourceEnd
0741: - typeParameterInfo.nameSourceStart
0742: + 1));
0743: }
0744: }
0745:
0746: // type modifiers
0747: this .typeModifiers[typeDepth] = typeInfo.modifiers;
0748:
0749: // categories
0750: addCategories(currentType, typeInfo.categories);
0751: }
0752:
0753: /**
0754: * @see ISourceElementRequestor
0755: */
0756: public void enterCompilationUnit() {
0757: // do nothing
0758: }
0759:
0760: /**
0761: * @see ISourceElementRequestor
0762: */
0763: public void enterConstructor(MethodInfo methodInfo) {
0764: enterAbstractMethod(methodInfo);
0765: }
0766:
0767: /**
0768: * @see ISourceElementRequestor
0769: */
0770: public void enterField(FieldInfo fieldInfo) {
0771: if (typeDepth >= 0) {
0772: this .memberDeclarationStart[typeDepth] = fieldInfo.declarationStart;
0773: this .memberNameRange[typeDepth] = new SourceRange(
0774: fieldInfo.nameSourceStart, fieldInfo.nameSourceEnd
0775: - fieldInfo.nameSourceStart + 1);
0776: String fieldName = new String(fieldInfo.name);
0777: this .memberName[typeDepth] = fieldName;
0778:
0779: // categories
0780: IType currentType = this .types[typeDepth];
0781: IField field = currentType.getField(fieldName);
0782: addCategories(field, fieldInfo.categories);
0783: }
0784: }
0785:
0786: /**
0787: * @see ISourceElementRequestor
0788: */
0789: public void enterInitializer(int declarationSourceStart,
0790: int modifiers) {
0791: //do nothing
0792: }
0793:
0794: /**
0795: * @see ISourceElementRequestor
0796: */
0797: public void enterMethod(MethodInfo methodInfo) {
0798: enterAbstractMethod(methodInfo);
0799: }
0800:
0801: private void enterAbstractMethod(MethodInfo methodInfo) {
0802: if (typeDepth >= 0) {
0803: this .memberName[typeDepth] = new String(methodInfo.name);
0804: this .memberNameRange[typeDepth] = new SourceRange(
0805: methodInfo.nameSourceStart,
0806: methodInfo.nameSourceEnd
0807: - methodInfo.nameSourceStart + 1);
0808: this .memberDeclarationStart[typeDepth] = methodInfo.declarationStart;
0809: IType currentType = this .types[typeDepth];
0810: int currenTypeModifiers = this .typeModifiers[typeDepth];
0811: char[][] parameterTypes = methodInfo.parameterTypes;
0812: if (parameterTypes != null && methodInfo.isConstructor
0813: && currentType.getDeclaringType() != null
0814: && !Flags.isStatic(currenTypeModifiers)) {
0815: IType declaringType = currentType.getDeclaringType();
0816: String declaringTypeName = declaringType
0817: .getElementName();
0818: if (declaringTypeName.length() == 0) {
0819: IClassFile classFile = declaringType.getClassFile();
0820: int length = parameterTypes.length;
0821: char[][] newParameterTypes = new char[length + 1][];
0822: declaringTypeName = classFile.getElementName();
0823: declaringTypeName = declaringTypeName.substring(0,
0824: declaringTypeName.indexOf('.'));
0825: newParameterTypes[0] = declaringTypeName
0826: .toCharArray();
0827: System.arraycopy(parameterTypes, 0,
0828: newParameterTypes, 1, length);
0829: this .methodParameterTypes[typeDepth] = newParameterTypes;
0830: } else {
0831: int length = parameterTypes.length;
0832: char[][] newParameterTypes = new char[length + 1][];
0833: newParameterTypes[0] = declaringTypeName
0834: .toCharArray();
0835: System.arraycopy(parameterTypes, 0,
0836: newParameterTypes, 1, length);
0837: this .methodParameterTypes[typeDepth] = newParameterTypes;
0838: }
0839: } else {
0840: this .methodParameterTypes[typeDepth] = parameterTypes;
0841: }
0842: this .methodParameterNames[typeDepth] = methodInfo.parameterNames;
0843:
0844: IMethod method = currentType
0845: .getMethod(
0846: this .memberName[typeDepth],
0847: convertTypeNamesToSigs(this .methodParameterTypes[typeDepth]));
0848:
0849: // type parameters
0850: if (methodInfo.typeParameters != null) {
0851: for (int i = 0, length = methodInfo.typeParameters.length; i < length; i++) {
0852: TypeParameterInfo typeParameterInfo = methodInfo.typeParameters[i];
0853: ITypeParameter typeParameter = method
0854: .getTypeParameter(new String(
0855: typeParameterInfo.name));
0856: setSourceRange(
0857: typeParameter,
0858: new SourceRange(
0859: typeParameterInfo.declarationStart,
0860: typeParameterInfo.declarationEnd
0861: - typeParameterInfo.declarationStart
0862: + 1),
0863: new SourceRange(
0864: typeParameterInfo.nameSourceStart,
0865: typeParameterInfo.nameSourceEnd
0866: - typeParameterInfo.nameSourceStart
0867: + 1));
0868: }
0869: }
0870:
0871: // categories
0872: addCategories(method, methodInfo.categories);
0873: }
0874: }
0875:
0876: /**
0877: * @see ISourceElementRequestor
0878: */
0879: public void exitType(int declarationEnd) {
0880: if (typeDepth >= 0) {
0881: IType currentType = this .types[typeDepth];
0882: setSourceRange(
0883: currentType,
0884: new SourceRange(
0885: this .typeDeclarationStarts[typeDepth],
0886: declarationEnd
0887: - this .typeDeclarationStarts[typeDepth]
0888: + 1),
0889: this .typeNameRanges[typeDepth]);
0890: this .typeDepth--;
0891: }
0892: }
0893:
0894: /**
0895: * @see ISourceElementRequestor
0896: */
0897: public void exitCompilationUnit(int declarationEnd) {
0898: //do nothing
0899: }
0900:
0901: /**
0902: * @see ISourceElementRequestor
0903: */
0904: public void exitConstructor(int declarationEnd) {
0905: exitAbstractMethod(declarationEnd);
0906: }
0907:
0908: /**
0909: * @see ISourceElementRequestor
0910: */
0911: public void exitField(int initializationStart, int declarationEnd,
0912: int declarationSourceEnd) {
0913: if (typeDepth >= 0) {
0914: IType currentType = this .types[typeDepth];
0915: setSourceRange(
0916: currentType.getField(this .memberName[typeDepth]),
0917: new SourceRange(
0918: this .memberDeclarationStart[typeDepth],
0919: declarationEnd
0920: - this .memberDeclarationStart[typeDepth]
0921: + 1),
0922: this .memberNameRange[typeDepth]);
0923: }
0924: }
0925:
0926: /**
0927: * @see ISourceElementRequestor
0928: */
0929: public void exitInitializer(int declarationEnd) {
0930: // implements abstract method
0931: }
0932:
0933: /**
0934: * @see ISourceElementRequestor
0935: */
0936: public void exitMethod(int declarationEnd, int defaultValueStart,
0937: int defaultValueEnd) {
0938: exitAbstractMethod(declarationEnd);
0939: }
0940:
0941: private void exitAbstractMethod(int declarationEnd) {
0942: if (typeDepth >= 0) {
0943: IType currentType = this .types[typeDepth];
0944: SourceRange sourceRange = new SourceRange(
0945: this .memberDeclarationStart[typeDepth],
0946: declarationEnd
0947: - this .memberDeclarationStart[typeDepth]
0948: + 1);
0949: IMethod method = currentType
0950: .getMethod(
0951: this .memberName[typeDepth],
0952: convertTypeNamesToSigs(this .methodParameterTypes[typeDepth]));
0953: setSourceRange(method, sourceRange,
0954: this .memberNameRange[typeDepth]);
0955: setMethodParameterNames(method,
0956: this .methodParameterNames[typeDepth]);
0957: }
0958: }
0959:
0960: /**
0961: * Locates and returns source code for the given (binary) type, in this
0962: * SourceMapper's ZIP file, or returns <code>null</code> if source
0963: * code cannot be found.
0964: */
0965: public char[] findSource(IType type, IBinaryType info) {
0966: if (!type.isBinary()) {
0967: return null;
0968: }
0969: String simpleSourceFileName = ((BinaryType) type)
0970: .getSourceFileName(info);
0971: if (simpleSourceFileName == null) {
0972: return null;
0973: }
0974: return findSource(type, simpleSourceFileName);
0975: }
0976:
0977: /**
0978: * Locates and returns source code for the given (binary) type, in this
0979: * SourceMapper's ZIP file, or returns <code>null</code> if source
0980: * code cannot be found.
0981: * The given simpleSourceFileName is the .java file name (without the enclosing
0982: * folder) used to create the given type (e.g. "A.java" for x/y/A$Inner.class)
0983: */
0984: public char[] findSource(IType type, String simpleSourceFileName) {
0985: long time = 0;
0986: if (VERBOSE) {
0987: time = System.currentTimeMillis();
0988: }
0989: PackageFragment pkgFrag = (PackageFragment) type
0990: .getPackageFragment();
0991: String name = org.eclipse.jdt.internal.core.util.Util
0992: .concatWith(pkgFrag.names, simpleSourceFileName, '/');
0993:
0994: char[] source = null;
0995:
0996: if (this .rootPath != null) {
0997: source = getSourceForRootPath(this .rootPath, name);
0998: }
0999:
1000: if (source == null) {
1001: computeAllRootPaths(type);
1002: if (this .rootPaths != null) {
1003: loop: for (Iterator iterator = this .rootPaths
1004: .iterator(); iterator.hasNext();) {
1005: String currentRootPath = (String) iterator.next();
1006: if (!currentRootPath.equals(this .rootPath)) {
1007: source = getSourceForRootPath(currentRootPath,
1008: name);
1009: if (source != null) {
1010: // remember right root path
1011: this .rootPath = currentRootPath;
1012: break loop;
1013: }
1014: }
1015: }
1016: }
1017: }
1018: if (VERBOSE) {
1019: System.out
1020: .println("spent " + (System.currentTimeMillis() - time) + "ms for " + type.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$
1021: }
1022: return source;
1023: }
1024:
1025: private char[] getSourceForRootPath(String currentRootPath,
1026: String name) {
1027: String newFullName;
1028: if (!currentRootPath
1029: .equals(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH)) {
1030: if (currentRootPath.endsWith("/")) { //$NON-NLS-1$
1031: newFullName = currentRootPath + name;
1032: } else {
1033: newFullName = currentRootPath + '/' + name;
1034: }
1035: } else {
1036: newFullName = name;
1037: }
1038: return this .findSource(newFullName);
1039: }
1040:
1041: public char[] findSource(String fullName) {
1042: char[] source = null;
1043: if (Util.isArchiveFileName(this .sourcePath.lastSegment())) {
1044: // try to get the entry
1045: ZipEntry entry = null;
1046: ZipFile zip = null;
1047: JavaModelManager manager = JavaModelManager
1048: .getJavaModelManager();
1049: try {
1050: zip = manager.getZipFile(this .sourcePath);
1051: entry = zip.getEntry(fullName);
1052: if (entry != null) {
1053: // now read the source code
1054: source = readSource(entry, zip);
1055: }
1056: } catch (CoreException e) {
1057: return null;
1058: } finally {
1059: manager.closeZipFile(zip); // handle null case
1060: }
1061: } else {
1062: Object target = JavaModel.getTarget(ResourcesPlugin
1063: .getWorkspace().getRoot(), this .sourcePath, true);
1064: if (target instanceof IResource) {
1065: if (target instanceof IContainer) {
1066: IResource res = ((IContainer) target)
1067: .findMember(fullName);
1068: if (res instanceof IFile) {
1069: try {
1070: source = org.eclipse.jdt.internal.core.util.Util
1071: .getResourceContentsAsCharArray((IFile) res);
1072: } catch (JavaModelException e) {
1073: // ignore
1074: }
1075: }
1076: }
1077: } else if (target instanceof File) {
1078: File file = (File) target;
1079: if (file.isDirectory()) {
1080: File sourceFile = new File(file, fullName);
1081: if (sourceFile.isFile()) {
1082: try {
1083: source = Util.getFileCharContent(
1084: sourceFile, this .encoding);
1085: } catch (IOException e) {
1086: // ignore
1087: }
1088: }
1089: }
1090: }
1091: }
1092: return source;
1093: }
1094:
1095: /**
1096: * Returns the SourceRange for the name of the given element, or
1097: * {-1, -1} if no source range is known for the name of the element.
1098: */
1099: public SourceRange getNameRange(IJavaElement element) {
1100: switch (element.getElementType()) {
1101: case IJavaElement.METHOD:
1102: if (((IMember) element).isBinary()) {
1103: IJavaElement[] el = getUnqualifiedMethodHandle(
1104: (IMethod) element, false);
1105: if (el[1] != null
1106: && this .sourceRanges.get(el[0]) == null) {
1107: element = getUnqualifiedMethodHandle(
1108: (IMethod) element, true)[0];
1109: } else {
1110: element = el[0];
1111: }
1112: }
1113: break;
1114: case IJavaElement.TYPE_PARAMETER:
1115: IJavaElement parent = element.getParent();
1116: if (parent.getElementType() == IJavaElement.METHOD) {
1117: IMethod method = (IMethod) parent;
1118: if (method.isBinary()) {
1119: IJavaElement[] el = getUnqualifiedMethodHandle(
1120: method, false);
1121: if (el[1] != null
1122: && this .sourceRanges.get(el[0]) == null) {
1123: method = (IMethod) getUnqualifiedMethodHandle(
1124: method, true)[0];
1125: } else {
1126: method = (IMethod) el[0];
1127: }
1128: element = method.getTypeParameter(element
1129: .getElementName());
1130: }
1131: }
1132: }
1133: SourceRange[] ranges = (SourceRange[]) this .sourceRanges
1134: .get(element);
1135: if (ranges == null) {
1136: return UNKNOWN_RANGE;
1137: } else {
1138: return ranges[1];
1139: }
1140: }
1141:
1142: /**
1143: * Returns parameters names for the given method, or
1144: * null if no parameter names are known for the method.
1145: */
1146: public char[][] getMethodParameterNames(IMethod method) {
1147: if (method.isBinary()) {
1148: IJavaElement[] el = getUnqualifiedMethodHandle(method,
1149: false);
1150: if (el[1] != null && this .parameterNames.get(el[0]) == null) {
1151: method = (IMethod) getUnqualifiedMethodHandle(method,
1152: true)[0];
1153: } else {
1154: method = (IMethod) el[0];
1155: }
1156: }
1157: char[][] parameters = (char[][]) this .parameterNames
1158: .get(method);
1159: if (parameters == null) {
1160: return null;
1161: } else {
1162: return parameters;
1163: }
1164: }
1165:
1166: /**
1167: * Returns the <code>SourceRange</code> for the given element, or
1168: * {-1, -1} if no source range is known for the element.
1169: */
1170: public SourceRange getSourceRange(IJavaElement element) {
1171: switch (element.getElementType()) {
1172: case IJavaElement.METHOD:
1173: if (((IMember) element).isBinary()) {
1174: IJavaElement[] el = getUnqualifiedMethodHandle(
1175: (IMethod) element, false);
1176: if (el[1] != null
1177: && this .sourceRanges.get(el[0]) == null) {
1178: element = getUnqualifiedMethodHandle(
1179: (IMethod) element, true)[0];
1180: } else {
1181: element = el[0];
1182: }
1183: }
1184: break;
1185: case IJavaElement.TYPE_PARAMETER:
1186: IJavaElement parent = element.getParent();
1187: if (parent.getElementType() == IJavaElement.METHOD) {
1188: IMethod method = (IMethod) parent;
1189: if (method.isBinary()) {
1190: IJavaElement[] el = getUnqualifiedMethodHandle(
1191: method, false);
1192: if (el[1] != null
1193: && this .sourceRanges.get(el[0]) == null) {
1194: method = (IMethod) getUnqualifiedMethodHandle(
1195: method, true)[0];
1196: } else {
1197: method = (IMethod) el[0];
1198: }
1199: element = method.getTypeParameter(element
1200: .getElementName());
1201: }
1202: }
1203: }
1204: SourceRange[] ranges = (SourceRange[]) this .sourceRanges
1205: .get(element);
1206: if (ranges == null) {
1207: return UNKNOWN_RANGE;
1208: } else {
1209: return ranges[0];
1210: }
1211: }
1212:
1213: /**
1214: * Returns the type with the given <code>typeName</code>. Returns inner classes
1215: * as well.
1216: */
1217: protected IType getType(String typeName) {
1218: if (typeName.length() == 0) {
1219: IJavaElement classFile = this .binaryType.getParent();
1220: String classFileName = classFile.getElementName();
1221: StringBuffer newClassFileName = new StringBuffer();
1222: int lastDollar = classFileName.lastIndexOf('$');
1223: for (int i = 0; i <= lastDollar; i++)
1224: newClassFileName.append(classFileName.charAt(i));
1225: newClassFileName.append(Integer
1226: .toString(this .anonymousCounter));
1227: PackageFragment pkg = (PackageFragment) classFile
1228: .getParent();
1229: return new BinaryType(new ClassFile(pkg, newClassFileName
1230: .toString()), typeName);
1231: } else if (this .binaryType.getElementName().equals(typeName))
1232: return this .binaryType;
1233: else
1234: return this .binaryType.getType(typeName);
1235: }
1236:
1237: /**
1238: * Creates a handle that has parameter types that are not
1239: * fully qualified so that the correct source is found.
1240: */
1241: protected IJavaElement[] getUnqualifiedMethodHandle(IMethod method,
1242: boolean noDollar) {
1243: boolean hasDollar = false;
1244: String[] qualifiedParameterTypes = method.getParameterTypes();
1245: String[] unqualifiedParameterTypes = new String[qualifiedParameterTypes.length];
1246: for (int i = 0; i < qualifiedParameterTypes.length; i++) {
1247: StringBuffer unqualifiedTypeSig = new StringBuffer();
1248: getUnqualifiedTypeSignature(qualifiedParameterTypes[i],
1249: 0/*start*/, qualifiedParameterTypes[i].length(),
1250: unqualifiedTypeSig, noDollar);
1251: unqualifiedParameterTypes[i] = unqualifiedTypeSig
1252: .toString();
1253: hasDollar |= unqualifiedParameterTypes[i].lastIndexOf('$') != -1;
1254: }
1255:
1256: IJavaElement[] result = new IJavaElement[2];
1257: result[0] = ((IType) method.getParent()).getMethod(method
1258: .getElementName(), unqualifiedParameterTypes);
1259: if (hasDollar) {
1260: result[1] = result[0];
1261: }
1262: return result;
1263: }
1264:
1265: private int getUnqualifiedTypeSignature(String qualifiedTypeSig,
1266: int start, int length, StringBuffer unqualifiedTypeSig,
1267: boolean noDollar) {
1268: char firstChar = qualifiedTypeSig.charAt(start);
1269: int end = start + 1;
1270: boolean sigStart = false;
1271: firstPass: for (int i = start; i < length; i++) {
1272: char current = qualifiedTypeSig.charAt(i);
1273: switch (current) {
1274: case Signature.C_ARRAY:
1275: case Signature.C_SUPER:
1276: case Signature.C_EXTENDS:
1277: unqualifiedTypeSig.append(current);
1278: start = i + 1;
1279: end = start + 1;
1280: firstChar = qualifiedTypeSig.charAt(start);
1281: break;
1282: case Signature.C_RESOLVED:
1283: case Signature.C_UNRESOLVED:
1284: case Signature.C_TYPE_VARIABLE:
1285: if (!sigStart) {
1286: start = ++i;
1287: sigStart = true;
1288: }
1289: break;
1290: case Signature.C_NAME_END:
1291: case Signature.C_GENERIC_START:
1292: end = i;
1293: break firstPass;
1294: case Signature.C_STAR:
1295: unqualifiedTypeSig.append(current);
1296: start = i + 1;
1297: end = start + 1;
1298: firstChar = qualifiedTypeSig.charAt(start);
1299: break;
1300: case Signature.C_GENERIC_END:
1301: return i;
1302: case Signature.C_DOT:
1303: start = ++i;
1304: break;
1305: }
1306: }
1307: switch (firstChar) {
1308: case Signature.C_RESOLVED:
1309: case Signature.C_UNRESOLVED:
1310: case Signature.C_TYPE_VARIABLE:
1311: unqualifiedTypeSig.append(Signature.C_UNRESOLVED);
1312: if (noDollar) {
1313: int lastDollar = qualifiedTypeSig.lastIndexOf('$', end);
1314: if (lastDollar > start)
1315: start = lastDollar + 1;
1316: }
1317: for (int i = start; i < length; i++) {
1318: char current = qualifiedTypeSig.charAt(i);
1319: switch (current) {
1320: case Signature.C_GENERIC_START:
1321: unqualifiedTypeSig.append(current);
1322: i++;
1323: do {
1324: i = getUnqualifiedTypeSignature(
1325: qualifiedTypeSig, i, length,
1326: unqualifiedTypeSig, noDollar);
1327: } while (qualifiedTypeSig.charAt(i) != Signature.C_GENERIC_END);
1328: unqualifiedTypeSig.append(Signature.C_GENERIC_END);
1329: break;
1330: case Signature.C_NAME_END:
1331: unqualifiedTypeSig.append(current);
1332: return i + 1;
1333: default:
1334: unqualifiedTypeSig.append(current);
1335: break;
1336: }
1337: }
1338: return length;
1339: default:
1340: // primitive type or wildcard
1341: unqualifiedTypeSig.append(qualifiedTypeSig.substring(start,
1342: end));
1343: return end;
1344: }
1345: }
1346:
1347: /**
1348: * Maps the given source code to the given binary type and its children.
1349: */
1350: public void mapSource(IType type, char[] contents, IBinaryType info) {
1351: this .mapSource(type, contents, info, null);
1352: }
1353:
1354: /**
1355: * Maps the given source code to the given binary type and its children.
1356: * If a non-null java element is passed, finds the name range for the
1357: * given java element without storing it.
1358: */
1359: public synchronized ISourceRange mapSource(IType type,
1360: char[] contents, IBinaryType info,
1361: IJavaElement elementToFind) {
1362:
1363: this .binaryType = (BinaryType) type;
1364:
1365: // check whether it is already mapped
1366: if (this .sourceRanges.get(type) != null)
1367: return (elementToFind != null) ? getNameRange(elementToFind)
1368: : null;
1369:
1370: this .importsTable.remove(this .binaryType);
1371: this .importsCounterTable.remove(this .binaryType);
1372: this .searchedElement = elementToFind;
1373: this .types = new IType[1];
1374: this .typeDeclarationStarts = new int[1];
1375: this .typeNameRanges = new SourceRange[1];
1376: this .typeModifiers = new int[1];
1377: this .typeDepth = -1;
1378: this .memberDeclarationStart = new int[1];
1379: this .memberName = new String[1];
1380: this .memberNameRange = new SourceRange[1];
1381: this .methodParameterTypes = new char[1][][];
1382: this .methodParameterNames = new char[1][][];
1383: this .anonymousCounter = 0;
1384:
1385: HashMap oldSourceRanges = (HashMap) this .sourceRanges.clone();
1386: try {
1387: IProblemFactory factory = new DefaultProblemFactory();
1388: SourceElementParser parser = null;
1389: this .anonymousClassName = 0;
1390: if (info == null) {
1391: try {
1392: info = (IBinaryType) this .binaryType
1393: .getElementInfo();
1394: } catch (JavaModelException e) {
1395: return null;
1396: }
1397: }
1398: boolean isAnonymousClass = info.isAnonymous();
1399: char[] fullName = info.getName();
1400: if (isAnonymousClass) {
1401: String eltName = this .binaryType.getParent()
1402: .getElementName();
1403: eltName = eltName.substring(
1404: eltName.lastIndexOf('$') + 1, eltName.length());
1405: try {
1406: this .anonymousClassName = Integer.parseInt(eltName);
1407: } catch (NumberFormatException e) {
1408: // ignore
1409: }
1410: }
1411: boolean doFullParse = hasToRetrieveSourceRangesForLocalClass(fullName);
1412: parser = new SourceElementParser(this , factory,
1413: new CompilerOptions(this .options), doFullParse,
1414: true/*optimize string literals*/);
1415: parser.javadocParser.checkDocComment = false; // disable javadoc parsing
1416: IJavaElement javaElement = this .binaryType
1417: .getCompilationUnit();
1418: if (javaElement == null)
1419: javaElement = this .binaryType.getParent();
1420: parser.parseCompilationUnit(new BasicCompilationUnit(
1421: contents, null, this .binaryType
1422: .sourceFileName(info), javaElement),
1423: doFullParse);
1424: if (elementToFind != null) {
1425: ISourceRange range = this .getNameRange(elementToFind);
1426: return range;
1427: } else {
1428: return null;
1429: }
1430: } finally {
1431: if (elementToFind != null) {
1432: this .sourceRanges = oldSourceRanges;
1433: }
1434: this .binaryType = null;
1435: this .searchedElement = null;
1436: this .types = null;
1437: this .typeDeclarationStarts = null;
1438: this .typeNameRanges = null;
1439: this .typeDepth = -1;
1440: }
1441: }
1442:
1443: private char[] readSource(ZipEntry entry, ZipFile zip) {
1444: try {
1445: byte[] bytes = Util.getZipEntryByteContent(entry, zip);
1446: if (bytes != null) {
1447: return Util.bytesToChar(bytes, this .encoding);
1448: }
1449: } catch (IOException e) {
1450: // ignore
1451: }
1452: return null;
1453: }
1454:
1455: /**
1456: * Sets the mapping for this method to its parameter names.
1457: *
1458: * @see #parameterNames
1459: */
1460: protected void setMethodParameterNames(IMethod method,
1461: char[][] parameterNames) {
1462: if (parameterNames == null) {
1463: parameterNames = CharOperation.NO_CHAR_CHAR;
1464: }
1465: this .parameterNames.put(method, parameterNames);
1466: }
1467:
1468: /**
1469: * Sets the mapping for this element to its source ranges for its source range
1470: * and name range.
1471: *
1472: * @see #sourceRanges
1473: */
1474: protected void setSourceRange(IJavaElement element,
1475: SourceRange sourceRange, SourceRange nameRange) {
1476: this .sourceRanges.put(element, new SourceRange[] { sourceRange,
1477: nameRange });
1478: }
1479:
1480: /**
1481: * Return a char[][] array containing the imports of the attached source for the binary type
1482: */
1483: public char[][] getImports(BinaryType type) {
1484: char[][] imports = (char[][]) this .importsTable.get(type);
1485: if (imports != null) {
1486: int importsCounter = ((Integer) this .importsCounterTable
1487: .get(type)).intValue();
1488: if (imports.length != importsCounter) {
1489: System.arraycopy(imports, 0,
1490: (imports = new char[importsCounter][]), 0,
1491: importsCounter);
1492: }
1493: this .importsTable.put(type, imports);
1494: }
1495: return imports;
1496: }
1497:
1498: private boolean hasToRetrieveSourceRangesForLocalClass(
1499: char[] eltName) {
1500: /*
1501: * A$1$B$2 : true
1502: * A$B$B$2 : true
1503: * A$C$B$D : false
1504: * A$F$B$D$1$F : true
1505: * A$F$B$D$1F : true
1506: * A$1 : true
1507: * A$B : false
1508: */
1509: if (eltName == null)
1510: return false;
1511: int length = eltName.length;
1512: int dollarIndex = CharOperation.indexOf('$', eltName, 0);
1513: while (dollarIndex != -1) {
1514: int nameStart = dollarIndex + 1;
1515: if (nameStart == length)
1516: return false;
1517: if (Character.isDigit(eltName[nameStart]))
1518: return true;
1519: dollarIndex = CharOperation
1520: .indexOf('$', eltName, nameStart);
1521: }
1522: return false;
1523: }
1524:
1525: }
|