Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: f8d1686eebea32780f6d5690722c04a67197eabc (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*******************************************************************************
 * Copyright (c) 2007 - 2018 BEA Systems, Inc 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:
 *    wharley@bea.com - initial API and implementation
 *    Fabian Steeg <steeg@hbz-nrw.de> - Pass automatically provided options to Java 6 processors - https://bugs.eclipse.org/341298
 *******************************************************************************/

package org.eclipse.jdt.internal.apt.pluggable.core.dispatch;

import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toMap;

import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.lang.model.element.Element;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.apt.core.env.Phase;
import org.eclipse.jdt.apt.core.internal.AptCompilationParticipant;
import org.eclipse.jdt.apt.core.internal.AptPlugin;
import org.eclipse.jdt.apt.core.internal.AptProject;
import org.eclipse.jdt.apt.core.internal.generatedfile.FileGenerationResult;
import org.eclipse.jdt.apt.core.util.AptConfig;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
import org.eclipse.jdt.internal.compiler.apt.model.IElementInfo;

/**
 * Implementation of ProcessingEnvironment when running inside IDE.
 * The lifetime of this object corresponds to the lifetime of the
 * {@link IdeAnnotationProcessorManager} that owns it.
 * @see org.eclipse.jdt.internal.compiler.apt.dispatch.BatchProcessingEnvImpl
 */
public abstract class IdeProcessingEnvImpl extends BaseProcessingEnvImpl {
	
	private final IdeAnnotationProcessorManager _dispatchManager;
	private final IJavaProject _javaProject;
	protected final AptProject _aptProject;
	private final boolean _isTestCode;

	public IdeProcessingEnvImpl(IdeAnnotationProcessorManager dispatchManager,
			IJavaProject jproject, Compiler compiler, boolean isTestCode) 
	{
		_dispatchManager = dispatchManager;
		_javaProject = jproject;
		_compiler = compiler;
		this._isTestCode = isTestCode;
		_aptProject = AptPlugin.getAptProject(jproject);
		_filer = new IdeFilerImpl(_dispatchManager, this);
		_messager = new IdeMessagerImpl(_dispatchManager, this);
	}
	
	/* (non-Javadoc)
	 * @see javax.annotation.processing.ProcessingEnvironment#getLocale()
	 */
	@Override
	public Locale getLocale() {
		return Locale.getDefault();
	}

	@Override
	public Map<String, String> getOptions() {
		if (null == _processorOptions) {
			// Java 5 processor options include items on the command line such as -s,
			// -classpath, etc., but Java 6 options only include the options specified
			// with -A, which will have been parsed into key/value pairs with no dash.
			Map<String, String> allOptions = AptConfig.getProcessorOptions(_javaProject, isTestCode());

			// But we make them available as variables in processor options configured by users
			// (e.g. %classpath% in an option value is replaced with the value of -classpath):
			Map<Boolean, List<Entry<String, String>>> isCommandLineOption = allOptions.entrySet().stream()
					.collect(partitioningBy(option -> option.getKey().startsWith("-")));

			Map<String, String> commandLineOptions = isCommandLineOption.get(true).stream()
					.collect(toMap(e -> e.getKey().substring(1), Entry::getValue));

			Map<String, String> processorOptions = isCommandLineOption.get(false).stream()
					.collect(toMap(Entry::getKey, replacePlaceholdersUsing(commandLineOptions)));

			processorOptions.put("phase", getPhase().toString()); //$NON-NLS-1$
			_processorOptions = Collections.unmodifiableMap(processorOptions);
		}
		return _processorOptions;
	}

	private Function<Entry<String, String>, String> replacePlaceholdersUsing(Map<String, String> commandLineOptions) {
		return option -> {
			String variable, replacement, optionValue = option.getValue();
			Matcher placeholder = Pattern.compile("%([^%]+)%").matcher(optionValue);
			if (placeholder.find() && (variable = placeholder.group(1)) != null
					&& (replacement = commandLineOptions.get(variable)) != null) {
				optionValue = optionValue.replace("%" + variable + "%", replacement);
			}
			return optionValue;
		};
	}
	
	public AptProject getAptProject() {
		return _aptProject;
	}
	
	public IJavaProject getJavaProject() {
		return _javaProject;
	}
	
	public IProject getProject() {
		return _javaProject.getProject();
	}

	/**
	 * @return whether this environment supports building or reconciling.
	 */
	public abstract Phase getPhase();

	/**
	 * Get the IFile that contains or represents the specified source element.
	 * If the element is a package, get the IFile corresponding to its
	 * package-info.java file.  If the element is a top-level type, get the
	 * IFile corresponding to its type.  If the element is a nested element
	 * of some sort (nested type, method, etc.) then get the IFile corresponding
	 * to the containing top-level type.
	 * If the element is not a source type at all, then return null.
	 * @param elem
	 * @return may be null
	 */
	public IFile getEnclosingIFile(Element elem) {
		// if this cast fails it could be that a non-Eclipse element got passed in somehow.
		IElementInfo impl = (IElementInfo)elem;
		String name = impl.getFileName();
		if (name == null) {
			return null;
		}
		// The name will be workspace-relative, e.g., /project/src/packages/File.java.
		IFile file = _javaProject.getProject().getParent().getFile(new Path(name));
		return file;
	}

	/**
	 * Inform the environment that a new Java file has been generated.
	 * @param result must be non-null
	 */
	public void addNewUnit(FileGenerationResult result) {
		AptCompilationParticipant.getInstance().addJava6GeneratedFile(result.getFile());
		addNewUnit(_dispatchManager.findCompilationUnit(result.getFile()));
	}
	
	/**
	 * Inform the environment that a new non-Java file has been generated.
	 * This file will not be submitted to a subsequent round of processing in
	 * the current build, even if the file happens to be in a source location
	 * and named with a Java-like name.  However, its dependencies will be
	 * tracked in the same manner as Java files, e.g., it will be deleted
	 * if changes in source cause it to no longer be generated.
	 * @param file must be non-null
	 */
	public void addNewResource(IFile file) {
		AptCompilationParticipant.getInstance().addJava6GeneratedFile(file);
	}

	public boolean currentProcessorSupportsRTTG()
	{
		// Reconcile time type generation is not currently enabled for Java 6 processors
		return false;
	}

	public boolean isTestCode() {
		return _isTestCode;
	}
	public Compiler getCompiler() {
		return _compiler;
	}
}

Back to the top