blob: 9b20471ab403fa9141eb282e43111eeedb999a30 [file] [log] [blame]
/*
* Copyright (c) 2015, 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.sjavac.comp.dependencies;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.code.Kinds.Kind;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.Dependencies.GraphDependencies;
import com.sun.tools.javac.util.Dependencies.GraphDependencies.CompletionNode;
import com.sun.tools.javac.util.GraphUtils.Node;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.comp.JavaFileObjectWithLocation;
import com.sun.tools.sjavac.comp.PubAPIs;
public class NewDependencyCollector implements TaskListener {
private final Context context;
private final Collection<JavaFileObject> explicitJFOs;
private Map<String, Map<String, Set<String>>> deps;
private Map<String, Map<String, Set<String>>> cpDeps;
public NewDependencyCollector(Context context,
Collection<JavaFileObject> explicitJFOs) {
this.context = context;
this.explicitJFOs = explicitJFOs;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.COMPILATION) {
collectPubApisOfDependencies(context, explicitJFOs);
deps = getDependencies(context, explicitJFOs, false);
cpDeps = getDependencies(context, explicitJFOs, true);
}
}
public Map<String, Map<String, Set<String>>> getDependencies(boolean cp) {
return cp ? cpDeps : deps;
}
private Set<CompletionNode> getDependencyNodes(Context context,
Collection<JavaFileObject> explicitJFOs,
boolean explicits) {
GraphDependencies deps = (GraphDependencies) GraphDependencies.instance(context);
return deps.getNodes()
.stream()
.map(n -> (CompletionNode) n)
.filter(n -> n.getClassSymbol().fullname != null)
.filter(n -> explicits == explicitJFOs.contains(n.getClassSymbol().classfile))
.collect(Collectors.toSet());
}
private void collectPubApisOfDependencies(Context context,
Collection<JavaFileObject> explicitJFOs) {
PubAPIs pubApis = PubAPIs.instance(context);
for (CompletionNode cDepNode : getDependencyNodes(context, explicitJFOs, false)) {
ClassSymbol cs = cDepNode.getClassSymbol().outermostClass();
Location loc = getLocationOf(cs);
// We're completely ignorant of PLATFORM_CLASS_PATH classes
if (loc == StandardLocation.CLASS_PATH || loc == StandardLocation.SOURCE_PATH)
pubApis.visitPubapi(cs);
}
}
private Location getLocationOf(ClassSymbol cs) {
JavaFileObject jfo = cs.outermostClass().classfile;
if (jfo instanceof JavaFileObjectWithLocation) {
return ((JavaFileObjectWithLocation<?>) jfo).getLocation();
}
// jfo is most likely on PLATFORM_CLASS_PATH.
// See notes in SmartFileManager::locWrap
return null;
}
// :Package -> fully qualified class name [from] -> set of fully qualified class names [to]
private Map<String, Map<String, Set<String>>> getDependencies(Context context,
Collection<JavaFileObject> explicitJFOs,
boolean cp) {
Map<String, Map<String, Set<String>>> result = new HashMap<>();
for (CompletionNode cnode : getDependencyNodes(context, explicitJFOs, true)) {
String fqDep = cnode.getClassSymbol().outermostClass().flatname.toString();
String depPkg = Util.pkgNameOfClassName(fqDep);
Map<String, Set<String>> depsForThisClass = result.get(depPkg);
if (depsForThisClass == null) {
result.put(depPkg, depsForThisClass = new HashMap<>());
}
Set<String> fqDeps = depsForThisClass.get(fqDep);
if (fqDeps == null) {
depsForThisClass.put(fqDep, fqDeps = new HashSet<>());
}
for (Node<?,?> depNode : getAllDependencies(cnode)) {
CompletionNode cDepNode = (CompletionNode) depNode;
// Symbol is not regarded to depend on itself.
if (cDepNode == cnode) {
continue;
}
// Skip anonymous classes
if (cDepNode.getClassSymbol().fullname == null) {
continue;
}
if (isSymbolRelevant(cp, cDepNode.getClassSymbol())) {
fqDeps.add(cDepNode.getClassSymbol().outermostClass().flatname.toString());
}
}
// The completion dependency graph is not transitively closed for inheritance relations.
// For sjavac's purposes however, a class depends on it's super super type, so below we
// make sure that we include super types.
for (ClassSymbol cs : allSupertypes(cnode.getClassSymbol())) {
if (isSymbolRelevant(cp, cs)) {
fqDeps.add(cs.outermostClass().flatname.toString());
}
}
}
return result;
}
public boolean isSymbolRelevant(boolean cp, ClassSymbol cs) {
Location csLoc = getLocationOf(cs);
Location relevantLocation = cp ? StandardLocation.CLASS_PATH : StandardLocation.SOURCE_PATH;
return csLoc == relevantLocation;
}
private Set<ClassSymbol> allSupertypes(TypeSymbol t) {
if (t == null || !(t instanceof ClassSymbol)) {
return Collections.emptySet();
}
Set<ClassSymbol> result = new HashSet<>();
ClassSymbol cs = (ClassSymbol) t;
result.add(cs);
result.addAll(allSupertypes(cs.getSuperclass().tsym));
for (Type it : cs.getInterfaces()) {
result.addAll(allSupertypes(it.tsym));
}
return result;
}
private Collection<? extends Node<?, ?>> getAllDependencies(CompletionNode cnode) {
return Stream.of(cnode.getSupportedDependencyKinds())
.flatMap(dk -> cnode.getDependenciesByKind(dk).stream())
.collect(Collectors.toSet());
}
}