Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2a4858daae16a80b6d4407070b19816283ec5a7b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*******************************************************************************
 * Copyright (c) 2005, 2015 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<>();
        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);
            }
        } catch (IOException ioe) {
        	AptPlugin.log(ioe, Messages.AnnotationProcessorFactoryLoader_ioError + jar.getAbsolutePath());
        } 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
    };
	
}

Back to the top