| /******************************************************************************* |
| * Copyright (c) 2007 - 2018 BEA Systems, Inc and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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; |
| } |
| } |