package org.eclipse.jdt.internal.core; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.core.runtime.*; | |
import org.eclipse.core.resources.*; | |
import org.eclipse.jdt.core.*; | |
import java.io.*; | |
import java.util.*; | |
import java.util.zip.*; | |
/** | |
* A package fragment root that corresponds to a .jar or .zip. | |
* | |
* <p>NOTE: The only visible entries from a .jar or .zip package fragment root | |
* are .class files. | |
* <p>NOTE: A jar package fragment root may or may not have an associated resource. | |
* | |
* @see IPackageFragmentRoot | |
* @see JarPackageFragmentRootInfo | |
*/ | |
public class JarPackageFragmentRoot extends PackageFragmentRoot { | |
/** | |
* The absolute path to the jar file. | |
*/ | |
protected IPath fJarPath= null; | |
/** | |
* The delimiter between the zip path and source path in the | |
* attachment server property. | |
*/ | |
protected final static char ATTACHMENT_PROPERTY_DELIMITER= '*'; | |
/** | |
* The name of the meta-inf directory not to be included as a | |
* jar package fragment. | |
* @see #computeJarChildren | |
*/ | |
//protected final static String META_INF_NAME = "META-INF/"; | |
/** | |
* Constructs a package fragment root which is the root of the Java package directory hierarchy | |
* based on a JAR file that is not contained in a <code>IJavaProject</code> and | |
* does not have an associated <code>IResource</code>. | |
*/ | |
protected JarPackageFragmentRoot(String jarPath, IJavaProject project) { | |
super(null, project, jarPath); | |
fJarPath= new Path(jarPath); | |
} | |
/** | |
* Constructs a package fragment root which is the root of the Java package directory hierarchy | |
* based on a JAR file. | |
*/ | |
protected JarPackageFragmentRoot(IResource resource, IJavaProject project) { | |
super(resource, project); | |
fJarPath= resource.getFullPath(); | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public void attachSource(IPath zipPath, IPath rootPath, IProgressMonitor monitor) throws JavaModelException { | |
QualifiedName qName= getSourceAttachmentPropertyName(); | |
try { | |
verifyAttachSource(zipPath); | |
if (monitor != null) { | |
monitor.beginTask(Util.bind("element.attachingSource"/*nonNLS*/), 2); | |
} | |
SourceMapper mapper= null; | |
SourceMapper oldMapper= getSourceMapper(); | |
IWorkspace workspace= getJavaModel().getWorkspace(); | |
boolean rootNeedsToBeClosed= false; | |
if (zipPath == null) { | |
//source being detached | |
rootNeedsToBeClosed= true; | |
/* Disable deltas (see 1GDTUSD) | |
// fire a delta to notify the UI about the source detachement. | |
JavaModelManager manager = (JavaModelManager) JavaModelManager.getJavaModelManager(); | |
JavaModel model = (JavaModel) getJavaModel(); | |
JavaElementDelta attachedSourceDelta = new JavaElementDelta(model); | |
attachedSourceDelta .sourceDetached(this); // this would be a JarPackageFragmentRoot | |
manager.registerResourceDelta(attachedSourceDelta ); | |
manager.fire(); // maybe you want to fire the change later. Let us know about it. | |
*/ | |
} else { | |
/* | |
// fire a delta to notify the UI about the source attachement. | |
JavaModelManager manager = (JavaModelManager) JavaModelManager.getJavaModelManager(); | |
JavaModel model = (JavaModel) getJavaModel(); | |
JavaElementDelta attachedSourceDelta = new JavaElementDelta(model); | |
attachedSourceDelta .sourceAttached(this); // this would be a JarPackageFragmentRoot | |
manager.registerResourceDelta(attachedSourceDelta ); | |
manager.fire(); // maybe you want to fire the change later. Let us know about it. | |
*/ | |
String rootPathString= null; | |
if (rootPath == null) { | |
rootPath= new Path(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH); | |
} | |
//check if different from the current attachment | |
IPath storedZipPath= getSourceAttachmentPath(); | |
IPath storedRootPath= getSourceAttachmentRootPath(); | |
if (monitor != null) { | |
monitor.worked(1); | |
} | |
if (storedZipPath != null) { | |
if (!(storedZipPath.equals(zipPath) && rootPath.equals(storedRootPath))) { | |
rootNeedsToBeClosed= true; | |
} | |
} | |
if ((zipPath.isAbsolute() && workspace.getRoot().findMember(zipPath) != null) || !zipPath.isAbsolute()) { | |
// internal to the workbench | |
// a resource | |
IResource zipFile= workspace.getRoot().findMember(zipPath); | |
if (zipFile == null) { | |
if (monitor != null) { | |
monitor.done(); | |
} | |
throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, zipPath)); | |
} | |
if (!(zipFile.getType() == IResource.FILE)) { | |
if (monitor != null) { | |
monitor.done(); | |
} | |
throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, zipPath)); | |
} | |
} | |
mapper= new SourceMapper(zipPath, rootPath.toOSString(), (JavaModel) getJavaModel()); | |
} | |
setSourceMapper(mapper); | |
if (zipPath == null) { | |
//remove the property | |
getWorkspace().getRoot().setPersistentProperty(qName, null); | |
} else { | |
//set the property to the path of the mapped zip | |
getWorkspace().getRoot().setPersistentProperty(qName, zipPath.toString() + ATTACHMENT_PROPERTY_DELIMITER + rootPath.toString()); | |
} | |
if (rootNeedsToBeClosed) { | |
if (oldMapper != null) { | |
oldMapper.close(); | |
} | |
IBufferManager manager= BufferManager.getDefaultBufferManager(); | |
Enumeration openBuffers= manager.getOpenBuffers(); | |
while (openBuffers.hasMoreElements()) { | |
IBuffer buffer= (IBuffer) openBuffers.nextElement(); | |
IOpenable possibleJarMember= buffer.getOwner(); | |
if (isAncestorOf((IJavaElement) possibleJarMember)) { | |
buffer.close(); | |
} | |
} | |
if (monitor != null) { | |
monitor.worked(1); | |
} | |
} | |
} catch (JavaModelException e) { | |
try { | |
getWorkspace().getRoot().setPersistentProperty(qName, null); // loose info - will be recomputed | |
} catch(CoreException ce){ | |
} | |
throw e; | |
} catch (CoreException rae) { | |
try { | |
getWorkspace().getRoot().setPersistentProperty(qName, null); // loose info - will be recomputed | |
} catch(CoreException ce){ | |
} | |
throw new JavaModelException(rae); | |
} finally { | |
if (monitor != null) { | |
monitor.done(); | |
} | |
} | |
} | |
/** | |
* Close the associated JAR file stored in the info of this element. If | |
* this jar has an associated ZIP source attachment, close it too. | |
* | |
* @see IOpenable | |
*/ | |
protected void closing(Object info) throws JavaModelException { | |
SourceMapper mapper= getSourceMapper(); | |
if (mapper != null) { | |
mapper.close(); | |
} | |
super.closing(info); | |
} | |
/** | |
* Compute the package fragment children of this package fragment root. | |
* These are all of the directory zip entries, and any directories implied | |
* by the path of class files contained in the jar of this package fragment root. | |
* Has the side effect of opening the package fragment children. | |
*/ | |
protected boolean computeChildren(OpenableElementInfo info) throws JavaModelException { | |
Vector vChildren= new Vector(); | |
computeJarChildren((JarPackageFragmentRootInfo) info, vChildren); | |
IJavaElement[] children= new IJavaElement[vChildren.size()]; | |
vChildren.copyInto(children); | |
info.setChildren(children); | |
return true; | |
} | |
/** | |
* Determine all of the package fragments associated with this package fragment root. | |
* Cache the zip entries for each package fragment in the info for the package fragment. | |
* The package fragment children are all opened. | |
* Add all of the package fragments to vChildren. | |
* | |
* @exception JavaModelException The resource (the jar) associated with this package fragment root does not exist | |
*/ | |
protected void computeJarChildren(JarPackageFragmentRootInfo info, Vector vChildren) throws JavaModelException { | |
ZipFile jar= null; | |
try { | |
jar= getJar(); | |
Hashtable packageFragToTypes= new Hashtable(); | |
// always create the default package | |
packageFragToTypes.put(IPackageFragment.DEFAULT_PACKAGE_NAME, new Vector[] { new Vector(), new Vector() | |
}); | |
Vector[] temp; | |
for (Enumeration e= jar.entries(); e.hasMoreElements();) { | |
ZipEntry member= (ZipEntry) e.nextElement(); | |
String eName= member.getName(); | |
if (member.isDirectory()) { | |
eName= eName.substring(0, eName.length() - 1); | |
eName= eName.replace('/', '.'); | |
temp= (Vector[]) packageFragToTypes.get(eName); | |
if (temp == null) { | |
temp= new Vector[] { new Vector(), new Vector() | |
}; | |
packageFragToTypes.put(eName, temp); | |
} | |
} else { | |
if (Util.isClassFileName(eName)) { | |
//only interested in class files | |
//store the class file entry name to be cached in the appropriate package fragment | |
//zip entries only use '/' | |
Vector classTemp; | |
int lastSeparator= eName.lastIndexOf('/'); | |
String key= IPackageFragment.DEFAULT_PACKAGE_NAME; | |
String value= eName; | |
if (lastSeparator != -1) { | |
//not in the default package | |
eName= eName.replace('/', '.'); | |
value= eName.substring(lastSeparator + 1); | |
key= eName.substring(0, lastSeparator); | |
} | |
temp= (Vector[]) packageFragToTypes.get(key); | |
if (temp == null) { | |
// build all package fragments in the key | |
lastSeparator= key.indexOf('.'); | |
while (lastSeparator > 0) { | |
String prefix= key.substring(0, lastSeparator); | |
if (packageFragToTypes.get(prefix) == null) { | |
packageFragToTypes.put(prefix, new Vector[] { new Vector(), new Vector() | |
}); | |
} | |
lastSeparator= key.indexOf('.', lastSeparator + 1); | |
} | |
classTemp= new Vector(); | |
classTemp.addElement(value); | |
packageFragToTypes.put(key, new Vector[] {classTemp, new Vector() | |
}); | |
} else { | |
classTemp= temp[0]; | |
classTemp.addElement(value); | |
} | |
} else { | |
Vector resTemp; | |
int lastSeparator= eName.lastIndexOf('/'); | |
String key= IPackageFragment.DEFAULT_PACKAGE_NAME; | |
String value= eName; | |
if (lastSeparator != -1) { | |
//not in the default package | |
eName= eName.replace('/', '.'); | |
key= eName.substring(0, lastSeparator); | |
} | |
temp= (Vector[]) packageFragToTypes.get(key); | |
if (temp == null) { | |
// build all package fragments in the key | |
lastSeparator= key.indexOf('.'); | |
while (lastSeparator > 0) { | |
String prefix= key.substring(0, lastSeparator); | |
if (packageFragToTypes.get(prefix) == null) { | |
packageFragToTypes.put(prefix, new Vector[] { new Vector(), new Vector() | |
}); | |
} | |
lastSeparator= key.indexOf('.', lastSeparator + 1); | |
} | |
resTemp= new Vector(); | |
resTemp.addElement(value); | |
packageFragToTypes.put(key, new Vector[] { new Vector(), resTemp }); | |
} else { | |
resTemp= temp[1]; | |
resTemp.addElement(value); | |
} | |
} | |
} | |
} | |
//loop through all of referenced packages, creating package fragments if necessary | |
// and cache the entry names in the infos created for those package fragments | |
Enumeration packages= packageFragToTypes.keys(); | |
while (packages.hasMoreElements()) { | |
String packName= (String) packages.nextElement(); | |
Vector[] entries= (Vector[]) packageFragToTypes.get(packName); | |
JarPackageFragment packFrag= (JarPackageFragment) getPackageFragment(packName); | |
JarPackageFragmentInfo fragInfo= (JarPackageFragmentInfo) packFrag.createElementInfo(); | |
fragInfo.setEntryNames(entries[0]); | |
int resLength= entries[1].size(); | |
if (resLength == 0) { | |
packFrag.computeNonJavaResources(new String[] {}, fragInfo, jar.getName()); | |
} else { | |
String[] resNames= new String[resLength]; | |
entries[1].copyInto(resNames); | |
packFrag.computeNonJavaResources(resNames, fragInfo, jar.getName()); | |
} | |
packFrag.computeChildren(fragInfo); | |
fgJavaModelManager.putInfo(packFrag, fragInfo); | |
vChildren.addElement(packFrag); | |
} | |
} catch (CoreException e) { | |
throw new JavaModelException(e); | |
} finally { | |
if (jar != null) { | |
try { | |
jar.close(); | |
} catch (IOException e) { | |
// ignore | |
} | |
} | |
} | |
} | |
/** | |
* Returns a new element info for this element. | |
*/ | |
protected OpenableElementInfo createElementInfo() { | |
return new JarPackageFragmentRootInfo(); | |
} | |
/** | |
* A Jar is always K_BINARY. | |
* | |
* @exception NotPresentException if the project and root do | |
* not exist. | |
*/ | |
protected int determineKind(IResource underlyingResource) throws JavaModelException { | |
return IPackageFragmentRoot.K_BINARY; | |
} | |
/** | |
* Returns true if this handle represents the same jar | |
* as the given handle. Two jars are equal if they share | |
* the same zip file. | |
* | |
* @see Object#equals | |
*/ | |
public boolean equals(Object o) { | |
if (this == o) | |
return true; | |
if (o instanceof JarPackageFragmentRoot) { | |
JarPackageFragmentRoot other= (JarPackageFragmentRoot) o; | |
return fJarPath.equals(other.fJarPath); | |
} | |
return false; | |
} | |
public IClasspathEntry findSourceAttachmentRecommendation() { | |
try { | |
IPath rootPath = this.getPath(); | |
IClasspathEntry entry; | |
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); | |
// try on enclosing project first | |
JavaProject parentProject = (JavaProject) getJavaProject(); | |
try { | |
entry = parentProject.getClasspathEntryFor(rootPath); | |
if (entry != null){ | |
Object target = JavaModel.getTarget(workspaceRoot, entry.getSourceAttachmentPath(), true); | |
if (target instanceof IFile){ | |
IFile file = (IFile) target; | |
if ("jar"/*nonNLS*/.equalsIgnoreCase(file.getFileExtension()) || "zip"/*nonNLS*/.equalsIgnoreCase(file.getFileExtension())){ | |
return entry; | |
} | |
} | |
if (target instanceof java.io.File){ | |
java.io.File file = (java.io.File) target; | |
String name = file.getName(); | |
if (Util.endsWithIgnoreCase(name, ".jar"/*nonNLS*/) || Util.endsWithIgnoreCase(name, ".zip"/*nonNLS*/)){ | |
return entry; | |
} | |
} | |
} | |
} catch(JavaModelException e){ | |
} | |
// iterate over all projects | |
IJavaModel model = getJavaModel(); | |
IJavaProject[] jProjects = model.getJavaProjects(); | |
for (int i = 0, max = jProjects.length; i < max; i++){ | |
JavaProject jProject = (JavaProject) jProjects[i]; | |
if (jProject == parentProject) continue; // already done | |
try { | |
entry = jProject.getClasspathEntryFor(rootPath); | |
if (entry != null){ | |
Object target = JavaModel.getTarget(workspaceRoot, entry.getSourceAttachmentPath(), true); | |
if (target instanceof IFile){ | |
IFile file = (IFile) target; | |
String name = file.getName(); | |
if (Util.endsWithIgnoreCase(name, ".jar"/*nonNLS*/) || Util.endsWithIgnoreCase(name, ".zip"/*nonNLS*/)){ | |
return entry; | |
} | |
} | |
if (target instanceof java.io.File){ | |
java.io.File file = (java.io.File) target; | |
String name = file.getName(); | |
if (Util.endsWithIgnoreCase(name, ".jar"/*nonNLS*/) || Util.endsWithIgnoreCase(name, ".zip"/*nonNLS*/)){ | |
return entry; | |
} | |
} | |
} | |
} catch(JavaModelException e){ | |
} | |
} | |
} catch(JavaModelException e){ | |
} | |
return null; | |
} | |
/** | |
* Returns the underlying ZipFile for this Jar package fragment root. | |
* | |
* @exception CoreException if an error occurs accessing the jar | |
*/ | |
public ZipFile getJar() throws CoreException { | |
return fgJavaModelManager.getZipFile(getPath()); | |
} | |
/** | |
* @see IJavaElement | |
*/ | |
public IJavaProject getJavaProject() { | |
IJavaElement parent= getParent(); | |
if (parent == null) { | |
return null; | |
} else { | |
return parent.getJavaProject(); | |
} | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public int getKind() { | |
return IPackageFragmentRoot.K_BINARY; | |
} | |
/** | |
* Returns an array of non-java resources contained in the receiver. | |
*/ | |
public Object[] getNonJavaResources() throws JavaModelException { | |
// We want to show non java resources of the default package at the root (see PR #1G58NB8) | |
return ((JarPackageFragment) this.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME)).storedNonJavaResources(); | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public IPackageFragment getPackageFragment(String packageName) { | |
return new JarPackageFragment(this, packageName); | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public IPath getPath() { | |
if (fResource == null) { | |
return fJarPath; | |
} else { | |
return super.getPath(); | |
} | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public IPath getSourceAttachmentPath() throws JavaModelException { | |
String serverPathString= getSourceAttachmentProperty(); | |
if (serverPathString == null) { | |
return null; | |
} | |
int index= serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER); | |
if (index < 0) return null; | |
String serverZipPathString= serverPathString.substring(0, index); | |
return new Path(serverZipPathString); | |
} | |
/** | |
* Returns the server property for this package fragment root's | |
* source attachement. | |
*/ | |
protected String getSourceAttachmentProperty() throws JavaModelException { | |
String propertyString = null; | |
QualifiedName qName= getSourceAttachmentPropertyName(); | |
try { | |
propertyString = getWorkspace().getRoot().getPersistentProperty(qName); | |
// if no existing source attachment information, then lookup a recommendation from classpath entries | |
if (propertyString == null || propertyString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER) < 0){ | |
IClasspathEntry recommendation = findSourceAttachmentRecommendation(); | |
if (recommendation != null){ | |
propertyString = recommendation.getSourceAttachmentPath().toString() | |
+ ATTACHMENT_PROPERTY_DELIMITER | |
+ (recommendation.getSourceAttachmentRootPath() == null ? ""/*nonNLS*/ : recommendation.getSourceAttachmentRootPath().toString()); | |
getWorkspace().getRoot().setPersistentProperty(qName, propertyString); | |
} | |
} | |
return propertyString; | |
} catch (CoreException ce) { | |
throw new JavaModelException(ce); | |
} | |
} | |
/** | |
* Returns the qualified name for the source attachment property | |
* of this jar. | |
*/ | |
protected QualifiedName getSourceAttachmentPropertyName() throws JavaModelException { | |
ZipFile jarFile = null; | |
try { | |
jarFile = getJar(); | |
return new QualifiedName(JavaCore.PLUGIN_ID, "sourceattachment: "/*nonNLS*/ + jarFile.getName()); | |
} catch (CoreException e) { | |
throw new JavaModelException(e); | |
} finally { | |
try { | |
if (jarFile != null) { | |
jarFile.close(); | |
} | |
} catch(IOException e) { | |
// ignore | |
} | |
} | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public IPath getSourceAttachmentRootPath() throws JavaModelException { | |
String serverPathString= getSourceAttachmentProperty(); | |
if (serverPathString == null) { | |
return null; | |
} | |
int index= serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER); | |
String serverRootPathString= IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH; | |
if (index != serverPathString.length() - 1) { | |
serverRootPathString= serverPathString.substring(index + 1); | |
} | |
return new Path(serverRootPathString); | |
} | |
/** | |
* @see JavaElement | |
*/ | |
public SourceMapper getSourceMapper() { | |
try { | |
return ((JarPackageFragmentRootInfo) getElementInfo()).getSourceMapper(); | |
} catch (JavaModelException e) { | |
return null; | |
} | |
} | |
/** | |
* @see IJavaElement | |
*/ | |
public IResource getUnderlyingResource() throws JavaModelException { | |
if (fResource == null) { | |
return null; | |
} else { | |
return super.getUnderlyingResource(); | |
} | |
} | |
/** | |
* 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; | |
} | |
} | |
public int hashCode() { | |
return fJarPath.hashCode(); | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public boolean isArchive() { | |
return true; | |
} | |
/** | |
* @see IPackageFragmentRoot | |
*/ | |
public boolean isExternal() { | |
return fResource == null; | |
} | |
/** | |
* Jars and jar entries are all read only | |
*/ | |
public boolean isReadOnly() { | |
return true; | |
} | |
/** | |
* @see Openable#openWhenClosed() | |
*/ | |
protected void openWhenClosed(IProgressMonitor pm) throws JavaModelException { | |
super.openWhenClosed(pm); | |
try { | |
//restore any stored attached source zip | |
IPath zipPath= getSourceAttachmentPath(); | |
if (zipPath != null) { | |
IPath rootPath= getSourceAttachmentRootPath(); | |
attachSource(zipPath, rootPath, pm); | |
} | |
} catch(JavaModelException e){ // no attached source | |
} | |
} | |
/** | |
* An archive cannot refresh its children. | |
*/ | |
public void refreshChildren() { | |
// do nothing | |
} | |
/** | |
* Reset the array of non-java resources contained in the receiver to null. | |
*/ | |
public void resetNonJavaResources() throws JavaModelException { | |
((JarPackageFragmentRootInfo) getElementInfo()).setNonJavaResources(null); | |
} | |
/** | |
* @private - for use by <code>AttachSourceOperation</code> only. | |
* Sets the source mapper associated with this jar. | |
*/ | |
public void setSourceMapper(SourceMapper mapper) throws JavaModelException { | |
((JarPackageFragmentRootInfo) getElementInfo()).setSourceMapper(mapper); | |
} | |
/** | |
* Possible failures: <ul> | |
* <li>RELATIVE_PATH - the path supplied to this operation must be | |
* an absolute path | |
* <li>ELEMENT_NOT_PRESENT - the jar supplied to the operation | |
* does not exist | |
* </ul> | |
*/ | |
protected void verifyAttachSource(IPath zipPath) throws JavaModelException { | |
IJavaModelStatus status= null; | |
if (!exists0()) { | |
throw newNotPresentException(); | |
} else if (zipPath != null && !zipPath.isAbsolute()) { | |
throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, zipPath)); | |
} | |
} | |
/** | |
* @see JavaElement#getHandleMemento() | |
*/ | |
public String getHandleMemento(){ | |
StringBuffer buff= new StringBuffer(((JavaElement)getParent()).getHandleMemento()); | |
buff.append(getHandleMementoDelimiter()); | |
buff.append(this.fJarPath.toString()); // 1GEP51U | |
return buff.toString(); | |
} | |
} |