/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.code;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Function;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.Completer;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.jvm.ModuleNameReader;
import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import static com.sun.tools.javac.code.Kinds.Kind.*;
This class provides operations to locate module definitions
from the source and class files on the paths provided to javac.
This is NOT part of any supported API.
If you write code that depends on this, you do so at your own risk.
This code and its internal interfaces are subject to change or
deletion without notice.
/**
* This class provides operations to locate module definitions
* from the source and class files on the paths provided to javac.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class ModuleFinder {
The context key for the module finder. /** The context key for the module finder. */
protected static final Context.Key<ModuleFinder> moduleFinderKey = new Context.Key<>();
The log to use for verbose output. /** The log to use for verbose output. */
private final Log log;
The symbol table. /** The symbol table. */
private final Symtab syms;
The name table. /** The name table. */
private final Names names;
private final ClassFinder classFinder;
Access to files
/** Access to files
*/
private final JavaFileManager fileManager;
private final JCDiagnostic.Factory diags;
private ModuleNameReader moduleNameReader;
public ModuleNameFromSourceReader moduleNameFromSourceReader;
Get the ModuleFinder instance for this invocation. /** Get the ModuleFinder instance for this invocation. */
public static ModuleFinder instance(Context context) {
ModuleFinder instance = context.get(moduleFinderKey);
if (instance == null)
instance = new ModuleFinder(context);
return instance;
}
Construct a new module finder. /** Construct a new module finder. */
protected ModuleFinder(Context context) {
context.put(moduleFinderKey, this);
names = Names.instance(context);
syms = Symtab.instance(context);
fileManager = context.get(JavaFileManager.class);
log = Log.instance(context);
classFinder = ClassFinder.instance(context);
diags = JCDiagnostic.Factory.instance(context);
}
class ModuleLocationIterator implements Iterator<Set<Location>> {
StandardLocation outer;
Set<Location> next = null;
Iterator<StandardLocation> outerIter = Arrays.asList(
StandardLocation.MODULE_SOURCE_PATH,
StandardLocation.UPGRADE_MODULE_PATH,
StandardLocation.SYSTEM_MODULES,
StandardLocation.MODULE_PATH
).iterator();
Iterator<Set<Location>> innerIter = null;
@Override
public boolean hasNext() {
while (next == null) {
while (innerIter == null || !innerIter.hasNext()) {
if (outerIter.hasNext()) {
outer = outerIter.next();
try {
innerIter = fileManager.listLocationsForModules(outer).iterator();
} catch (IOException e) {
System.err.println("error listing module locations for " + outer + ": " + e); // FIXME
}
} else
return false;
}
if (innerIter.hasNext())
next = innerIter.next();
}
return true;
}
@Override
public Set<Location> next() {
hasNext();
if (next != null) {
Set<Location> result = next;
next = null;
return result;
}
throw new NoSuchElementException();
}
}
ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator();
public ModuleSymbol findModule(Name name) {
return findModule(syms.enterModule(name));
}
public ModuleSymbol findModule(ModuleSymbol msym) {
if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) {
// fill in location
List<ModuleSymbol> list = scanModulePath(msym);
if (list.isEmpty()) {
msym.kind = ERR;
}
}
if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
// fill in module-info
findModuleInfo(msym);
}
return msym;
}
public List<ModuleSymbol> findAllModules() {
List<ModuleSymbol> list = scanModulePath(null);
for (ModuleSymbol msym: list) {
if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
// fill in module-info
findModuleInfo(msym);
}
}
return list;
}
public ModuleSymbol findSingleModule() {
try {
JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE);
JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
JavaFileObject fo = (src_fo == null) ? class_fo
: (class_fo == null) ? src_fo
: classFinder.preferredFileObject(src_fo, class_fo);
ModuleSymbol msym;
if (fo == null) {
msym = syms.unnamedModule;
} else {
msym = readModule(fo);
}
if (msym.patchLocation == null) {
msym.classLocation = StandardLocation.CLASS_OUTPUT;
} else {
msym.patchOutputLocation = StandardLocation.CLASS_OUTPUT;
}
return msym;
} catch (IOException e) {
throw new Error(e); // FIXME
}
}
private ModuleSymbol readModule(JavaFileObject fo) throws IOException {
Name name;
switch (fo.getKind()) {
case SOURCE:
name = moduleNameFromSourceReader.readModuleName(fo);
if (name == null) {
JCDiagnostic diag =
diags.fragment("file.does.not.contain.module");
ClassSymbol errModuleInfo = syms.defineClass(names.module_info, syms.errModule);
throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags);
}
break;
case CLASS:
try {
name = names.fromString(readModuleName(fo));
} catch (BadClassFile | IOException ex) {
//fillIn will report proper errors:
name = names.error;
}
break;
default:
Assert.error();
name = names.error;
break;
}
ModuleSymbol msym = syms.enterModule(name);
msym.module_info.classfile = fo;
if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && name != names.error) {
msym.patchLocation = fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, name.toString());
if (msym.patchLocation != null) {
JavaFileObject patchFO = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), patchFO);
patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), patchFO);
if (patchFO != null) {
msym.module_info.classfile = patchFO;
}
}
}
msym.completer = Completer.NULL_COMPLETER;
classFinder.fillIn(msym.module_info);
return msym;
}
private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile {
if (moduleNameReader == null)
moduleNameReader = new ModuleNameReader();
return moduleNameReader.readModuleName(jfo);
}
private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException {
if (location == null || !fileManager.hasLocation(location))
return null;
return fileManager.getJavaFileForInput(location,
names.module_info.toString(),
kind);
}
private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) {
ListBuffer<ModuleSymbol> results = new ListBuffer<>();
Map<Name, Location> namesInSet = new HashMap<>();
boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
while (moduleLocationIterator.hasNext()) {
Set<Location> locns = (moduleLocationIterator.next());
namesInSet.clear();
for (Location l: locns) {
try {
Name n = names.fromString(fileManager.inferModuleName(l));
if (namesInSet.put(n, l) == null) {
ModuleSymbol msym = syms.enterModule(n);
if (msym.sourceLocation != null || msym.classLocation != null) {
// module has already been found, so ignore this instance
continue;
}
if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) &&
msym.patchLocation == null) {
msym.patchLocation =
fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH,
msym.name.toString());
if (msym.patchLocation != null &&
multiModuleMode &&
fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
msym.patchOutputLocation =
fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
msym.name.toString());
}
}
if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) {
msym.sourceLocation = l;
if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
msym.classLocation =
fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
msym.name.toString());
}
} else {
msym.classLocation = l;
}
if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES ||
moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) {
msym.flags_field |= Flags.SYSTEM_MODULE;
}
if (toFind == null ||
(toFind == msym && (msym.sourceLocation != null || msym.classLocation != null))) {
// Note: cannot return msym directly, because we must finish
// processing this set first
results.add(msym);
}
} else {
log.error(Errors.DuplicateModuleOnPath(
getDescription(moduleLocationIterator.outer), n));
}
} catch (IOException e) {
// skip location for now? log error?
}
}
if (toFind != null && results.nonEmpty())
return results.toList();
}
return results.toList();
}
private void findModuleInfo(ModuleSymbol msym) {
try {
JavaFileObject fo;
fo = getModuleInfoFromLocation(msym.patchOutputLocation, Kind.CLASS);
fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), fo);
fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), fo);
if (fo == null) {
fo = getModuleInfoFromLocation(msym.classLocation, Kind.CLASS);
fo = preferredFileObject(getModuleInfoFromLocation(msym.sourceLocation, Kind.SOURCE), fo);
}
if (fo == null) {
String moduleName = msym.sourceLocation == null && msym.classLocation != null ?
fileManager.inferModuleName(msym.classLocation) : null;
if (moduleName != null) {
msym.module_info.classfile = null;
msym.flags_field |= Flags.AUTOMATIC_MODULE;
} else {
msym.kind = ERR;
}
} else {
msym.module_info.classfile = fo;
msym.module_info.completer = new Symbol.Completer() {
@Override
public void complete(Symbol sym) throws CompletionFailure {
classFinder.fillIn(msym.module_info);
}
@Override
public String toString() {
return "ModuleInfoCompleter";
}
};
}
} catch (IOException e) {
msym.kind = ERR;
}
}
private JavaFileObject preferredFileObject(JavaFileObject fo1, JavaFileObject fo2) {
if (fo1 == null) return fo2;
if (fo2 == null) return fo1;
return classFinder.preferredFileObject(fo1, fo2);
}
Fragment getDescription(StandardLocation l) {
switch (l) {
case MODULE_PATH: return Fragments.LocnModule_path;
case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path;
case SYSTEM_MODULES: return Fragments.LocnSystem_modules;
case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path;
default:
throw new AssertionError();
}
}
public interface ModuleNameFromSourceReader {
public Name readModuleName(JavaFileObject file);
}
}