1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell; 27 28import java.util.ArrayList; 29import java.util.Collection; 30import java.util.Collections; 31import java.util.HashMap; 32import java.util.HashSet; 33import java.util.LinkedHashSet; 34import java.util.List; 35import java.util.Map; 36import java.util.Set; 37import java.util.regex.Matcher; 38import java.util.stream.Stream; 39 40import static java.util.stream.Collectors.toList; 41import static jdk.jshell.Util.PREFIX_PATTERN; 42import static jdk.jshell.Util.REPL_PACKAGE; 43import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP; 44 45/** 46 * Maintain relationships between the significant entities: Snippets, 47 * internal snippet index, Keys, etc. 48 * @author Robert Field 49 */ 50final class SnippetMaps { 51 52 private final List<Snippet> keyIndexToSnippet = new ArrayList<>(); 53 private final Set<Snippet> snippets = new LinkedHashSet<>(); 54 private final Map<String, Set<Integer>> dependencies = new HashMap<>(); 55 private final JShell state; 56 57 SnippetMaps(JShell proc) { 58 this.state = proc; 59 } 60 61 void installSnippet(Snippet sn) { 62 if (sn != null && snippets.add(sn)) { 63 if (sn.key() != null) { 64 sn.setId((state.idGenerator != null) 65 ? state.idGenerator.apply(sn, sn.key().index()) 66 : "" + sn.key().index()); 67 setSnippet(sn.key().index(), sn); 68 } 69 } 70 } 71 72 private void setSnippet(int ki, Snippet snip) { 73 while (ki >= keyIndexToSnippet.size()) { 74 keyIndexToSnippet.add(null); 75 } 76 keyIndexToSnippet.set(ki, snip); 77 } 78 79 Snippet getSnippet(Key key) { 80 return getSnippet(key.index()); 81 } 82 83 Snippet getSnippet(int ki) { 84 Snippet sn = getSnippetDeadOrAlive(ki); 85 return (sn != null && !sn.status().isActive()) 86 ? null 87 : sn; 88 } 89 90 Snippet getSnippetDeadOrAlive(int ki) { 91 if (ki >= keyIndexToSnippet.size()) { 92 return null; 93 } 94 return keyIndexToSnippet.get(ki); 95 } 96 97 List<Snippet> snippetList() { 98 return new ArrayList<>(snippets); 99 } 100 101 String packageAndImportsExcept(Set<Key> except, Collection<Snippet> plus) { 102 StringBuilder sb = new StringBuilder(); 103 sb.append("package ").append(REPL_PACKAGE).append(";\n"); 104 for (Snippet si : keyIndexToSnippet) { 105 if (si != null && si.status().isDefined() && (except == null || !except.contains(si.key()))) { 106 sb.append(si.importLine(state)); 107 } 108 } 109 if (plus != null) { 110 plus.stream() 111 .forEach(psi -> sb.append(psi.importLine(state))); 112 } 113 return sb.toString(); 114 } 115 116 List<Snippet> getDependents(Snippet snip) { 117 if (!snip.kind().isPersistent()) { 118 return Collections.emptyList(); 119 } 120 Set<Integer> depset; 121 if (snip.unitName.equals("*")) { 122 // star import 123 depset = new HashSet<>(); 124 for (Set<Integer> as : dependencies.values()) { 125 depset.addAll(as); 126 } 127 } else { 128 depset = dependencies.get(snip.name()); 129 } 130 if (depset == null) { 131 return Collections.emptyList(); 132 } 133 List<Snippet> deps = new ArrayList<>(); 134 for (Integer dss : depset) { 135 Snippet dep = getSnippetDeadOrAlive(dss); 136 if (dep != null) { 137 deps.add(dep); 138 state.debug(DBG_DEP, "Found dependency %s -> %s\n", snip.name(), dep.name()); 139 } 140 } 141 return deps; 142 } 143 144 void mapDependencies(Snippet snip) { 145 addDependencies(snip.declareReferences(), snip); 146 addDependencies(snip.bodyReferences(), snip); 147 } 148 149 private void addDependencies(Collection<String> refs, Snippet snip) { 150 if (refs == null) return; 151 for (String ref : refs) { 152 dependencies.computeIfAbsent(ref, k -> new HashSet<>()) 153 .add(snip.key().index()); 154 state.debug(DBG_DEP, "Added dependency %s -> %s\n", ref, snip.name()); 155 } 156 } 157 158 String fullClassNameAndPackageToClass(String full, String pkg) { 159 Matcher mat = PREFIX_PATTERN.matcher(full); 160 if (mat.lookingAt()) { 161 return full.substring(mat.end()); 162 } 163 state.debug(DBG_DEP, "SM %s %s\n", full, pkg); 164 List<String> klasses = importSnippets() 165 .filter(isi -> !isi.isStar) 166 .map(isi -> isi.fullname) 167 .collect(toList()); 168 for (String k : klasses) { 169 if (k.equals(full)) { 170 return full.substring(full.lastIndexOf(".")+1, full.length()); 171 } 172 } 173 List<String> pkgs = importSnippets() 174 .filter(isi -> isi.isStar) 175 .map(isi -> isi.fullname.substring(0, isi.fullname.lastIndexOf("."))) 176 .collect(toList()); 177 pkgs.add(0, "java.lang"); 178 for (String ipkg : pkgs) { 179 if (!ipkg.isEmpty() && ipkg.equals(pkg)) { 180 return full.substring(pkg.length() + 1); 181 } 182 } 183 return full; 184 } 185 186 /** 187 * Compute the set of imports to prepend to a snippet 188 * @return a stream of the import needed 189 */ 190 private Stream<ImportSnippet> importSnippets() { 191 return state.keyMap.importKeys() 192 .map(key -> (ImportSnippet)getSnippet(key)) 193 .filter(sn -> sn != null && state.status(sn).isDefined()); 194 } 195} 196