package org.eclipse.jdt.internal.core;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.IDependent;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.nd.java.JavaNames;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory;
import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils;
import org.eclipse.jdt.internal.core.util.MementoTokenizer;
import org.eclipse.jdt.internal.core.util.Util;
@SuppressWarnings({"rawtypes"})
public class ClassFile extends AbstractClassFile implements IOrdinaryClassFile {
protected BinaryType binaryType = null;
private IPath externalAnnotationBase;
protected ClassFile(PackageFragment parent, String nameWithoutExtension) {
super(parent, nameWithoutExtension);
}
@Override
protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
IBinaryType typeInfo = getBinaryTypeInfo();
if (typeInfo == null) {
info.setChildren(JavaElement.NO_ELEMENTS);
return false;
}
IType type = getType();
info.setChildren(new IJavaElement[] {type});
newElements.put(type, typeInfo);
((ClassFileInfo) info).readBinaryChildren(this, (HashMap) newElements, typeInfo);
return true;
}
@Override
public void codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException {
String source = getSource();
if (source != null) {
BinaryType type = (BinaryType) getType();
BasicCompilationUnit cu =
new BasicCompilationUnit(
getSource().toCharArray(),
null,
type.sourceFileName((IBinaryType) type.getElementInfo()),
getJavaProject());
codeComplete(cu, cu, offset, requestor, owner, null, monitor);
}
}
@Override
public IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner owner) throws JavaModelException {
IBuffer buffer = getBuffer();
char[] contents;
if (buffer != null && (contents = buffer.getCharacters()) != null) {
BinaryType type = (BinaryType) getType();
BasicCompilationUnit cu = new BasicCompilationUnit(contents, null, type.sourceFileName((IBinaryType) type.getElementInfo()), this);
return super.codeSelect(cu, offset, length, owner);
} else {
return new IJavaElement[] {};
}
}
public boolean existsUsingJarTypeCache() {
if (getPackageFragmentRoot().isArchive()) {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
IType type = getType();
Object info = manager.getInfo(type);
if (info == JavaModelCache.NON_EXISTING_JAR_TYPE_INFO)
return false;
else if (info != null)
return true;
JavaElementInfo parentInfo = (JavaElementInfo) manager.getInfo(getParent());
if (parentInfo != null) {
IJavaElement[] children = parentInfo.getChildren();
for (int i = 0, length = children.length; i < length; i++) {
IJavaElement child = children[i];
if (child instanceof ClassFile && this.name.equals(((ClassFile) child).name))
return true;
}
return false;
}
try {
info = getJarBinaryTypeInfo();
} catch (CoreException | IOException | ClassFormatException e) {
}
manager.putJarTypeInfo(type, info == null ? JavaModelCache.NON_EXISTING_JAR_TYPE_INFO : info);
return info != null;
} else
return exists();
}
@Override
public IType findPrimaryType() {
IType primaryType= getType();
if (primaryType.exists()) {
return primaryType;
}
return null;
}
@Override
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
return getType().getAttachedJavadoc(monitor);
}
public IBinaryType getBinaryTypeInfo() throws JavaModelException {
try {
IBinaryType info = getJarBinaryTypeInfo();
if (info == null) {
throw newNotPresentException();
}
return info;
} catch (ClassFormatException cfe) {
if (JavaCore.getPlugin().isDebugging()) {
cfe.printStackTrace(System.err);
}
return null;
} catch (IOException ioe) {
throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION);
} catch (CoreException e) {
if (e instanceof JavaModelException) {
throw (JavaModelException)e;
} else {
throw new JavaModelException(e);
}
}
}
public String getName() {
return this.name;
}
private IBinaryType getJarBinaryTypeInfo() throws CoreException, IOException, ClassFormatException {
BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(this);
if (descriptor == null) {
return null;
}
IBinaryType result = null;
IPackageFragmentRoot root = getPackageFragmentRoot();
if (getPackageFragmentRoot() instanceof JarPackageFragmentRoot) {
if (root instanceof JrtPackageFragmentRoot || this.name.equals(IModule.MODULE_INFO)) {
PackageFragment pkg = (PackageFragment) getParent();
JarPackageFragmentRoot jarRoot = (JarPackageFragmentRoot) getPackageFragmentRoot();
String entryName = jarRoot.getClassFilePath(Util.concatWith(pkg.names, getElementName(), '/'));
byte[] contents = getClassFileContent(jarRoot, entryName);
if (contents != null) {
String fileName = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName;
result = new ClassFileReader(contents, fileName.toCharArray(), false);
}
} else {
result = BinaryTypeFactory.readType(descriptor, null);
}
} else {
result = BinaryTypeFactory.readType(descriptor, null);
}
if (result == null) {
return null;
}
if (root.getKind() == IPackageFragmentRoot.K_BINARY) {
JavaProject javaProject = (JavaProject) getAncestor(IJavaElement.JAVA_PROJECT);
IClasspathEntry entry;
try {
entry = javaProject.getClasspathEntryFor(getPath());
} catch (JavaModelException jme) {
return result;
}
if (entry != null) {
PackageFragment pkg = (PackageFragment) getParent();
String entryName = Util.concatWith(pkg.names, getElementName(), '/');
entryName = new String(CharArrayUtils.concat(
JavaNames.fieldDescriptorToBinaryName(descriptor.fieldDescriptor), SuffixConstants.SUFFIX_CLASS));
IProject project = javaProject.getProject();
IPath externalAnnotationPath = ClasspathEntry.getExternalAnnotationPath(entry, project, false);
if (externalAnnotationPath != null) {
result = setupExternalAnnotationProvider(project, externalAnnotationPath, result,
entryName.substring(0, entryName.length() - SuffixConstants.SUFFIX_CLASS.length));
} else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
result = new ExternalAnnotationDecorator(result, true);
}
}
}
return result;
}
private IBinaryType setupExternalAnnotationProvider(IProject project, final IPath externalAnnotationPath,
IBinaryType reader, final String typeName)
{
IBinaryType result = reader;
IWorkspaceRoot root = project.getWorkspace().getRoot();
IResource resource;
if (externalAnnotationPath.segmentCount() == 1) {
resource = root.getProject(externalAnnotationPath.lastSegment());
} else {
resource = root.getFolder(externalAnnotationPath);
if (!resource.exists())
resource = root.getFile(externalAnnotationPath);
}
String resolvedPath;
if (resource.exists()) {
if (resource.isVirtual()) {
Util.log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
"Virtual resource "+externalAnnotationPath+" cannot be used as annotationpath for project "+project.getName()));
return reader;
}
resolvedPath = resource.getLocation().toString();
} else {
resolvedPath = externalAnnotationPath.toString();
}
ZipFile annotationZip = null;
try {
annotationZip = ExternalAnnotationDecorator.getAnnotationZipFile(resolvedPath, new ExternalAnnotationDecorator.ZipFileProducer() {
@Override public ZipFile produce() throws IOException {
try {
return JavaModelManager.getJavaModelManager().getZipFile(externalAnnotationPath);
} catch (CoreException e) {
throw new IOException("Failed to read annotation file for "+typeName+" from "+externalAnnotationPath.toString(), e);
}
}});
ExternalAnnotationProvider annotationProvider = ExternalAnnotationDecorator
.externalAnnotationProvider(resolvedPath, typeName, annotationZip);
result = new ExternalAnnotationDecorator(reader, annotationProvider);
} catch (IOException e) {
Util.log(e);
return result;
} finally {
if (annotationZip != null)
JavaModelManager.getJavaModelManager().closeZipFile(annotationZip);
}
if (annotationZip == null) {
this.externalAnnotationBase = externalAnnotationPath;
ExternalAnnotationTracker.registerClassFile(externalAnnotationPath, new Path(typeName), this);
}
return result;
}
void closeAndRemoveFromJarTypeCache() throws JavaModelException {
super.close();
JavaModelManager.getJavaModelManager().removeFromJarTypeCache(this.binaryType);
}
@Override
public void close() throws JavaModelException {
if (this.externalAnnotationBase != null) {
String entryName = Util.concatWith(((PackageFragment) getParent()).names, this.name, '/');
ExternalAnnotationTracker.unregisterClassFile(this.externalAnnotationBase, new Path(entryName));
}
super.close();
}
@Override
public IClassFile getClassFile() {
return this;
}
@Override
public IJavaElement getElementAt(int position) throws JavaModelException {
IJavaElement parentElement = getParent();
while (parentElement.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) {
parentElement = parentElement.getParent();
}
PackageFragmentRoot root = (PackageFragmentRoot) parentElement;
SourceMapper mapper = root.getSourceMapper();
if (mapper == null) {
return null;
} else {
getBuffer();
IType type = getType();
return findElement(type, position, mapper);
}
}
@Override
public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
switch (token.charAt(0)) {
case JEM_TYPE:
if (!memento.hasMoreTokens()) return this;
String typeName = memento.nextToken();
JavaElement type = new BinaryType(this, typeName);
return type.getHandleFromMemento(memento, owner);
}
return null;
}
@Override
protected char getHandleMementoDelimiter() {
return JavaElement.JEM_CLASSFILE;
}
public String getTopLevelTypeName() {
String topLevelTypeName = getElementName();
int firstDollar = topLevelTypeName.indexOf('$');
if (firstDollar != -1) {
topLevelTypeName = topLevelTypeName.substring(0, firstDollar);
} else {
topLevelTypeName = topLevelTypeName.substring(0, topLevelTypeName.length()-SUFFIX_CLASS.length);
}
return topLevelTypeName;
}
@Override
public IType getType() {
if (this.binaryType == null) {
this.binaryType = new BinaryType(this, getTypeName());
}
return this.binaryType;
}
public String getTypeName() {
int lastDollar = this.name.lastIndexOf('$');
return lastDollar > -1 ? Util.localTypeName(this.name, lastDollar, this.name.length()) : this.name;
}
@Override
public ICompilationUnit getWorkingCopy(WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException {
CompilationUnit workingCopy = new ClassFileWorkingCopy(this, owner == null ? DefaultWorkingCopyOwner.PRIMARY : owner);
JavaModelManager manager = JavaModelManager.getJavaModelManager();
JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo =
manager.getPerWorkingCopyInfo(workingCopy, false, true, null);
if (perWorkingCopyInfo != null) {
return perWorkingCopyInfo.getWorkingCopy();
}
BecomeWorkingCopyOperation op = new BecomeWorkingCopyOperation(workingCopy, null);
op.runOperation(monitor);
return workingCopy;
}
@Override
public boolean isClass() throws JavaModelException {
return getType().isClass();
}
@Override
public boolean isInterface() throws JavaModelException {
return getType().isInterface();
}
@Override
protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException {
IType outerMostEnclosingType = getOuterMostEnclosingType();
IBuffer buffer = getBufferManager().getBuffer(outerMostEnclosingType.getClassFile());
if (buffer == null) {
SourceMapper mapper = getSourceMapper();
IBinaryType typeInfo = info instanceof IBinaryType ? (IBinaryType) info : null;
if (mapper != null) {
buffer = mapSource(mapper, typeInfo, outerMostEnclosingType.getClassFile());
}
}
return buffer;
}
private IBuffer mapSource(SourceMapper mapper, IBinaryType info, IClassFile bufferOwner) {
char[] contents = mapper.findSource(getType(), info);
if (contents != null) {
IBuffer buffer = BufferManager.createBuffer(bufferOwner);
if (buffer == null) return null;
BufferManager bufManager = getBufferManager();
bufManager.addBuffer(buffer);
if (buffer.getCharacters() == null){
buffer.setContents(contents);
}
buffer.addBufferChangedListener(this);
mapper.mapSource((NamedMember) getOuterMostEnclosingType(), contents, info);
return buffer;
} else {
IBuffer buffer = BufferManager.createNullBuffer(bufferOwner);
if (buffer == null) return null;
BufferManager bufManager = getBufferManager();
bufManager.addBuffer(buffer);
buffer.addBufferChangedListener(this);
return buffer;
}
}
static String simpleName(char[] className) {
if (className == null)
return null;
String simpleName = new String(unqualifiedName(className));
int lastDollar = simpleName.lastIndexOf('$');
if (lastDollar != -1)
return Util.localTypeName(simpleName, lastDollar, simpleName.length());
else
return simpleName;
}
private IType getOuterMostEnclosingType() {
IType type = getType();
IType enclosingType = type.getDeclaringType();
while (enclosingType != null) {
type = enclosingType;
enclosingType = type.getDeclaringType();
}
return type;
}
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;
}
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;
}
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;
}
}