/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core; import java.util.ArrayList; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; import org.eclipse.jdt.internal.core.util.Util; /** * 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 org.eclipse.jdt.core.IPackageFragmentRoot * @see org.eclipse.jdt.internal.core.JarPackageFragmentRootInfo */ public class JarPackageFragmentRoot extends PackageFragmentRoot { private final static ArrayList EMPTY_LIST= new ArrayList(); /** * The path to the jar file (a workspace relative path if the jar is internal, or an OS path if * the jar is external) */ protected final IPath jarPath; /** * 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(IPath externalJarPath, JavaProject project) { super(null, project); this.jarPath= externalJarPath; } /** * Constructs a package fragment root which is the root of the Java package directory hierarchy * based on a JAR file. */ protected JarPackageFragmentRoot(IResource resource, JavaProject project) { super(resource, project); this.jarPath= resource.getFullPath(); } /** * 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. */ protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws JavaModelException { HashtableOfArrayToObject rawPackageInfo= new HashtableOfArrayToObject(); IJavaElement[] children; ZipFile jar= null; try { IJavaProject project= getJavaProject(); String sourceLevel= project.getOption(JavaCore.COMPILER_SOURCE, true); String compliance= project.getOption(JavaCore.COMPILER_COMPLIANCE, true); jar= getJar(); // always create the default package rawPackageInfo.put(CharOperation.NO_STRINGS, new ArrayList[] { EMPTY_LIST, EMPTY_LIST }); for (Enumeration e= jar.entries(); e.hasMoreElements();) { ZipEntry member= (ZipEntry)e.nextElement(); initRawPackageInfo(rawPackageInfo, member.getName(), member.isDirectory(), sourceLevel, compliance); } // loop through all of referenced packages, creating package fragments if necessary // and cache the entry names in the rawPackageInfo table children= new IJavaElement[rawPackageInfo.size()]; int index= 0; for (int i= 0, length= rawPackageInfo.keyTable.length; i < length; i++) { String[] pkgName= (String[])rawPackageInfo.keyTable[i]; if (pkgName == null) continue; children[index++]= getPackageFragment(pkgName); } } catch (CoreException e) { if (e.getCause() instanceof ZipException) { // not a ZIP archive, leave the children empty Util.log(e, "Invalid ZIP archive: " + toStringWithAncestors()); //$NON-NLS-1$ children= NO_ELEMENTS; } else if (e instanceof JavaModelException) { throw (JavaModelException)e; } else { throw new JavaModelException(e); } } finally { JavaModelManager.getJavaModelManager().closeZipFile(jar); } info.setChildren(children); ((JarPackageFragmentRootInfo)info).rawPackageInfo= rawPackageInfo; return true; } /** * Returns a new element info for this element. */ protected Object createElementInfo() { return new JarPackageFragmentRootInfo(); } /** * A Jar is always K_BINARY. */ protected int determineKind(IResource underlyingResource) { 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 this.jarPath.equals(other.jarPath); } return false; } public String getElementName() { return this.jarPath.lastSegment(); } /** * 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 JavaModelManager.getJavaModelManager().getZipFile(getPath()); } /** * @see IPackageFragmentRoot */ public int getKind() { return IPackageFragmentRoot.K_BINARY; } int internalKind() throws JavaModelException { 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) Object[] defaultPkgResources= ((JarPackageFragment)getPackageFragment(CharOperation.NO_STRINGS)).storedNonJavaResources(); int length= defaultPkgResources.length; if (length == 0) return defaultPkgResources; Object[] nonJavaResources= new Object[length]; for (int i= 0; i < length; i++) { JarEntryResource nonJavaResource= (JarEntryResource)defaultPkgResources[i]; nonJavaResources[i]= nonJavaResource.clone(this); } return nonJavaResources; } public PackageFragment getPackageFragment(String[] pkgName) { return new JarPackageFragment(this, pkgName); } public IPath internalPath() { if (isExternal()) { return this.jarPath; } else { return super.internalPath(); } } public IResource resource(PackageFragmentRoot root) { if (this.resource == null) { // external jar return null; } return super.resource(root); } /** * @see IJavaElement */ public IResource getUnderlyingResource() throws JavaModelException { if (isExternal()) { if (!exists()) throw newNotPresentException(); return null; } else { return super.getUnderlyingResource(); } } public int hashCode() { return this.jarPath.hashCode(); } private void initRawPackageInfo(HashtableOfArrayToObject rawPackageInfo, String entryName, boolean isDirectory, String sourceLevel, String compliance) { int lastSeparator= isDirectory ? entryName.length() - 1 : entryName.lastIndexOf('/'); String[] pkgName= Util.splitOn('/', entryName, 0, lastSeparator); String[] existing= null; int length= pkgName.length; int existingLength= length; while (existingLength >= 0) { existing= (String[])rawPackageInfo.getKey(pkgName, existingLength); if (existing != null) break; existingLength--; } JavaModelManager manager= JavaModelManager.getJavaModelManager(); for (int i= existingLength; i < length; i++) { if (Util.isValidFolderNameForPackage(pkgName[i], sourceLevel, compliance)) { System.arraycopy(existing, 0, existing= new String[i + 1], 0, i); existing[i]= manager.intern(pkgName[i]); rawPackageInfo.put(existing, new ArrayList[] { EMPTY_LIST, EMPTY_LIST }); } else { // non-Java resource folder if (!isDirectory) { ArrayList[] children= (ArrayList[])rawPackageInfo.get(existing); if (children[1/*NON_JAVA*/] == EMPTY_LIST) children[1/*NON_JAVA*/]= new ArrayList(); children[1/*NON_JAVA*/].add(entryName); } return; } } if (isDirectory) return; // add classfile info amongst children ArrayList[] children= (ArrayList[])rawPackageInfo.get(pkgName); if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(entryName)) { if (children[0/*JAVA*/] == EMPTY_LIST) children[0/*JAVA*/]= new ArrayList(); String nameWithoutExtension= entryName.substring(lastSeparator + 1, entryName.length() - 6); children[0/*JAVA*/].add(nameWithoutExtension); } else { if (children[1/*NON_JAVA*/] == EMPTY_LIST) children[1/*NON_JAVA*/]= new ArrayList(); children[1/*NON_JAVA*/].add(entryName); } } /** * @see IPackageFragmentRoot */ public boolean isArchive() { return true; } /** * @see IPackageFragmentRoot */ public boolean isExternal() { return resource() == null; } /** * Jars and jar entries are all read only */ public boolean isReadOnly() { return true; } /** * Returns whether the corresponding resource or associated file exists */ protected boolean resourceExists(IResource underlyingResource) { if (underlyingResource == null) { return JavaModel.getExternalTarget( getPath()/*don't make the path relative as this is an external archive*/, true/*check existence*/) != null; } else { return super.resourceExists(underlyingResource); } } protected void toStringAncestors(StringBuffer buffer) { if (isExternal()) // don't show project as it is irrelevant for external jar files. // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=146615 return; super.toStringAncestors(buffer); } }