blob: 6374e834fcfd8dd098340a8f70e6258c96ff71a5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.core;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
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.core.runtime.IStatus;
import org.eclipse.wst.jsdt.internal.core.util.Util;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IPackageFragment;
import org.eclipse.wst.jsdt.core.IPackageFragmentRoot;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.internal.core.util.HashtableOfArrayToObject;
/**
* 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.wst.jsdt.core.IPackageFragmentRoot
* @see org.eclipse.wst.jsdt.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>IJavaScriptProject</code> and
* does not have an associated <code>IResource</code>.
*/
public 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, Map newElements) throws JavaScriptModelException {
HashtableOfArrayToObject rawPackageInfo = new HashtableOfArrayToObject();
IJavaScriptElement[] children;
ZipFile jar = null;
try {
IJavaScriptProject project = getJavaScriptProject();
String sourceLevel = project.getOption(JavaScriptCore.COMPILER_SOURCE, true);
String compliance = project.getOption(JavaScriptCore.COMPILER_COMPLIANCE, true);
jar = getJar();
// always create the default package
rawPackageInfo.put(CharOperation.NO_STRINGS, new ArrayList[] { EMPTY_LIST, EMPTY_LIST });
// loop through all of referenced packages, creating package fragments if necessary
// and cache the entry names in the rawPackageInfo table
for (Enumeration e= jar.entries(); e.hasMoreElements();) {
ZipEntry member= (ZipEntry) e.nextElement();
initRawPackageInfo(rawPackageInfo, member.getName(), member.isDirectory(), sourceLevel, compliance);
}
children = new IJavaScriptElement[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
Logger.log(IStatus.ERROR, "Invalid ZIP archive: " + toStringWithAncestors()); //$NON-NLS-1$
children = NO_ELEMENTS;
} else if (e instanceof JavaScriptModelException) {
throw (JavaScriptModelException)e;
} else {
throw new JavaScriptModelException(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;
}
/**
* @see IJavaScriptElement
*/
public boolean exists() {
if (isExternal())
return getPath().toFile().exists() && validateOnClasspath().isOK();
return super.exists();
}
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 JavaScriptModelException {
return IPackageFragmentRoot.K_BINARY;
}
/**
* Returns an array of non-java resources contained in the receiver.
*/
public Object[] getNonJavaResources() throws JavaScriptModelException {
// 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);
}
/**
* @see IPackageFragmentRoot
*/
public IPackageFragment getPackageFragment(String packageName) {
return getPackageFragment(Util.splitOn('/', packageName, 0, packageName.length()));
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.internal.core.PackageFragmentRoot#getPath()
*/
public IPath getPath() {
if (isExternal())
return this.jarPath;
return super.getPath();
}
/**
* @see IJavaScriptElement
*/
public IResource getUnderlyingResource() throws JavaScriptModelException {
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.wst.jsdt.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() - 3); // length of dot and expected filename extension
// children[0/*JAVA*/].add(nameWithoutExtension);
if (lastSeparator > 0)
children[0/* JAVA */].add(entryName.substring(lastSeparator+1));
else
children[0/* JAVA */].add(entryName);
} 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;
}
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);
}
}