blob: dcf70a5e7ff0902d2beac0ae6b050fe0e176f5cc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 BEA Systems, 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:
* mkaufman@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer;
/**
* Represents a jar file that contains annotation processor factories.
* The factories are listed in the jar's META-INF/services folder, in
* a file named com.sun.mirror.apt.AnnotationProcessorFactory.
*/
public abstract class JarFactoryContainer extends FactoryContainer
{
/**
* @return a java.io.File. The file is not guaranteed to exist.
*/
public abstract File getJarFile();
@Override
public boolean exists() {
try {
final File jarFile = getJarFile();
if(jarFile == null)
return false;
return getJarFile().exists();
} catch (SecurityException e) {
return false;
}
}
@Override
protected Map<String, String> loadFactoryNames() throws IOException {
return getServiceClassnamesFromJar( getJarFile() );
}
/**
* Given a jar file, get the names of any AnnotationProcessorFactory
* implementations it offers. The information is based on the Sun
* <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">
* Jar Service Provider spec</a>: the jar file contains a META-INF/services
* directory; that directory contains text files named according to the desired
* interfaces; and each file contains the names of the classes implementing
* the specified service. The files may also contain whitespace (which is to
* be ignored). The '#' character indicates the beginning of a line comment,
* also to be ignored. Implied but not stated in the spec is that this routine
* also ignores anything after the first nonwhitespace token on a line.
* @param jar the jar file.
* @return a list, possibly empty, of fully qualified classnames to be instantiated.
*/
protected static Map<String, String> getServiceClassnamesFromJar(File jar) throws IOException
{
Map<String, String> classNames = new LinkedHashMap<String, String>();
JarFile jarFile = null;
try {
jarFile = new JarFile(jar);
for (String serviceName : AUTOLOAD_SERVICES) {
String providerName = "META-INF/services/" + serviceName; //$NON-NLS-1$
// Get the service provider def file out of the jar.
JarEntry provider = jarFile.getJarEntry(providerName);
if (provider == null) {
continue;
}
// Extract classnames from the service provider def file.
InputStream is = jarFile.getInputStream(provider);
readServiceProvider(is, serviceName, classNames);
}
}
finally {
try {if (jarFile != null) jarFile.close();} catch (IOException ioe) {}
}
return classNames;
}
/**
* Read service classnames from a service provider definition.
* @param is an input stream corresponding to a Sun-style service provider
* definition file, e.g., one of the files named in AUTOLOAD_SERVICES.
* @param classNames a list to which the classes named in is will be added.
*/
protected static void readServiceProvider(InputStream is, String serviceName, Map<String, String> classNames) throws IOException {
BufferedReader rd = null;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$
for (String line = rd.readLine(); line != null; line = rd.readLine()) {
// hack off any comments
int iComment = line.indexOf('#');
if (iComment >= 0) {
line = line.substring(0, iComment);
}
// add the first non-whitespace token to the list
final String[] tokens = line.split("\\s", 2); //$NON-NLS-1$
if (tokens[0].length() > 0) {
classNames.put(tokens[0], serviceName);
}
}
rd.close();
}
finally {
if (rd != null) try {rd.close();} catch (IOException ioe) {}
}
}
/** List of jar file entries within META-INF/services that specify autoloadable service providers */
private static final String[] AUTOLOAD_SERVICES = {
AptPlugin.JAVA5_FACTORY_NAME,
AptPlugin.JAVA6_FACTORY_NAME
};
}