Copyright (c) 2017, 2018 GK Software AG, 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: Stephan Herrmann - initial API and implementation
/******************************************************************************* * Copyright (c) 2017, 2018 GK Software AG, 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: * Stephan Herrmann - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.internal.core; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IStatus; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IModuleDescription; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.env.IModuleAwareNameEnvironment; import org.eclipse.jdt.internal.compiler.env.IUpdatableModule; import org.eclipse.jdt.internal.compiler.env.IUpdatableModule.UpdateKind; import org.eclipse.jdt.internal.compiler.env.IUpdatableModule.UpdatesByKind; import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding; import org.eclipse.jdt.internal.core.util.Util;
An instance of this class collects add-exports and add-reads options from a project's class path entries, and performs the corresponding updates when requested by the compiler.

For patch-module and limit-modules see org.eclipse.jdt.internal.core.builder.ModuleEntryProcessor.

/** * An instance of this class collects <code>add-exports</code> and <code>add-reads</code> options from * a project's class path entries, and performs the corresponding updates when requested by the compiler. * <p>For <code>patch-module</code> and <code>limit-modules</code> see * org.eclipse.jdt.internal.core.builder.ModuleEntryProcessor.</p> */
public class ModuleUpdater { private JavaProject javaProoject; private Map<String,UpdatesByKind> moduleUpdates = new HashMap<>(); public ModuleUpdater(JavaProject javaProject) { this.javaProoject = javaProject; }
Detects any ADD_EXPORTS or ADD_READS classpath attributes, parses the value, and collects the resulting module updates.
Params:
  • entry – a classpath entry of the current project.
Throws:
/** * Detects any ADD_EXPORTS or ADD_READS classpath attributes, parses the value, * and collects the resulting module updates. * @param entry a classpath entry of the current project. * @throws JavaModelException */
public void computeModuleUpdates(IClasspathEntry entry) throws JavaModelException { for (IClasspathAttribute attribute : entry.getExtraAttributes()) { String attributeName = attribute.getName(); String values = attribute.getValue(); // the attributes considered here may have multiple values separated by ':' if (attributeName.equals(IClasspathAttribute.ADD_EXPORTS)) { for (String value : values.split(":")) { // format: <source-module>/<package>=<target-module>(,<target-module>)* //$NON-NLS-1$ int slash = value.indexOf('/'); int equals = value.indexOf('='); if (slash != -1 && equals != -1) { String modName = value.substring(0, slash); char[] packName = value.substring(slash+1, equals).toCharArray(); char[][] targets = CharOperation.splitOn(',', value.substring(equals+1).toCharArray()); addModuleUpdate(modName, new IUpdatableModule.AddExports(packName, targets), UpdateKind.PACKAGE); } else { Util.log(IStatus.WARNING, "Invalid argument to add-exports: "+value); //$NON-NLS-1$ } } } else if (attributeName.equals(IClasspathAttribute.ADD_READS)) { for (String value : values.split(":")) { // format: <source-module>=<target-module> //$NON-NLS-1$ int equals = value.indexOf('='); if (equals != -1) { String srcMod = value.substring(0, equals); char[] targetMod = value.substring(equals+1).toCharArray(); addModuleUpdate(srcMod, new IUpdatableModule.AddReads(targetMod), UpdateKind.MODULE); } else { Util.log(IStatus.WARNING, "Invalid argument to add-reads: "+value); //$NON-NLS-1$ } } } else if (attributeName.equals(IClasspathAttribute.MODULE_MAIN_CLASS)) { IModuleDescription module = this.javaProoject.getModuleDescription(); if (module == null) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); addModuleUpdate(module.getElementName(), m -> m.setMainClassName(values.toCharArray()), UpdateKind.MODULE); } } } public void addModuleUpdate(String moduleName, Consumer<IUpdatableModule> update, UpdateKind kind) { UpdatesByKind updates = this.moduleUpdates.get(moduleName); if (updates == null) { this.moduleUpdates.put(moduleName, updates = new UpdatesByKind()); } updates.getList(kind, true).add(update); }
See Also:
  • applyModuleUpdates.applyModuleUpdates(IUpdatableModule, UpdateKind)
/** * @see IModuleAwareNameEnvironment#applyModuleUpdates(IUpdatableModule, UpdateKind) */
public void applyModuleUpdates(IUpdatableModule compilerModule, UpdateKind kind) { char[] name = compilerModule.name(); if (name != ModuleBinding.UNNAMED) { // can't update the unnamed module UpdatesByKind updates = this.moduleUpdates.get(String.valueOf(name)); if (updates != null) { for (Consumer<IUpdatableModule> update : updates.getList(kind, false)) update.accept(compilerModule); } } } private static boolean containsNonModularDependency(IClasspathEntry[] entries) { for (IClasspathEntry e : entries) { if (e.getEntryKind() != IClasspathEntry.CPE_SOURCE && !((ClasspathEntry) e).isModular()) return true; } return false; } // Bug 520713: allow test code to access code on the classpath public void addReadUnnamedForNonEmptyClasspath(JavaProject project, IClasspathEntry[] expandedClasspath) throws JavaModelException { for (String moduleName : determineModulesOfProjectsWithNonEmptyClasspath(project, expandedClasspath)) { addModuleUpdate(moduleName, m -> m.addReads(ModuleBinding.ALL_UNNAMED), UpdateKind.MODULE); } } public static Set<String> determineModulesOfProjectsWithNonEmptyClasspath(JavaProject project, IClasspathEntry[] expandedClasspath) throws JavaModelException { LinkedHashSet<String> list = new LinkedHashSet<>(); if (containsNonModularDependency(expandedClasspath)) { IModuleDescription moduleDescription = project.getModuleDescription(); if (moduleDescription != null) { list.add(moduleDescription.getElementName()); } } for (IClasspathEntry e1 : expandedClasspath) { if (e1.getEntryKind() == IClasspathEntry.CPE_PROJECT) { Object target = JavaModel.getTarget(e1.getPath(), true); if (target instanceof IProject) { IProject prereqProject = (IProject) target; if (JavaProject.hasJavaNature(prereqProject)) { JavaProject prereqJavaProject = (JavaProject) JavaCore.create(prereqProject); if (containsNonModularDependency(prereqJavaProject.getResolvedClasspath())) { IModuleDescription prereqModuleDescription = prereqJavaProject.getModuleDescription(); if (prereqModuleDescription != null) { list.add(prereqModuleDescription.getElementName()); } } } } } } return list; } public UpdatesByKind getUpdates(String moduleName) { return this.moduleUpdates.get(moduleName); } }