/*
* 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 jdk.jshell;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static jdk.internal.jshell.remote.RemoteCodes.PREFIX_PATTERN;
import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP;
import static jdk.internal.jshell.remote.RemoteCodes.REPL_PACKAGE;
/**
* Maintain relationships between the significant entities: Snippets,
* internal snippet index, Keys, etc.
* @author Robert Field
*/
final class SnippetMaps {
private final List<Snippet> keyIndexToSnippet = new ArrayList<>();
private final Set<Snippet> snippets = new LinkedHashSet<>();
private final Map<String, Set<Integer>> dependencies = new HashMap<>();
private final JShell state;
SnippetMaps(JShell proc) {
this.state = proc;
}
void installSnippet(Snippet sn) {
if (sn != null && snippets.add(sn)) {
if (sn.key() != null) {
sn.setId((state.idGenerator != null)
? state.idGenerator.apply(sn, sn.key().index())
: "" + sn.key().index());
setSnippet(sn.key().index(), sn);
}
}
}
private void setSnippet(int ki, Snippet snip) {
while (ki >= keyIndexToSnippet.size()) {
keyIndexToSnippet.add(null);
}
keyIndexToSnippet.set(ki, snip);
}
Snippet getSnippet(Key key) {
return getSnippet(key.index());
}
Snippet getSnippet(int ki) {
Snippet sn = getSnippetDeadOrAlive(ki);
return (sn != null && !sn.status().isActive)
? null
: sn;
}
Snippet getSnippetDeadOrAlive(int ki) {
if (ki >= keyIndexToSnippet.size()) {
return null;
}
return keyIndexToSnippet.get(ki);
}
List<Snippet> snippetList() {
return new ArrayList<>(snippets);
}
String packageAndImportsExcept(Set<Key> except, Collection<Snippet> plus) {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(REPL_PACKAGE).append(";\n");
for (Snippet si : keyIndexToSnippet) {
if (si != null && si.status().isDefined && (except == null || !except.contains(si.key()))) {
sb.append(si.importLine(state));
}
}
if (plus != null) {
plus.stream()
.forEach(psi -> sb.append(psi.importLine(state)));
}
return sb.toString();
}
List<Snippet> getDependents(Snippet snip) {
if (!snip.kind().isPersistent) {
return Collections.emptyList();
}
Set<Integer> depset;
if (snip.unitName.equals("*")) {
// star import
depset = new HashSet<>();
for (Set<Integer> as : dependencies.values()) {
depset.addAll(as);
}
} else {
depset = dependencies.get(snip.name());
}
if (depset == null) {
return Collections.emptyList();
}
List<Snippet> deps = new ArrayList<>();
for (Integer dss : depset) {
Snippet dep = getSnippetDeadOrAlive(dss);
if (dep != null) {
deps.add(dep);
state.debug(DBG_DEP, "Found dependency %s -> %s\n", snip.name(), dep.name());
}
}
return deps;
}
void mapDependencies(Snippet snip) {
addDependencies(snip.declareReferences(), snip);
addDependencies(snip.bodyReferences(), snip);
}
private void addDependencies(Collection<String> refs, Snippet snip) {
if (refs == null) return;
for (String ref : refs) {
dependencies.computeIfAbsent(ref, k -> new HashSet<>())
.add(snip.key().index());
state.debug(DBG_DEP, "Added dependency %s -> %s\n", ref, snip.name());
}
}
String fullClassNameAndPackageToClass(String full, String pkg) {
Matcher mat = PREFIX_PATTERN.matcher(full);
if (mat.lookingAt()) {
return full.substring(mat.end());
}
state.debug(DBG_DEP, "SM %s %s\n", full, pkg);
List<String> klasses = importSnippets()
.filter(isi -> !isi.isStar)
.map(isi -> isi.fullname)
.collect(toList());
for (String k : klasses) {
if (k.equals(full)) {
return full.substring(full.lastIndexOf(".")+1, full.length());
}
}
List<String> pkgs = importSnippets()
.filter(isi -> isi.isStar)
.map(isi -> isi.fullname.substring(0, isi.fullname.lastIndexOf(".")))
.collect(toList());
pkgs.add(0, "java.lang");
for (String ipkg : pkgs) {
if (!ipkg.isEmpty() && ipkg.equals(pkg)) {
return full.substring(pkg.length() + 1);
}
}
return full;
}
/**
* Compute the set of imports to prepend to a snippet
* @return a stream of the import needed
*/
private Stream<ImportSnippet> importSnippets() {
return state.keyMap.importKeys()
.map(key -> (ImportSnippet)getSnippet(key))
.filter(sn -> sn != null && state.status(sn).isDefined);
}
}