SnippetMaps.java revision 3062:15bdc18525ff
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.internal.jshell.remote.RemoteCodes.prefixPattern; 42import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP; 43 44/** 45 * Maintain relationships between the significant entities: Snippets, 46 * internal snippet index, Keys, etc. 47 * @author Robert Field 48 */ 49final class SnippetMaps { 50 51 private String packageName; 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 if (ki >= keyIndexToSnippet.size()) { 85 return null; 86 } 87 return keyIndexToSnippet.get(ki); 88 } 89 90 List<Snippet> snippetList() { 91 return new ArrayList<>(snippets); 92 } 93 94 void setPackageName(String n) { 95 packageName = n; 96 } 97 98 String packageName() { 99 return packageName; 100 } 101 102 String classFullName(Snippet sn) { 103 return packageName + "." + sn.className(); 104 } 105 106 String packageAndImportsExcept(Set<Key> except, Collection<Snippet> plus) { 107 StringBuilder sb = new StringBuilder(); 108 sb.append("package ").append(packageName()).append(";\n"); 109 for (Snippet si : keyIndexToSnippet) { 110 if (si != null && si.status().isDefined && (except == null || !except.contains(si.key()))) { 111 sb.append(si.importLine(state)); 112 } 113 } 114 if (plus != null) { 115 plus.stream() 116 .forEach(psi -> sb.append(psi.importLine(state))); 117 } 118 return sb.toString(); 119 } 120 121 List<Snippet> getDependents(Snippet snip) { 122 if (!snip.kind().isPersistent) { 123 return Collections.emptyList(); 124 } 125 Set<Integer> depset; 126 if (snip.unitName.equals("*")) { 127 // star import 128 depset = new HashSet<>(); 129 for (Set<Integer> as : dependencies.values()) { 130 depset.addAll(as); 131 } 132 } else { 133 depset = dependencies.get(snip.name()); 134 } 135 if (depset == null) { 136 return Collections.emptyList(); 137 } 138 List<Snippet> deps = new ArrayList<>(); 139 for (Integer dss : depset) { 140 Snippet dep = getSnippet(dss); 141 if (dep != null) { 142 deps.add(dep); 143 state.debug(DBG_DEP, "Found dependency %s -> %s\n", snip.name(), dep.name()); 144 } 145 } 146 return deps; 147 } 148 149 void mapDependencies(Snippet snip) { 150 addDependencies(snip.declareReferences(), snip); 151 addDependencies(snip.bodyReferences(), snip); 152 } 153 154 private void addDependencies(Collection<String> refs, Snippet snip) { 155 if (refs == null) return; 156 for (String ref : refs) { 157 dependencies.computeIfAbsent(ref, k -> new HashSet<>()) 158 .add(snip.key().index()); 159 state.debug(DBG_DEP, "Added dependency %s -> %s\n", ref, snip.name()); 160 } 161 } 162 163 String fullClassNameAndPackageToClass(String full, String pkg) { 164 Matcher mat = prefixPattern.matcher(full); 165 if (mat.lookingAt()) { 166 return full.substring(mat.end()); 167 } 168 state.debug(DBG_DEP, "SM %s %s\n", full, pkg); 169 List<String> klasses = importSnippets() 170 .filter(isi -> !isi.isStar) 171 .map(isi -> isi.fullname) 172 .collect(toList()); 173 for (String k : klasses) { 174 if (k.equals(full)) { 175 return full.substring(full.lastIndexOf(".")+1, full.length()); 176 } 177 } 178 List<String> pkgs = importSnippets() 179 .filter(isi -> isi.isStar) 180 .map(isi -> isi.fullname.substring(0, isi.fullname.lastIndexOf("."))) 181 .collect(toList()); 182 pkgs.add(0, "java.lang"); 183 for (String ipkg : pkgs) { 184 if (!ipkg.isEmpty() && ipkg.equals(pkg)) { 185 return full.substring(pkg.length() + 1); 186 } 187 } 188 return full; 189 } 190 191 /** 192 * Compute the set of imports to prepend to a snippet 193 * @return a stream of the import needed 194 */ 195 private Stream<ImportSnippet> importSnippets() { 196 return state.keyMap.importKeys() 197 .map(key -> (ImportSnippet)getSnippet(key)) 198 .filter(sn -> state.status(sn).isDefined); 199 } 200} 201