Feedback.java revision 3360:8102be8ddff2
172017Scg/* 272017Scg * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 372017Scg * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 472017Scg * 572017Scg * This code is free software; you can redistribute it and/or modify it 672017Scg * under the terms of the GNU General Public License version 2 only, as 772017Scg * published by the Free Software Foundation. Oracle designates this 872017Scg * particular file as subject to the "Classpath" exception as provided 972017Scg * by Oracle in the LICENSE file that accompanied this code. 1072017Scg * 1172017Scg * This code is distributed in the hope that it will be useful, but WITHOUT 1272017Scg * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1372017Scg * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1472017Scg * version 2 for more details (a copy is included in the LICENSE file that 1572017Scg * accompanied this code). 1672017Scg * 1772017Scg * You should have received a copy of the GNU General Public License version 1872017Scg * 2 along with this work; if not, write to the Free Software Foundation, 1972017Scg * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2072017Scg * 2172017Scg * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2272017Scg * or visit www.oracle.com if you need additional information or have any 2372017Scg * questions. 2472017Scg */ 2572017Scg 2672017Scgpackage jdk.internal.jshell.tool; 2772017Scg 2872455Scgimport java.util.ArrayList; 2972455Scgimport java.util.Arrays; 3072455Scgimport java.util.Collection; 3172017Scgimport java.util.EnumSet; 3272017Scgimport java.util.HashMap; 3372017Scgimport java.util.List; 3472017Scgimport java.util.Locale; 3572017Scgimport java.util.Map; 3672017Scgimport java.util.regex.Matcher; 3772017Scgimport java.util.regex.Pattern; 3872017Scgimport static java.util.stream.Collectors.joining; 3972017Scg 4082180Scg/** 4182180Scg * Feedback customization support 4284771Sorion * 4372017Scg * @author Robert Field 4472017Scg */ 4572017Scgclass Feedback { 4672017Scg 4772017Scg // Patern for substituted fields within a customized format string 4872017Scg private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}"); 4972017Scg 5072017Scg // Current mode 5172017Scg private Mode mode = new Mode("", false); // initial value placeholder during start-up 5272017Scg 5372017Scg // Mapping of mode names to mode modes 5472017Scg private final Map<String, Mode> modeMap = new HashMap<>(); 5572017Scg 5672017Scg // Mapping selector enum names to enums 5772017Scg private final Map<String, Selector<?>> selectorMap = new HashMap<>(); 5872017Scg 5972017Scg private static final long ALWAYS = bits(FormatCase.all, FormatAction.all, FormatWhen.all, 6072017Scg FormatResolve.all, FormatUnresolved.all, FormatErrors.all); 6172017Scg private static final long ANY = 0L; 6272017Scg 6372017Scg public boolean shouldDisplayCommandFluff() { 6472017Scg return mode.commandFluff; 6572017Scg } 6672017Scg 6772017Scg public String getPre() { 6872017Scg return mode.format("pre", ANY); 6972017Scg } 7072017Scg 7174763Scg public String getPost() { 7274763Scg return mode.format("post", ANY); 7372017Scg } 7472455Scg 7572455Scg public String getErrorPre() { 7672455Scg return mode.format("errorpre", ANY); 7772017Scg } 7872017Scg 7972017Scg public String getErrorPost() { 8072017Scg return mode.format("errorpost", ANY); 8172017Scg } 8272017Scg 8372017Scg public String format(FormatCase fc, FormatAction fa, FormatWhen fw, 8472017Scg FormatResolve fr, FormatUnresolved fu, FormatErrors fe, 8572017Scg String name, String type, String value, String unresolved, List<String> errorLines) { 8672017Scg return mode.format(fc, fa, fw, fr, fu, fe, 8772017Scg name, type, value, unresolved, errorLines); 8872017Scg } 8972017Scg 9072017Scg public String getPrompt(String nextId) { 9172017Scg return mode.getPrompt(nextId); 9272017Scg } 9384771Sorion 9472017Scg public String getContinuationPrompt(String nextId) { 9572017Scg return mode.getContinuationPrompt(nextId); 9672017Scg } 9772017Scg 9872017Scg public boolean setFeedback(MessageHandler messageHandler, ArgTokenizer at) { 9972017Scg return new Setter(messageHandler, at).setFeedback(); 10072017Scg } 10172017Scg 10272017Scg public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) { 10372017Scg return new Setter(messageHandler, at).setFormat(); 10472017Scg } 10572455Scg 10672017Scg public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) { 10772017Scg return new Setter(messageHandler, at).setNewMode(); 10872017Scg } 10972017Scg 11072017Scg public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) { 11172017Scg return new Setter(messageHandler, at).setPrompt(); 11272017Scg } 11372017Scg 11472017Scg { 11572017Scg for (FormatCase e : EnumSet.allOf(FormatCase.class)) 11672017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 11772017Scg for (FormatAction e : EnumSet.allOf(FormatAction.class)) 11872017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 11972017Scg for (FormatResolve e : EnumSet.allOf(FormatResolve.class)) 12072017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 12172017Scg for (FormatUnresolved e : EnumSet.allOf(FormatUnresolved.class)) 12272017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 12372017Scg for (FormatErrors e : EnumSet.allOf(FormatErrors.class)) 12472017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 12572017Scg for (FormatWhen e : EnumSet.allOf(FormatWhen.class)) 12672017Scg selectorMap.put(e.name().toLowerCase(Locale.US), e); 12772017Scg } 12872017Scg 12972017Scg /** 13072017Scg * Holds all the context of a mode mode 13172017Scg */ 13272017Scg private static class Mode { 13372017Scg 13472017Scg // Name of mode 13572017Scg final String name; 13672017Scg 13772017Scg // Display command verification/information 13874763Scg final boolean commandFluff; 13972017Scg 14072017Scg // Event cases: class, method, expression, ... 14172017Scg final Map<String, List<Setting>> cases; 14272017Scg 14372017Scg String prompt = "\n-> "; 14472017Scg String continuationPrompt = ">> "; 14572017Scg 14672017Scg static class Setting { 14772017Scg final long enumBits; 14872017Scg final String format; 14972017Scg Setting(long enumBits, String format) { 15072017Scg this.enumBits = enumBits; 15172017Scg this.format = format; 15272017Scg } 15372017Scg } 15472017Scg 15572017Scg /** 15672017Scg * Set up an empty mode. 15772017Scg * 15872017Scg * @param name 15972017Scg * @param commandFluff True if should display command fluff messages 16072017Scg */ 16172017Scg Mode(String name, boolean commandFluff) { 16272017Scg this.name = name; 16372017Scg this.commandFluff = commandFluff; 16472017Scg cases = new HashMap<>(); 16572017Scg add("name", new Setting(ALWAYS, "%1$s")); 16672017Scg add("type", new Setting(ALWAYS, "%2$s")); 16772017Scg add("value", new Setting(ALWAYS, "%3$s")); 16872017Scg add("unresolved", new Setting(ALWAYS, "%4$s")); 16972017Scg add("errors", new Setting(ALWAYS, "%5$s")); 17072017Scg add("err", new Setting(ALWAYS, "%6$s")); 17172017Scg 17272017Scg add("errorline", new Setting(ALWAYS, " {err}%n")); 17372017Scg 17472017Scg add("pre", new Setting(ALWAYS, "| ")); 17572017Scg add("post", new Setting(ALWAYS, "%n")); 17672017Scg add("errorpre", new Setting(ALWAYS, "| ")); 17772017Scg add("errorpost", new Setting(ALWAYS, "%n")); 17872017Scg } 17972017Scg 18072017Scg /** 18172017Scg * Set up a copied mode. 18272017Scg * 18372017Scg * @param name 18472017Scg * @param commandFluff True if should display command fluff messages 18572017Scg * @param m Mode to copy, or null for no fresh 18672017Scg */ 18772017Scg Mode(String name, boolean commandFluff, Mode m) { 18872017Scg this.name = name; 18972017Scg this.commandFluff = commandFluff; 19072017Scg cases = new HashMap<>(); 19172017Scg 19272017Scg m.cases.entrySet().stream() 19372017Scg .forEach(fes -> fes.getValue() 19472017Scg .forEach(ing -> add(fes.getKey(), ing))); 19572017Scg 19672017Scg this.prompt = m.prompt; 19772017Scg this.continuationPrompt = m.continuationPrompt; 19872017Scg } 19972017Scg 20072017Scg private boolean add(String field, Setting ing) { 20172017Scg List<Setting> settings = cases.computeIfAbsent(field, k -> new ArrayList<>()); 20272017Scg if (settings == null) { 20372017Scg return false; 20472017Scg } 20572017Scg settings.add(ing); 20672455Scg return true; 20772017Scg } 20872017Scg 20972017Scg void set(String field, 21072017Scg Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw, 21172017Scg Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce, 21272017Scg String format) { 21372017Scg long bits = bits(cc, ca, cw, cr, cu, ce); 21472017Scg set(field, bits, format); 21572017Scg } 21672017Scg 21772017Scg void set(String field, long bits, String format) { 21872017Scg add(field, new Setting(bits, format)); 21972017Scg } 22072017Scg 22172017Scg /** 22272017Scg * Lookup format Replace fields with context specific formats. 22372017Scg * 22472017Scg * @return format string 22572017Scg */ 22672017Scg String format(String field, long bits) { 22772017Scg List<Setting> settings = cases.get(field); 22872017Scg if (settings == null) { 22972017Scg return ""; //TODO error? 23072017Scg } 23172455Scg String format = null; 23272017Scg for (int i = settings.size() - 1; i >= 0; --i) { 23372017Scg Setting ing = settings.get(i); 23472017Scg long mask = ing.enumBits; 23572017Scg if ((bits & mask) == bits) { 23672017Scg format = ing.format; 23772017Scg break; 23872017Scg } 23972455Scg } 24072017Scg if (format == null || format.isEmpty()) { 24172017Scg return ""; 24272455Scg } 24372017Scg Matcher m = FIELD_PATTERN.matcher(format); 24472017Scg StringBuffer sb = new StringBuffer(format.length()); 24572017Scg while (m.find()) { 24672017Scg String fieldName = m.group(1); 24772017Scg String sub = format(fieldName, bits); 24872017Scg m.appendReplacement(sb, Matcher.quoteReplacement(sub)); 24972017Scg } 25072017Scg m.appendTail(sb); 25172017Scg return sb.toString(); 25272017Scg } 25372017Scg 25472017Scg String format(FormatCase fc, FormatAction fa, FormatWhen fw, 25572455Scg FormatResolve fr, FormatUnresolved fu, FormatErrors fe, 25672017Scg String name, String type, String value, String unresolved, List<String> errorLines) { 25772017Scg long bits = bits(fc, fa, fw, fr, fu, fe); 25872017Scg String fname = name==null? "" : name; 25972017Scg String ftype = type==null? "" : type; 26072455Scg String fvalue = value==null? "" : value; 26172017Scg String funresolved = unresolved==null? "" : unresolved; 26272017Scg String errors = errorLines.stream() 26372017Scg .map(el -> String.format( 26472017Scg format("errorline", bits), 26572455Scg fname, ftype, fvalue, funresolved, "*cannot-use-errors-here*", el)) 26672455Scg .collect(joining()); 26772017Scg return String.format( 26872017Scg format("display", bits), 26972017Scg fname, ftype, fvalue, funresolved, errors, "*cannot-use-err-here*"); 27072017Scg } 27172017Scg 27272017Scg void setPrompts(String prompt, String continuationPrompt) { 27372017Scg this.prompt = prompt; 27472017Scg this.continuationPrompt = continuationPrompt; 27572017Scg } 27672017Scg 27772017Scg String getPrompt(String nextId) { 27872017Scg return String.format(prompt, nextId); 27972017Scg } 28072455Scg 28172455Scg String getContinuationPrompt(String nextId) { 28272017Scg return String.format(continuationPrompt, nextId); 28372017Scg } 28472017Scg } 28572017Scg 28672017Scg // Representation of one instance of all the enum values as bits in a long 28772017Scg private static long bits(FormatCase fc, FormatAction fa, FormatWhen fw, 28872017Scg FormatResolve fr, FormatUnresolved fu, FormatErrors fe) { 28972455Scg long res = 0L; 29072017Scg res |= 1 << fc.ordinal(); 29172017Scg res <<= FormatAction.count; 29272017Scg res |= 1 << fa.ordinal(); 29372017Scg res <<= FormatWhen.count; 29472017Scg res |= 1 << fw.ordinal(); 29572455Scg res <<= FormatResolve.count; 29672017Scg res |= 1 << fr.ordinal(); 29772455Scg res <<= FormatUnresolved.count; 29872017Scg res |= 1 << fu.ordinal(); 29972017Scg res <<= FormatErrors.count; 30072017Scg res |= 1 << fe.ordinal(); 30172017Scg return res; 30272017Scg } 30372017Scg 30472017Scg // Representation of a space of enum values as or'edbits in a long 30572017Scg private static long bits(Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw, 30672017Scg Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce) { 30772017Scg long res = 0L; 30872017Scg for (FormatCase fc : cc) 30972017Scg res |= 1 << fc.ordinal(); 31072017Scg res <<= FormatAction.count; 31172017Scg for (FormatAction fa : ca) 31272017Scg res |= 1 << fa.ordinal(); 31372017Scg res <<= FormatWhen.count; 31474763Scg for (FormatWhen fw : cw) 31572017Scg res |= 1 << fw.ordinal(); 31672017Scg res <<= FormatResolve.count; 31772017Scg for (FormatResolve fr : cr) 31872017Scg res |= 1 << fr.ordinal(); 31972017Scg res <<= FormatUnresolved.count; 32084771Sorion for (FormatUnresolved fu : cu) 32172017Scg res |= 1 << fu.ordinal(); 32272017Scg res <<= FormatErrors.count; 32372017Scg for (FormatErrors fe : ce) 32472017Scg res |= 1 << fe.ordinal(); 32572017Scg return res; 32672017Scg } 32772017Scg 32872017Scg interface Selector<E extends Enum<E> & Selector<E>> { 32972455Scg SelectorCollector<E> collector(Setter.SelectorList sl); 33072017Scg String doc(); 33172017Scg } 33272017Scg 33372017Scg /** 33472017Scg * The event cases 33572017Scg */ 33672017Scg public enum FormatCase implements Selector<FormatCase> { 33772017Scg IMPORT("import declaration"), 33872017Scg CLASS("class declaration"), 33972017Scg INTERFACE("interface declaration"), 34072017Scg ENUM("enum declaration"), 34172017Scg ANNOTATION("annotation interface declaration"), 34272017Scg METHOD("method declaration -- note: {type}==parameter-types"), 34372017Scg VARDECL("variable declaration without init"), 34484771Sorion VARINIT("variable declaration with init"), 34572455Scg EXPRESSION("expression -- note: {name}==scratch-variable-name"), 34672017Scg VARVALUE("variable value expression"), 34772017Scg ASSIGNMENT("assign variable"), 34872017Scg STATEMENT("statement"); 34972017Scg String doc; 35072017Scg static final EnumSet<FormatCase> all = EnumSet.allOf(FormatCase.class); 35184771Sorion static final int count = all.size(); 35272455Scg 35372017Scg @Override 35472017Scg public SelectorCollector<FormatCase> collector(Setter.SelectorList sl) { 35572017Scg return sl.cases; 35672017Scg } 35784771Sorion 35872017Scg @Override 35982837Sorion public String doc() { 36072017Scg return doc; 36172017Scg } 36272017Scg 36372017Scg private FormatCase(String doc) { 36472017Scg this.doc = doc; 36572017Scg } 36672017Scg } 36772017Scg 36872017Scg /** 36972017Scg * The event actions 37072017Scg */ 37172017Scg public enum FormatAction implements Selector<FormatAction> { 37272017Scg ADDED("snippet has been added"), 37372017Scg MODIFIED("an existing snippet has been modified"), 37472017Scg REPLACED("an existing snippet has been replaced with a new snippet"), 37572017Scg OVERWROTE("an existing snippet has been overwritten"), 37672017Scg DROPPED("snippet has been dropped"), 37772017Scg USED("snippet was used when it cannot be"); 37872017Scg String doc; 37972017Scg static final EnumSet<FormatAction> all = EnumSet.allOf(FormatAction.class); 38072017Scg static final int count = all.size(); 38172017Scg 38272017Scg @Override 38372017Scg public SelectorCollector<FormatAction> collector(Setter.SelectorList sl) { 38472017Scg return sl.actions; 38572017Scg } 38672017Scg 38772017Scg @Override 38872017Scg public String doc() { 38972017Scg return doc; 39072455Scg } 39172017Scg 39272017Scg private FormatAction(String doc) { 39372017Scg this.doc = doc; 39472017Scg } 39572017Scg } 39672017Scg 39772017Scg /** 39872017Scg * When the event occurs: primary or update 39972017Scg */ 40072017Scg public enum FormatWhen implements Selector<FormatWhen> { 40172017Scg PRIMARY("the entered snippet"), 40272017Scg UPDATE("an update to a dependent snippet"); 40372017Scg String doc; 40472017Scg static final EnumSet<FormatWhen> all = EnumSet.allOf(FormatWhen.class); 40572017Scg static final int count = all.size(); 40672017Scg 40772017Scg @Override 40872017Scg public SelectorCollector<FormatWhen> collector(Setter.SelectorList sl) { 40972017Scg return sl.whens; 41072017Scg } 41172017Scg 41272017Scg @Override 41372017Scg public String doc() { 41472017Scg return doc; 41572017Scg } 41672017Scg 41772017Scg private FormatWhen(String doc) { 41872017Scg this.doc = doc; 41972017Scg } 42072017Scg } 42172017Scg 42272017Scg /** 42372017Scg * Resolution problems 42472017Scg */ 42572017Scg public enum FormatResolve implements Selector<FormatResolve> { 42672017Scg OK("resolved correctly"), 42772017Scg DEFINED("defined despite recoverably unresolved references"), 42872017Scg NOTDEFINED("not defined because of recoverably unresolved references"); 42972017Scg String doc; 43072017Scg static final EnumSet<FormatResolve> all = EnumSet.allOf(FormatResolve.class); 43172017Scg static final int count = all.size(); 43272017Scg 43372017Scg @Override 43472017Scg public SelectorCollector<FormatResolve> collector(Setter.SelectorList sl) { 43572017Scg return sl.resolves; 43672017Scg } 43772017Scg 43872017Scg @Override 43972017Scg public String doc() { 44072017Scg return doc; 44172017Scg } 44274763Scg 44372017Scg private FormatResolve(String doc) { 44472017Scg this.doc = doc; 44572017Scg } 44672017Scg } 44772017Scg 44872017Scg /** 44972017Scg * Count of unresolved references 45072017Scg */ 45172017Scg public enum FormatUnresolved implements Selector<FormatUnresolved> { 45272017Scg UNRESOLVED0("no names are unresolved"), 45372017Scg UNRESOLVED1("one name is unresolved"), 45472017Scg UNRESOLVED2("two or more names are unresolved"); 45572017Scg String doc; 45672017Scg static final EnumSet<FormatUnresolved> all = EnumSet.allOf(FormatUnresolved.class); 45772017Scg static final int count = all.size(); 45872017Scg 45972017Scg @Override 46072017Scg public SelectorCollector<FormatUnresolved> collector(Setter.SelectorList sl) { 46172017Scg return sl.unresolvedCounts; 46272017Scg } 46372017Scg 46472017Scg @Override 46572017Scg public String doc() { 46672017Scg return doc; 46772017Scg } 46872017Scg 46972017Scg private FormatUnresolved(String doc) { 47072017Scg this.doc = doc; 47172455Scg } 47272017Scg } 47372017Scg 47472455Scg /** 47572017Scg * Count of unresolved references 47672455Scg */ 47772017Scg public enum FormatErrors implements Selector<FormatErrors> { 47872017Scg ERROR0("no errors"), 47972017Scg ERROR1("one error"), 48072017Scg ERROR2("two or more errors"); 48172017Scg String doc; 48272017Scg static final EnumSet<FormatErrors> all = EnumSet.allOf(FormatErrors.class); 48372017Scg static final int count = all.size(); 48472017Scg 48572455Scg @Override 48672017Scg public SelectorCollector<FormatErrors> collector(Setter.SelectorList sl) { 48772017Scg return sl.errorCounts; 48872017Scg } 48972017Scg 49072017Scg @Override 49172017Scg public String doc() { 49272017Scg return doc; 49372017Scg } 49472455Scg 49572017Scg private FormatErrors(String doc) { 49672017Scg this.doc = doc; 49772017Scg } 49872017Scg } 49972017Scg 50072017Scg class SelectorCollector<E extends Enum<E> & Selector<E>> { 50172017Scg final EnumSet<E> all; 50272017Scg EnumSet<E> set = null; 50372017Scg SelectorCollector(EnumSet<E> all) { 50472017Scg this.all = all; 50572017Scg } 50672017Scg void add(Object o) { 50772017Scg @SuppressWarnings("unchecked") 50872017Scg E e = (E) o; 50972017Scg if (set == null) { 51072017Scg set = EnumSet.of(e); 51172455Scg } else { 51272017Scg set.add(e); 51372017Scg } 51472017Scg } 51572017Scg 51672017Scg boolean isEmpty() { 51772017Scg return set == null; 51872017Scg } 51972017Scg 52072017Scg EnumSet<E> getSet() { 52172017Scg return set == null 52272017Scg ? all 52372017Scg : set; 52472017Scg } 52572455Scg } 52672017Scg 52772017Scg // Class used to set custom eval output formats 52872017Scg // For both /set format -- Parse arguments, setting custom format, or printing error 52972455Scg private class Setter { 53072017Scg 53172017Scg private final ArgTokenizer at; 53272017Scg private final MessageHandler messageHandler; 53372017Scg boolean valid = true; 53472455Scg 53572017Scg Setter(MessageHandler messageHandler, ArgTokenizer at) { 53672455Scg this.messageHandler = messageHandler; 53772455Scg this.at = at; 53872455Scg } 53972455Scg 54072455Scg void fluff(String format, Object... args) { 54172017Scg messageHandler.fluff(format, args); 54272455Scg } 54372455Scg 54472455Scg void fluffmsg(String messageKey, Object... args) { 54572455Scg messageHandler.fluffmsg(messageKey, args); 54672017Scg } 54772017Scg 54872455Scg void errorat(String messageKey, Object... args) { 54972455Scg Object[] a2 = Arrays.copyOf(args, args.length + 2); 55072017Scg a2[args.length] = at.whole(); 55172017Scg messageHandler.errormsg(messageKey, a2); 55272017Scg } 55372017Scg 55472017Scg // For /set prompt <mode> "<prompt>" "<continuation-prompt>" 55572017Scg boolean setPrompt() { 55672017Scg Mode m = nextMode(); 55772017Scg String prompt = nextFormat(); 55872017Scg String continuationPrompt = nextFormat(); 55972017Scg if (valid) { 56072017Scg m.setPrompts(prompt, continuationPrompt); 56172017Scg } else { 56272017Scg fluffmsg("jshell.msg.see", "/help /set prompt"); 56372455Scg } 56472017Scg return valid; 56572017Scg } 56672017Scg 56772017Scg // For /set newmode <new-mode> [command|quiet [<old-mode>]] 56872017Scg boolean setNewMode() { 56972017Scg String umode = at.next(); 57072017Scg if (umode == null) { 57172017Scg errorat("jshell.err.feedback.expected.new.feedback.mode"); 57272017Scg valid = false; 57372455Scg } 57472017Scg if (modeMap.containsKey(umode)) { 57572017Scg errorat("jshell.err.feedback.expected.mode.name", umode); 57672455Scg valid = false; 57772017Scg } 57872455Scg String[] fluffOpt = at.next("command", "quiet"); 57972455Scg boolean fluff = fluffOpt == null || fluffOpt.length != 1 || "command".equals(fluffOpt[0]); 58072017Scg if (fluffOpt != null && fluffOpt.length != 1) { 58172017Scg errorat("jshell.err.feedback.command.quiet"); 58272455Scg valid = false; 58372017Scg } 58472017Scg Mode om = null; 58572017Scg String omode = at.next(); 58672017Scg if (omode != null) { 58772455Scg om = toMode(omode); 58872455Scg } 58972455Scg if (valid) { 59072017Scg Mode nm = (om != null) 59172017Scg ? new Mode(umode, fluff, om) 59272017Scg : new Mode(umode, fluff); 59372017Scg modeMap.put(umode, nm); 59472017Scg fluffmsg("jshell.msg.feedback.new.mode", nm.name); 59572017Scg } else { 59672455Scg fluffmsg("jshell.msg.see", "/help /set newmode"); 59772017Scg } 59872017Scg return valid; 59972017Scg } 60072017Scg 60172017Scg // For /set feedback <mode> 60272017Scg boolean setFeedback() { 60372017Scg Mode m = nextMode(); 60472017Scg if (valid && m != null) { 60572017Scg mode = m; 60672017Scg fluffmsg("jshell.msg.feedback.mode", mode.name); 60772017Scg } else { 60872455Scg fluffmsg("jshell.msg.see", "/help /set feedback"); 60972017Scg printFeedbackModes(); 61072017Scg } 61172017Scg return valid; 61272455Scg } 61372455Scg 61472017Scg // For /set format <mode> "<format>" <selector>... 61572017Scg boolean setFormat() { 61672017Scg Mode m = nextMode(); 61772017Scg String field = at.next(); 61872017Scg if (field == null || at.isQuoted()) { 61972017Scg errorat("jshell.err.feedback.expected.field"); 62072017Scg valid = false; 62172017Scg } 62272017Scg String format = valid? nextFormat() : null; 62372017Scg String slRaw; 62472017Scg List<SelectorList> slList = new ArrayList<>(); 62572017Scg while (valid && (slRaw = at.next()) != null) { 62672017Scg SelectorList sl = new SelectorList(); 62772017Scg sl.parseSelectorList(slRaw); 62872017Scg slList.add(sl); 62972017Scg } 63072017Scg if (valid) { 63172017Scg if (slList.isEmpty()) { 63272017Scg m.set(field, ALWAYS, format); 63372455Scg } else { 63472455Scg slList.stream() 63572455Scg .forEach(sl -> m.set(field, 63672017Scg sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(), 63772017Scg sl.resolves.getSet(), sl.unresolvedCounts.getSet(), sl.errorCounts.getSet(), 63872017Scg format)); 63972455Scg } 64072017Scg } else { 64172017Scg fluffmsg("jshell.msg.see", "/help /set format"); 64272455Scg } 64372455Scg return valid; 64472017Scg } 64572017Scg 64672017Scg Mode nextMode() { 64772017Scg String umode = at.next(); 64872017Scg return toMode(umode); 64972017Scg } 65072017Scg 65172017Scg Mode toMode(String umode) { 65272455Scg if (umode == null) { 65372017Scg errorat("jshell.err.feedback.expected.mode"); 65472017Scg valid = false; 65572017Scg return null; 65672017Scg } 65772017Scg Mode m = modeMap.get(umode); 65872017Scg if (m != null) { 65972017Scg return m; 66072017Scg } 66172017Scg // Failing an exact match, go searching 66272017Scg Mode[] matches = modeMap.entrySet().stream() 66372455Scg .filter(e -> e.getKey().startsWith(umode)) 66472017Scg .map(e -> e.getValue()) 66572017Scg .toArray(size -> new Mode[size]); 66672017Scg if (matches.length == 1) { 66772017Scg return matches[0]; 66872017Scg } else { 66972017Scg valid = false; 67072017Scg if (matches.length == 0) { 67172017Scg errorat("jshell.err.feedback.does.not.match.mode", umode); 67272017Scg } else { 67372017Scg errorat("jshell.err.feedback.ambiguous.mode", umode); 67472017Scg } 67572017Scg printFeedbackModes(); 67672017Scg return null; 67772017Scg } 67872017Scg } 67972017Scg 68072017Scg void printFeedbackModes() { 68172017Scg fluffmsg("jshell.msg.feedback.mode.following"); 68272017Scg modeMap.keySet().stream() 68372017Scg .forEach(mk -> fluff(" %s", mk)); 68472017Scg } 68572017Scg 68672017Scg // Test if the format string is correctly 68772017Scg final String nextFormat() { 68872017Scg String format = at.next(); 68972017Scg if (format == null) { 69072017Scg errorat("jshell.err.feedback.expected.format"); 69172017Scg valid = false; 69272017Scg return null; 69372017Scg } 69472017Scg if (!at.isQuoted()) { 69572017Scg errorat("jshell.err.feedback.must.be.quoted", format); 69672017Scg valid = false; 69772017Scg return null; 69872017Scg } 69972017Scg return format; 70072017Scg } 70172017Scg 70272017Scg class SelectorList { 70372017Scg 70472017Scg SelectorCollector<FormatCase> cases = new SelectorCollector<>(FormatCase.all); 70572017Scg SelectorCollector<FormatAction> actions = new SelectorCollector<>(FormatAction.all); 70672017Scg SelectorCollector<FormatWhen> whens = new SelectorCollector<>(FormatWhen.all); 70772017Scg SelectorCollector<FormatResolve> resolves = new SelectorCollector<>(FormatResolve.all); 70872017Scg SelectorCollector<FormatUnresolved> unresolvedCounts = new SelectorCollector<>(FormatUnresolved.all); 70972017Scg SelectorCollector<FormatErrors> errorCounts = new SelectorCollector<>(FormatErrors.all); 71072017Scg 71172017Scg final void parseSelectorList(String sl) { 71272017Scg for (String s : sl.split("-")) { 71372017Scg SelectorCollector<?> lastCollector = null; 71472017Scg for (String as : s.split(",")) { 71572017Scg if (!as.isEmpty()) { 71672017Scg Selector<?> sel = selectorMap.get(as); 71772455Scg if (sel == null) { 71872017Scg errorat("jshell.err.feedback.not.a.valid.selector", as, s); 71972455Scg valid = false; 72072455Scg return; 72172017Scg } 72272017Scg SelectorCollector<?> collector = sel.collector(this); 72372017Scg if (lastCollector == null) { 72472017Scg if (!collector.isEmpty()) { 72572017Scg errorat("jshell.err.feedback.multiple.sections", as, s); 72672017Scg valid = false; 72772017Scg return; 72872017Scg } 72972017Scg } else if (collector != lastCollector) { 73072017Scg errorat("jshell.err.feedback.different.selector.kinds", as, s); 73172017Scg valid = false; 73272017Scg return; 73372017Scg } 73472017Scg collector.add(sel); 73572017Scg lastCollector = collector; 73672017Scg } 73772017Scg } 73872017Scg } 73972017Scg } 74072017Scg } 74172017Scg } 74272017Scg} 74372017Scg