/*******************************************************************************
* Copyright (c) 2013 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.completions.externaltype;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.springsource.ide.eclipse.commons.completions.CompletionsActivator;
import org.springsource.ide.eclipse.commons.completions.util.Requestor;
/**
* Discover external types from a jar file.
*
* @author Kris De Volder
*/
public class JarTypeDiscovery extends AbstractExternalTypeSource implements ExternalTypeDiscovery {
private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder");
private final File jarFile;
public JarTypeDiscovery(File jarFile) {
this.jarFile = jarFile;
}
@Override
public void getTypes(Requestor<ExternalTypeEntry> requestor) {
//quick and dirty but fast implementation. It doesn't look inside the class
//files but assumes the fq class names can be directly derived from the zip entries.
//it also assumes that all types in file are public (again because not looking inside the .class files).
ZipFile unzipper = null;
try {
unzipper = new ZipFile(jarFile);
Enumeration<? extends ZipEntry> entries = unzipper.entries();
boolean continu = true;
while (entries.hasMoreElements() && continu) {
ZipEntry e = entries.nextElement();
String path = e.getName();
//We are interested in class files that aren't inner or anonymous classes or classes in
// the default package
if (path.endsWith(".class") && !path.contains("$") && path.lastIndexOf('/')>1) {
//TODO: can optimize a little to do less string copying here.
int beg = path.charAt(0)=='/'?1:0;
String fqName = path.substring(beg, path.length()-6/*".class".length()*/);
fqName = fqName.replace('/', '.');
continu = requestor.receive(debug(new ExternalTypeEntry(fqName, getTypeSource())));
}
}
} catch (Exception e) {
CompletionsActivator.log(e);
} finally {
if (unzipper!=null) {
try {
unzipper.close();
} catch (IOException e) {
//ignore
}
}
}
}
private ExternalTypeEntry debug(ExternalTypeEntry externalTypeEntry) {
if (DEBUG) {
System.out.println(externalTypeEntry);
}
return externalTypeEntry;
}
protected ExternalTypeSource getTypeSource() {
return this;
}
@Override
public String toString() {
return "JarTypeDiscovery["+jarFile+"]";
}
@Override
public void addToClassPath(IJavaProject project, IProgressMonitor mon) {
//We assume that this only gets called if the type that triggers this addition
// is not already on the project's classpatht. The caller should ensure this.
try {
IClasspathEntry[] _cpes = project.getRawClasspath();
IClasspathEntry[] cpes = new IClasspathEntry[_cpes.length+1];
System.arraycopy(_cpes, 0, cpes, 0, _cpes.length);
cpes[_cpes.length] = JavaCore.newLibraryEntry(new Path(jarFile.getAbsolutePath()), null, null);
project.setRawClasspath(cpes, mon);
} catch (Exception e) {
CompletionsActivator.log(e);
}
}
}