All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jdt.internal.compiler.batch.FileSystem Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2000, 2024 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contribution for
 *								Bug 440687 - [compiler][batch][null] improve command line option for external annotations
 *     Salesforce - Contribution for
 *								https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2529
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.batch;

import java.io.File;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.NoSuchFileException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.zip.ZipFile;

import javax.lang.model.SourceVersion;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.compiler.env.IModulePathEntry;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.IModuleAwareNameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
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.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.compiler.util.Util;

public class FileSystem implements IModuleAwareNameEnvironment, SuffixConstants {

	// Keep the type as ArrayList and not List as there are clients that are already written to expect ArrayList.
	public static ArrayList EMPTY_CLASSPATH = new ArrayList<>();

	/**
	 * A Classpath, even though an IModuleLocation, can represent a plain
	 * classpath location too. The FileSystem tells the Classpath whether to behave as a module or regular class
	 * path via {@link Classpath#acceptModule(IModule)}.
	 *
	 * Sub types of classpath are responsible for appropriate behavior based on this.
	 */
	public interface Classpath extends IModulePathEntry {
		char[][][] findTypeNames(String qualifiedPackageName, String moduleName);
		NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName);
		NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly);
		boolean isPackage(String qualifiedPackageName, /*@Nullable*/String moduleName);
		default boolean hasModule() { return getModule() != null; }
		default boolean hasCUDeclaringPackage(String qualifiedPackageName, Function pkgNameExtractor) {
			return hasCompilationUnit(qualifiedPackageName, null);
		}
		/**
		 * Return a list of the jar file names defined in the Class-Path section
		 * of the jar file manifest if any, null else. Only ClasspathJar (and
		 * extending classes) instances may return a non-null result.
		 * @param  problemReporter problem reporter with which potential
		 *         misconfiguration issues are raised
		 * @return a list of the jar file names defined in the Class-Path
		 *         section of the jar file manifest if any
		 */
		List fetchLinkedJars(ClasspathSectionProblemReporter problemReporter);
		/**
		 * This method resets the environment. The resulting state is equivalent to
		 * a new name environment without creating a new object.
		 */
		void reset();
		/**
		 * Return a normalized path for file based classpath entries. This is an
		 * absolute path in which file separators are transformed to the
		 * platform-agnostic '/', ending with a '/' for directories. This is an
		 * absolute path in which file separators are transformed to the
		 * platform-agnostic '/', deprived from the '.jar' (resp. '.zip')
		 * extension for jar (resp. zip) files.
		 * @return a normalized path for file based classpath entries
		 */
		char[] normalizedPath();
		/**
		 * Return the path for file based classpath entries. This is an absolute path
		 * ending with a file separator for directories, an absolute path including the '.jar'
		 * (resp. '.zip') extension for jar (resp. zip) files.
		 * @return the path for file based classpath entries
		 */
		String getPath();
		/**
		 * Initialize the entry
		 */
		void initialize() throws IOException;
		/**
		 * Can the current location provide an external annotation file for the given type?
		 * @param qualifiedTypeName type name in qualified /-separated notation.
		 */
		boolean hasAnnotationFileFor(String qualifiedTypeName);
		/**
		 * Accepts to represent a module location with the given module description.
		 */
		public void acceptModule(IModule module);
		public String getDestinationPath();
		Collection getModuleNames(Collection limitModules);
		Collection getModuleNames(Collection limitModules, Function getModule);
		default boolean forbidsExportFrom(String modName) { return false; }
	}
	public interface ClasspathSectionProblemReporter {
		void invalidClasspathSection(String jarFilePath);
		void multipleClasspathSections(String jarFilePath);
	}

	/**
	 * This class is defined how to normalize the classpath entries.
	 * It removes duplicate entries.
	 */
	public static class ClasspathNormalizer {
		/**
		 * Returns the normalized classpath entries (no duplicate).
		 * 

The given classpath entries are FileSystem.Classpath. We check the getPath() in order to find * duplicate entries.

* * @param classpaths the given classpath entries * @return the normalized classpath entries */ public static ArrayList normalize(ArrayList classpaths) { ArrayList normalizedClasspath = new ArrayList<>(); HashSet cache = new HashSet<>(); for (Classpath classpath : classpaths) { if (!cache.contains(classpath)) { normalizedClasspath.add(classpath); cache.add(classpath); } } return normalizedClasspath; } } protected Classpath[] classpaths; // Used only in single-module mode when the module descriptor is // provided via command line. protected IModule module; Set knownFileNames; protected boolean annotationsFromClasspath; // should annotation files be read from the classpath (vs. explicit separate path)? private static HashMap JRT_CLASSPATH_CACHE = null; protected Map moduleLocations = new HashMap<>(); private Consumer nameEnvironmentAnswerListener; // a listener for findType* answers /** Tasks resulting from --add-reads or --add-exports command line options. */ Map moduleUpdates = new HashMap<>(); static boolean isJRE12Plus = false; private boolean hasLimitModules = false; static { try { isJRE12Plus = SourceVersion.valueOf("RELEASE_12") != null; //$NON-NLS-1$ } catch(IllegalArgumentException iae) { // fall back to default } } /* classPathNames is a collection is Strings representing the full path of each class path initialFileNames is a collection is Strings, the trailing '.java' will be removed if its not already. */ public FileSystem(String[] classpathNames, String[] initialFileNames, String encoding) { this(classpathNames, initialFileNames, encoding, null, null); } public FileSystem(String[] classpathNames, String[] initialFileNames, String encoding, String release) { this(classpathNames, initialFileNames, encoding, null, release); } protected FileSystem(String[] classpathNames, String[] initialFileNames, String encoding, Collection limitModules) { this(classpathNames,initialFileNames, encoding, limitModules, null); } protected FileSystem(String[] classpathNames, String[] initialFileNames, String encoding, Collection limitModules, String release) { final int classpathSize = classpathNames.length; this.classpaths = new Classpath[classpathSize]; int counter = 0; this.hasLimitModules = limitModules != null && !limitModules.isEmpty(); for (int i = 0; i < classpathSize; i++) { Classpath classpath = getClasspath(classpathNames[i], encoding, null, null, release); try { classpath.initialize(); for (String moduleName : classpath.getModuleNames(limitModules)) this.moduleLocations.put(moduleName, classpath); this.classpaths[counter++] = classpath; } catch (IOException e) { String error = "Failed to init " + classpath; //$NON-NLS-1$ if (JRTUtil.PROPAGATE_IO_ERRORS) { throw new IllegalStateException(error, e); } else { System.err.println(error); e.printStackTrace(); } } } if (counter != classpathSize) { System.arraycopy(this.classpaths, 0, (this.classpaths = new Classpath[counter]), 0, counter); } initializeKnownFileNames(initialFileNames); } protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annotationsFromClasspath, Set limitedModules) { final int length = paths.length; int counter = 0; this.classpaths = new FileSystem.Classpath[length]; this.hasLimitModules = limitedModules != null && !limitedModules.isEmpty(); for (int i = 0; i < length; i++) { final Classpath classpath = paths[i]; try { classpath.initialize(); for (String moduleName : classpath.getModuleNames(limitedModules)) this.moduleLocations.put(moduleName, classpath); this.classpaths[counter++] = classpath; } catch(InvalidPathException exception) { // JRE 9 could throw an IAE if the linked JAR paths have invalid chars, such as ":" // ignore } catch (NoSuchFileException e) { // we don't warn about inexisting jars (javac does the same as us) // see org.eclipse.jdt.core.tests.compiler.regression.BatchCompilerTest.test017b() } catch (IOException e) { String error = "Failed to init " + classpath; //$NON-NLS-1$ if (JRTUtil.PROPAGATE_IO_ERRORS) { throw new IllegalStateException(error, e); } else { System.err.println(error); e.printStackTrace(); } } } if (counter != length) { // should not happen System.arraycopy(this.classpaths, 0, (this.classpaths = new FileSystem.Classpath[counter]), 0, counter); } initializeModuleLocations(limitedModules); initializeKnownFileNames(initialFileNames); this.annotationsFromClasspath = annotationsFromClasspath; } private void initializeModuleLocations(Set limitedModules) { // First create the mapping of all module/Classpath // since the second iteration of getModuleNames() can't be relied on for // to get the right origin of module if (limitedModules == null) { for (Classpath c : this.classpaths) { for (String moduleName : c.getModuleNames(null)) this.moduleLocations.put(moduleName, c); } } else { Map moduleMap = new HashMap<>(); for (Classpath c : this.classpaths) { for (String moduleName : c.getModuleNames(null)) { moduleMap.put(moduleName, c); } } for (Classpath c : this.classpaths) { for (String moduleName : c.getModuleNames(limitedModules, m -> getModuleFromEnvironment(m.toCharArray()))) { Classpath classpath = moduleMap.get(moduleName); this.moduleLocations.put(moduleName, classpath); } } } } protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annotationsFromClasspath) { this(paths, initialFileNames, annotationsFromClasspath, null); } public static Classpath getClasspath(String classpathName, String encoding, AccessRuleSet accessRuleSet) { return getClasspath(classpathName, encoding, false, accessRuleSet, null, null, null); } public static Classpath getClasspath(String classpathName, String encoding, AccessRuleSet accessRuleSet, Map options, String release) { return getClasspath(classpathName, encoding, false, accessRuleSet, null, options, release); } public static Classpath getJrtClasspath(String jdkHome, String encoding, AccessRuleSet accessRuleSet, Map options) { return new ClasspathJrt(new File(convertPathSeparators(jdkHome)), true, accessRuleSet, null); } public static Classpath getOlderSystemRelease(String jdkHome, String release, AccessRuleSet accessRuleSet) { return isJRE12Plus ? new ClasspathJep247Jdk12(new File(convertPathSeparators(jdkHome)), release, accessRuleSet) : new ClasspathJep247(new File(convertPathSeparators(jdkHome)), release, accessRuleSet); } public static Classpath getClasspath(String classpathName, String encoding, boolean isSourceOnly, AccessRuleSet accessRuleSet, String destinationPath, Map options, String release) { Classpath result = null; File file = new File(convertPathSeparators(classpathName)); if (file.isDirectory()) { if (file.exists()) { result = new ClasspathDirectory(file, encoding, isSourceOnly ? ClasspathLocation.SOURCE : ClasspathLocation.SOURCE | ClasspathLocation.BINARY, accessRuleSet, destinationPath == null || destinationPath == Main.NONE ? destinationPath : // keep == comparison valid convertPathSeparators(destinationPath), options); } } else { int format = Util.archiveFormat(classpathName); if (format == Util.ZIP_FILE) { if (isSourceOnly) { // source only mode result = new ClasspathSourceJar(file, true, accessRuleSet, encoding, destinationPath == null || destinationPath == Main.NONE ? destinationPath : // keep == comparison valid convertPathSeparators(destinationPath)); } else if (destinationPath == null) { // class file only mode if (Util.isJrt(classpathName)) { if (JRT_CLASSPATH_CACHE == null) { JRT_CLASSPATH_CACHE = new HashMap<>(); } else { result = JRT_CLASSPATH_CACHE.get(file); } if (result == null) { result = new ClasspathJrt(file, true, accessRuleSet, null); try { result.initialize(); } catch (IOException e) { // Broken entry, but let clients have it anyway. } JRT_CLASSPATH_CACHE.put(file, result); } } else { result = (release == null) ? new ClasspathJar(file, true, accessRuleSet, null) : new ClasspathMultiReleaseJar(file, true, accessRuleSet, destinationPath, release); } } } else if (format == Util.JMOD_FILE) { return new ClasspathJmod(file, true, accessRuleSet, null); } } return result; } private void initializeKnownFileNames(String[] initialFileNames) { if (initialFileNames == null) { this.knownFileNames = new HashSet<>(0); return; } this.knownFileNames = new HashSet<>(initialFileNames.length * 2); for (int i = initialFileNames.length; --i >= 0;) { File compilationUnitFile = new File(initialFileNames[i]); char[] fileName = null; try { fileName = compilationUnitFile.getCanonicalPath().toCharArray(); } catch (IOException e) { // this should not happen as the file exists continue; } char[] matchingPathName = null; final int lastIndexOf = CharOperation.lastIndexOf('.', fileName); if (lastIndexOf != -1) { fileName = CharOperation.subarray(fileName, 0, lastIndexOf); } CharOperation.replace(fileName, '\\', '/'); boolean globalPathMatches = false; // the most nested path should be the selected one for (Classpath classpath : this.classpaths) { char[] matchCandidate = classpath.normalizedPath(); boolean currentPathMatch = false; if (classpath instanceof ClasspathDirectory && CharOperation.prefixEquals(matchCandidate, fileName)) { currentPathMatch = true; if (matchingPathName == null) { matchingPathName = matchCandidate; } else { if (currentPathMatch) { // we have a second source folder that matches the path of the source file if (matchCandidate.length > matchingPathName.length) { // we want to preserve the shortest possible path matchingPathName = matchCandidate; } } else { // we want to preserve the shortest possible path if (!globalPathMatches && matchCandidate.length < matchingPathName.length) { matchingPathName = matchCandidate; } } } if (currentPathMatch) { globalPathMatches = true; } } } if (matchingPathName == null) { this.knownFileNames.add(new String(fileName)); // leave as is... } else { this.knownFileNames.add(new String(CharOperation.subarray(fileName, matchingPathName.length, fileName.length))); } matchingPathName = null; } } /** TESTS ONLY */ public void scanForModules(Parser parser) { for (Classpath classpath : this.classpaths) { File file = new File(classpath.getPath()); IModule iModule = ModuleFinder.scanForModule(classpath, file, parser, false, null); if (iModule != null) this.moduleLocations.put(String.valueOf(iModule.name()), classpath); } } @Override public void cleanup() { for (Classpath classpath : this.classpaths) classpath.reset(); } private static String convertPathSeparators(String path) { return File.separatorChar == '/' ? path.replace('\\', '/') : path.replace('/', '\\'); } @SuppressWarnings("resource") // don't close classpathEntry.zipFile, which we don't own private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName, boolean asBinaryOnly, /*NonNull*/char[] moduleName) { NameEnvironmentAnswer answer = internalFindClass(qualifiedTypeName, typeName, asBinaryOnly, moduleName); if (this.annotationsFromClasspath && answer != null && answer.getBinaryType() instanceof ClassFileReader) { for (Classpath classpathEntry : this.classpaths) { if (classpathEntry.hasAnnotationFileFor(qualifiedTypeName)) { // in case of 'this.annotationsFromClasspath' we indeed search for .eea entries inside the main zipFile of the entry: ZipFile zip = classpathEntry instanceof ClasspathJar ? ((ClasspathJar) classpathEntry).zipFile : null; boolean shouldClose = false; try { if (zip == null) { zip = ExternalAnnotationDecorator.getAnnotationZipFile(classpathEntry.getPath(), null); shouldClose = true; } answer.setBinaryType(ExternalAnnotationDecorator.create(answer.getBinaryType(), classpathEntry.getPath(), qualifiedTypeName, zip)); return notify(answer); } catch (IOException e) { // ignore broken entry, keep searching } finally { if (shouldClose && zip != null) try { zip.close(); } catch (IOException e) { /* nothing */ } } } } // globally configured (annotationsFromClasspath), but no .eea found, decorate in order to answer NO_EEA_FILE: answer.setBinaryType(new ExternalAnnotationDecorator(answer.getBinaryType(), null)); } return notify(answer); } private NameEnvironmentAnswer notify(NameEnvironmentAnswer answer) { if(answer == null) { return answer; } Consumer listener = this.nameEnvironmentAnswerListener; if(listener != null) { listener.accept(answer); } return answer; } private NameEnvironmentAnswer internalFindClass(String qualifiedTypeName, char[] typeName, boolean asBinaryOnly, /*NonNull*/char[] moduleName) { if (this.knownFileNames.contains(qualifiedTypeName)) return null; // looking for a file which we know was provided at the beginning of the compilation String qualifiedBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class; String qualifiedPackageName = qualifiedTypeName.length() == typeName.length ? Util.EMPTY_STRING : qualifiedBinaryFileName.substring(0, qualifiedTypeName.length() - typeName.length - 1); LookupStrategy strategy = LookupStrategy.get(moduleName); if (strategy == LookupStrategy.Named) { if (this.moduleLocations != null) { // searching for a specific named module: String moduleNameString = String.valueOf(moduleName); Classpath classpath = this.moduleLocations.get(moduleNameString); if (classpath != null) { return classpath.findClass(typeName, qualifiedPackageName, moduleNameString, qualifiedBinaryFileName); } } return null; } String qp2 = File.separatorChar == '/' ? qualifiedPackageName : qualifiedPackageName.replace('/', File.separatorChar); NameEnvironmentAnswer suggestedAnswer = null; if (qualifiedPackageName == qp2) { for (Classpath classpath : this.classpaths) { if (!strategy.matches(classpath, Classpath::hasModule)) continue; NameEnvironmentAnswer answer = classpath.findClass(typeName, qualifiedPackageName, null, qualifiedBinaryFileName, asBinaryOnly); if (answer != null) { if (answer.moduleName() != null && !this.moduleLocations.containsKey(String.valueOf(answer.moduleName()))) continue; // type belongs to an unobservable module if (!answer.ignoreIfBetter()) { if (answer.isBetter(suggestedAnswer)) return answer; } else if (answer.isBetter(suggestedAnswer)) // remember suggestion and keep looking suggestedAnswer = answer; } } } else { String qb2 = qualifiedBinaryFileName.replace('/', File.separatorChar); for (Classpath p : this.classpaths) { if (!strategy.matches(p, Classpath::hasModule)) continue; NameEnvironmentAnswer answer = !(p instanceof ClasspathDirectory) ? p.findClass(typeName, qualifiedPackageName, null, qualifiedBinaryFileName, asBinaryOnly) : p.findClass(typeName, qp2, null, qb2, asBinaryOnly); if (answer != null) { if (answer.moduleName() != null && !this.moduleLocations.containsKey(String.valueOf(answer.moduleName()))) continue; // type belongs to an unobservable module if (!answer.ignoreIfBetter()) { if (answer.isBetter(suggestedAnswer)) return answer; } else if (answer.isBetter(suggestedAnswer)) // remember suggestion and keep looking suggestedAnswer = answer; } } } return suggestedAnswer; } @Override public NameEnvironmentAnswer findType(char[][] compoundName, char[] moduleName) { if (compoundName != null) return findClass( new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1], false, moduleName); return null; } public char[][][] findTypeNames(char[][] packageName) { char[][][] result = null; if (packageName != null) { String qualifiedPackageName = new String(CharOperation.concatWith(packageName, '/')); String qualifiedPackageName2 = File.separatorChar == '/' ? qualifiedPackageName : qualifiedPackageName.replace('/', File.separatorChar); if (qualifiedPackageName == qualifiedPackageName2) { for (Classpath classpath : this.classpaths) { char[][][] answers = classpath.findTypeNames(qualifiedPackageName, null); if (answers != null) { // concat with previous answers if (result == null) { result = answers; } else { int resultLength = result.length; int answersLength = answers.length; System.arraycopy(result, 0, (result = new char[answersLength + resultLength][][]), 0, resultLength); System.arraycopy(answers, 0, result, resultLength, answersLength); } } } } else { for (Classpath p : this.classpaths) { char[][][] answers = !(p instanceof ClasspathDirectory) ? p.findTypeNames(qualifiedPackageName, null) : p.findTypeNames(qualifiedPackageName2, null); if (answers != null) { // concat with previous answers if (result == null) { result = answers; } else { int resultLength = result.length; int answersLength = answers.length; System.arraycopy(result, 0, (result = new char[answersLength + resultLength][][]), 0, resultLength); System.arraycopy(answers, 0, result, resultLength, answersLength); } } } } } return result; } @Override public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, char[] moduleName) { if (typeName != null) return findClass( new String(CharOperation.concatWith(packageName, typeName, '/')), typeName, false, moduleName); return null; } @Override public char[][] getModulesDeclaringPackage(char[][] packageName, char[] moduleName) { String qualifiedPackageName = new String(CharOperation.concatWith(packageName, '/')); String moduleNameString = String.valueOf(moduleName); LookupStrategy strategy = LookupStrategy.get(moduleName); if (strategy == LookupStrategy.Named) { if (this.moduleLocations != null) { // specific search in a given module: Classpath classpath = this.moduleLocations.get(moduleNameString); if (classpath != null) { if (classpath.isPackage(qualifiedPackageName, moduleNameString)) return new char[][] {moduleName}; } } return null; } // search the entire environment and answer which modules declare that package: char[][] allNames = null; boolean hasUnobserable = false; for (Classpath cp : this.classpaths) { if (strategy.matches(cp, Classpath::hasModule)) { if (strategy == LookupStrategy.Unnamed) { // short-cut if (cp.isPackage(qualifiedPackageName, moduleNameString)) return new char[][] { ModuleBinding.UNNAMED }; } else { char[][] declaringModules = cp.getModulesDeclaringPackage(qualifiedPackageName, null); if (declaringModules != null) { if (cp instanceof ClasspathJrt && this.hasLimitModules) { declaringModules = filterModules(declaringModules); hasUnobserable |= declaringModules == null; } if (allNames == null) allNames = declaringModules; else allNames = CharOperation.arrayConcat(allNames, declaringModules); } } } } if (allNames == null && hasUnobserable) return new char[][] { ModuleBinding.UNOBSERVABLE }; return allNames; } private char[][] filterModules(char[][] declaringModules) { char[][] filtered = Arrays.stream(declaringModules).filter(m -> this.moduleLocations.containsKey(new String(m))).toArray(char[][]::new); if (filtered.length == 0) return null; return filtered; } private Parser getParser() { Map opts = new HashMap<>(); opts.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_9); return new Parser( new ProblemReporter(DefaultErrorHandlingPolicies.exitOnFirstError(), new CompilerOptions(opts), new DefaultProblemFactory(Locale.getDefault())), false); } @Override public boolean hasCompilationUnit(char[][] qualifiedPackageName, char[] moduleName, boolean checkCUs) { String qPackageName = String.valueOf(CharOperation.concatWith(qualifiedPackageName, '/')); String moduleNameString = String.valueOf(moduleName); LookupStrategy strategy = LookupStrategy.get(moduleName); Parser parser = checkCUs ? getParser() : null; Function pkgNameExtractor = sourceUnit -> { String pkgName = null; CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, 1); char[][] name = parser.parsePackageDeclaration(sourceUnit.getContents(), compilationResult); if (name != null) { pkgName = CharOperation.toString(name); } return pkgName; }; switch (strategy) { case Named: if (this.moduleLocations != null) { Classpath location = this.moduleLocations.get(moduleNameString); if (location != null) return checkCUs ? location.hasCUDeclaringPackage(qPackageName, pkgNameExtractor) : location.hasCompilationUnit(qPackageName, moduleNameString); } return false; default: for (Classpath location : this.classpaths) { if (strategy.matches(location, Classpath::hasModule)) if (location.hasCompilationUnit(qPackageName, moduleNameString)) return true; } return false; } } @Override public IModule getModule(char[] name) { if (this.module != null && CharOperation.equals(name, this.module.name())) { return this.module; } if (this.moduleLocations.containsKey(new String(name))) { for (Classpath classpath : this.classpaths) { IModule mod = classpath.getModule(name); if (mod != null) { return mod; } } } return null; } public IModule getModuleFromEnvironment(char[] name) { if (this.module != null && CharOperation.equals(name, this.module.name())) { return this.module; } for (Classpath classpath : this.classpaths) { IModule mod = classpath.getModule(name); if (mod != null) { return mod; } } return null; } @Override public char[][] getAllAutomaticModules() { Set set = new HashSet<>(); for (Classpath classpath : this.classpaths) { if (classpath.isAutomaticModule()) { set.add(classpath.getModule().name()); } } return set.toArray(new char[set.size()][]); } @Override public char[][] listPackages(char[] moduleName) { switch (LookupStrategy.get(moduleName)) { case Named: Classpath classpath = this.moduleLocations.get(new String(moduleName)); if (classpath != null) return classpath.listPackages(); return CharOperation.NO_CHAR_CHAR; default: throw new UnsupportedOperationException("can list packages only of a named module"); //$NON-NLS-1$ } } void addModuleUpdate(String moduleName, Consumer 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); } @Override public void applyModuleUpdates(IUpdatableModule compilerModule, IUpdatableModule.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 update : updates.getList(kind, false)) update.accept(compilerModule); } } } /** * @param nameEnvironmentAnswerListener * a listener for {@link NameEnvironmentAnswer} returned by findType* methods; useful for * tracking used/answered dependencies during compilation (may be null to unset) * @return a previously set listener (may be null) */ public Consumer setNameEnvironmentAnswerListener(Consumer nameEnvironmentAnswerListener) { Consumer existing = this.nameEnvironmentAnswerListener; this.nameEnvironmentAnswerListener = nameEnvironmentAnswerListener; return existing; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy