blob: d4c5da52e22d80f5d0cfaa8ef1185239e95e98cd [file] [log] [blame]
package org.eclipse.jdt.internal.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.ConfigurableOption;
import org.eclipse.jdt.internal.compiler.classfmt.*;
import java.io.*;
import java.util.*;
import java.util.zip.ZipFile;
import java.lang.reflect.Modifier;
/**
* @see IClassFile
*/
public class ClassFile extends Openable implements IClassFile {
protected BinaryType fBinaryType = null;
/*
* Creates a handle to a class file.
*
* @exception IllegalArgumentExcpetion if the name does not end with ".class"
*/
protected ClassFile(IPackageFragment parent, String name) {
super(CLASS_FILE, parent, name);
if (!Util.isClassFileName(name)) {
throw new IllegalArgumentException(Util.bind("element.invalidClassFileName")); //$NON-NLS-1$
}
}
/**
* @see ICodeAssist
*/
public void codeComplete(int offset, ICodeCompletionRequestor requestor) throws JavaModelException {
String source = getSource();
if (source != null) {
BasicCompilationUnit cu = new BasicCompilationUnit(getSource().toCharArray(), getElementName() + ".java"); //$NON-NLS-1$
codeComplete(cu, cu, offset, requestor);
}
}
/**
* @see ICodeResolve
*/
public IJavaElement[] codeSelect(int offset, int length) throws JavaModelException {
IBuffer buffer = getBuffer();
if (buffer != null) {
char[] contents = null;
contents = buffer.getCharacters();
String name = getElementName();
name = name.substring(0, name.length() - 6); // remove ".class"
name = name + ".java"; //$NON-NLS-1$
BasicCompilationUnit cu = new BasicCompilationUnit(contents, name);
return super.codeSelect(cu, offset, length);
} else {
//has no associated souce
return new IJavaElement[] {};
}
}
/**
* Returns a new element info for this element.
*/
protected OpenableElementInfo createElementInfo() {
return new ClassFileInfo(this);
}
/**
* Finds the deepest <code>IJavaElement</code> in the hierarchy of
* <code>elt</elt>'s children (including <code>elt</code> itself)
* which has a source range that encloses <code>position</code>
* according to <code>mapper</code>.
*/
protected IJavaElement findElement(IJavaElement elt, int position, SourceMapper mapper) {
SourceRange range = mapper.getSourceRange(elt);
if (range == null || position < range.getOffset() || range.getOffset() + range.getLength() - 1 < position) {
return null;
}
if (elt instanceof IParent) {
try {
IJavaElement[] children = ((IParent) elt).getChildren();
for (int i = 0; i < children.length; i++) {
IJavaElement match = findElement(children[i], position, mapper);
if (match != null) {
return match;
}
}
} catch (JavaModelException npe) {
}
}
return elt;
}
/**
* Creates the children elements for this class file adding the resulting
* new handles and info objects to the newElements table. Returns true
* if successful, or false if an error is encountered parsing the class file.
*
* @see Openable
* @see Signature
*/
protected boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, Hashtable newElements, IResource underlyingResource) throws JavaModelException {
IBinaryType typeInfo = getBinaryTypeInfo((IFile) underlyingResource);
if (typeInfo == null) {
// The structure of a class file is unknown if a class file format errors occurred
//during the creation of the diet class file representative of this ClassFile.
info.setChildren(new IJavaElement[] {});
return false;
}
// Make the type
IType type = new BinaryType(this, new String(simpleName(typeInfo.getName())));
info.addChild(type);
newElements.put(type, typeInfo);
return true;
}
/**
* Returns the <code>ClassFileReader</code>specific for this IClassFile, based
* on its underlying resource, or <code>null</code> if unable to create
* the diet class file.
* There are two cases to consider:<ul>
* <li>a class file corresponding to an IFile resource</li>
* <li>a class file corresponding to a zip entry in a JAR</li>
* </ul>
*
* @exception JavaModelException when the IFile resource or JAR is not available
* or when this class file is not present in the JAR
*/
private IBinaryType getBinaryTypeInfo(IFile file) throws JavaModelException {
JavaElement le = (JavaElement) getParent();
if (le instanceof JarPackageFragment) {
try {
JarPackageFragmentRoot root = (JarPackageFragmentRoot) le.getParent();
IBinaryType info = null;
ZipFile zip = null;
try {
zip = root.getJar();
String entryName = getParent().getElementName();
entryName = entryName.replace('.', '/');
if (entryName.equals("")) { //$NON-NLS-1$
entryName += getElementName();
} else {
entryName += '/' + getElementName();
}
info = ClassFileReader.read(zip, entryName);
} finally {
if (zip != null) {
try {
zip.close();
} catch (IOException e) {
// ignore
}
}
}
if (info == null) {
throw newNotPresentException();
}
return info;
} catch (ClassFormatException cfe) {
//the structure remains unknown
return null;
} catch (IOException ioe) {
throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION);
} catch (CoreException e) {
throw new JavaModelException(e);
}
} else {
byte[] contents = null;
contents = BufferManager.getResourceContentsAsBytes(file);
try {
return new ClassFileReader(contents, getElementName().toCharArray());
} catch (ClassFormatException cfe) {
//the structure remains unknown
return null;
}
}
}
/**
* Note: a buffer with no unsaved changes can be closed by the Java Model
* since it has a finite number of buffers allowed open at one time. If this
* is the first time a request is being made for the buffer, an attempt is
* made to create and fill this element's buffer. If the buffer has been
* closed since it was first opened, the buffer is re-created.
*
* @see IOpenable
*/
public IBuffer getBuffer() throws JavaModelException {
IBuffer buffer = getBufferManager().getBuffer(this);
if (buffer == null) {
// try to (re)open a buffer
return openBuffer(null);
}
return buffer;
}
/**
* @see IMember
*/
public IClassFile getClassFile() {
return this;
}
/**
* A class file has a corresponding resource unless it is contained
* in a jar.
*
* @see IJavaElement
*/
public IResource getCorrespondingResource() throws JavaModelException {
IPackageFragmentRoot root= (IPackageFragmentRoot)getParent().getParent();
if (root.isArchive()) {
return null;
} else {
return getUnderlyingResource();
}
}
/**
* @see IClassFile
*/
public IJavaElement getElementAt(int position) throws JavaModelException {
IJavaElement parent = getParent();
while (parent.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) {
parent = parent.getParent();
}
PackageFragmentRoot root = (PackageFragmentRoot) parent;
SourceMapper mapper = root.getSourceMapper();
if (mapper == null) {
return null;
} else {
IType type = getType();
return findElement(type, position, mapper);
}
}
/**
* @see JavaElement#getHandleMemento()
*/
protected char getHandleMementoDelimiter() {
return JavaElement.JEM_CLASSFILE;
}
/**
* @see ISourceReference
*/
public String getSource() throws JavaModelException {
IBuffer buffer = getBuffer();
if (buffer == null) {
return null;
}
return buffer.getContents();
}
/**
* @see ISourceReference
*/
public ISourceRange getSourceRange() throws JavaModelException {
return new SourceRange(0, getBuffer().getContents().toString().length());
}
/**
* @see IClassFile
*/
public IType getType() throws JavaModelException {
if (fBinaryType == null) {
// Remove the ".class" from the name of the ClassFile - always works
// since constructor fails if name does not end with ".class"
String name = fName.substring(0, fName.lastIndexOf('.'));
name = name.substring(name.lastIndexOf('.') + 1);
int index = name.lastIndexOf('$');
if (index > -1) {
name = name.substring(index + 1);
}
fBinaryType = new BinaryType(this, name);
}
return fBinaryType;
}
/**
*
*/
public WorkingCopy getWorkingCopy() {
String name = getElementName();
name = name.substring(0, name.length() - 6); // remove ".class"
name = name + ".java"; //$NON-NLS-1$
return new WorkingCopy((IPackageFragment) getParent(), name);
}
/**
* @see Openable
*/
protected boolean hasBuffer() {
return true;
}
/**
* If I am not open, return true to avoid parsing.
*
* @see IParent
*/
public boolean hasChildren() throws JavaModelException {
if (isOpen()) {
return getChildren().length > 0;
} else {
return true;
}
}
/**
* @see IClassFile
*/
public boolean isClass() throws JavaModelException {
return getType().isClass();
}
/**
* @see IClassFile
*/
public boolean isInterface() throws JavaModelException {
return getType().isInterface();
}
/**
* Returns true - class files are always read only.
*/
public boolean isReadOnly() {
return true;
}
/**
* Opens and returns buffer on the source code associated with this class file.
* Maps the source code to the children elements of this class file.
* If no source code is associated with this class file,
* <code>null</code> is returned.
*
* @see Openable
*/
protected IBuffer openBuffer(IProgressMonitor pm) throws JavaModelException {
SourceMapper mapper = getSourceMapper();
if (mapper != null) {
char[] contents = mapper.findSource(getType());
if (contents != null) {
IBufferManager bufManager = getBufferManager();
IBuffer buf = bufManager.openBuffer(contents, pm, this, isReadOnly());
// do the source mapping
mapper.mapSource(getType(), contents);
return buf;
}
} else {
// Attempts to find the corresponding java file
String qualifiedName = getType().getFullyQualifiedName();
INameLookup lookup = ((JavaProject) getJavaProject()).getNameLookup();
ICompilationUnit cu = lookup.findCompilationUnit(qualifiedName);
if (cu != null) {
return cu.getBuffer();
}
}
return null;
}
/**
* Returns the Java Model format of the simple class name for the
* given className which is provided in diet class file format,
* or <code>null</code> if the given className is <code>null</code>.
* (This removes package name and enclosing type names).
*
* <p><code>ClassFileReader</code> format is similar to "java/lang/Object",
* and corresponding Java Model simple name format is "Object".
*/
/* package */ static char[] simpleName(char[] className) {
if (className == null)
return null;
className = unqualifiedName(className);
int count = 0;
for (int i = className.length - 1; i > -1; i--) {
if (className[i] == '$') {
char[] name = new char[count];
System.arraycopy(className, i + 1, name, 0, count);
return name;
}
count++;
}
return className;
}
/**
* Returns the Java Model representation of the given name
* which is provided in diet class file format, or <code>null</code>
* if the given name is <code>null</code>.
*
* <p><code>ClassFileReader</code> format is similar to "java/lang/Object",
* and corresponding Java Model format is "java.lang.Object".
*/
public static char[] translatedName(char[] name) {
if (name == null)
return null;
int nameLength = name.length;
char[] newName= new char[nameLength];
for (int i= 0; i < nameLength; i++) {
if (name[i] == '/') {
newName[i]= '.';
} else {
newName[i]= name[i];
}
}
return newName;
}
/**
* Returns the Java Model representation of the given names
* which are provided in diet class file format, or <code>null</code>
* if the given names are <code>null</code>.
*
* <p><code>ClassFileReader</code> format is similar to "java/lang/Object",
* and corresponding Java Model format is "java.lang.Object".
*/
/* package */ static char[][] translatedNames(char[][] names) {
if (names == null)
return null;
int length = names.length;
char[][] newNames = new char[length][];
for(int i = 0; i < length; i++) {
newNames[i] = translatedName(names[i]);
}
return newNames;
}
/**
* Returns the Java Model format of the unqualified class name for the
* given className which is provided in diet class file format,
* or <code>null</code> if the given className is <code>null</code>.
* (This removes the package name, but not enclosing type names).
*
* <p><code>ClassFileReader</code> format is similar to "java/lang/Object",
* and corresponding Java Model simple name format is "Object".
*/
/* package */ static char[] unqualifiedName(char[] className) {
if (className == null)
return null;
int count = 0;
for (int i = className.length - 1; i > -1; i--) {
if (className[i] == '/') {
char[] name = new char[count];
System.arraycopy(className, i + 1, name, 0, count);
return name;
}
count++;
}
return className;
}
}