langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java
changeset 38539 71874886920f
parent 38531 c449daa25b45
child 40600 3874367e6e6f
equal deleted inserted replaced
38538:8bdc63ff6961 38539:71874886920f
    53     private static final String TRUNCATION_FIELD = "<truncation>";
    53     private static final String TRUNCATION_FIELD = "<truncation>";
    54 
    54 
    55     // For encoding to Properties String
    55     // For encoding to Properties String
    56     private static final String RECORD_SEPARATOR = "\u241E";
    56     private static final String RECORD_SEPARATOR = "\u241E";
    57 
    57 
    58     // Current mode
    58     // Current mode -- initial value is placeholder during start-up
    59     private Mode mode = new Mode("", false); // initial value placeholder during start-up
    59     private Mode mode = new Mode("");
       
    60 
       
    61     // Retained current mode -- for checks
       
    62     private Mode retainedCurrentMode = null;
    60 
    63 
    61     // Mapping of mode name to mode
    64     // Mapping of mode name to mode
    62     private final Map<String, Mode> modeMap = new HashMap<>();
    65     private final Map<String, Mode> modeMap = new HashMap<>();
    63 
    66 
    64     // Mapping of mode names to encoded retained mode
    67     // Mapping of mode names to encoded retained mode
   116 
   119 
   117     public boolean setTruncation(MessageHandler messageHandler, ArgTokenizer at) {
   120     public boolean setTruncation(MessageHandler messageHandler, ArgTokenizer at) {
   118         return new Setter(messageHandler, at).setTruncation();
   121         return new Setter(messageHandler, at).setTruncation();
   119     }
   122     }
   120 
   123 
   121     public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) {
   124     public boolean setMode(MessageHandler messageHandler, ArgTokenizer at) {
   122         return new Setter(messageHandler, at).setNewMode();
   125         return new Setter(messageHandler, at).setMode();
   123     }
   126     }
   124 
   127 
   125     public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) {
   128     public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) {
   126         return new Setter(messageHandler, at).setPrompt();
   129         return new Setter(messageHandler, at).setPrompt();
   127     }
   130     }
   133     public String retainMode(MessageHandler messageHandler, ArgTokenizer at) {
   136     public String retainMode(MessageHandler messageHandler, ArgTokenizer at) {
   134         return new Setter(messageHandler, at).retainMode();
   137         return new Setter(messageHandler, at).retainMode();
   135     }
   138     }
   136 
   139 
   137     public boolean restoreEncodedModes(MessageHandler messageHandler, String encoded) {
   140     public boolean restoreEncodedModes(MessageHandler messageHandler, String encoded) {
   138         return new Setter(messageHandler, new ArgTokenizer("")).restoreEncodedModes(encoded);
   141         return new Setter(messageHandler, new ArgTokenizer("<init>", "")).restoreEncodedModes(encoded);
   139     }
   142     }
   140 
   143 
   141     public void markModesReadOnly() {
   144     public void markModesReadOnly() {
   142         modeMap.values().stream()
   145         modeMap.values().stream()
   143                 .forEach(m -> m.readOnly = true);
   146                 .forEach(m -> m.readOnly = true);
   165 
   168 
   166         // Name of mode
   169         // Name of mode
   167         final String name;
   170         final String name;
   168 
   171 
   169         // Display command verification/information
   172         // Display command verification/information
   170         final boolean commandFluff;
   173         boolean commandFluff;
   171 
   174 
   172         // Event cases: class, method, expression, ...
   175         // Event cases: class, method, expression, ...
   173         final Map<String, List<Setting>> cases;
   176         final Map<String, List<Setting>> cases;
   174 
   177 
   175         boolean readOnly = false;
   178         boolean readOnly = false;
   190          * Set up an empty mode.
   193          * Set up an empty mode.
   191          *
   194          *
   192          * @param name
   195          * @param name
   193          * @param commandFluff True if should display command fluff messages
   196          * @param commandFluff True if should display command fluff messages
   194          */
   197          */
   195         Mode(String name, boolean commandFluff) {
   198         Mode(String name) {
   196             this.name = name;
   199             this.name = name;
   197             this.commandFluff = commandFluff;
   200             this.cases = new HashMap<>();
   198             cases = new HashMap<>();
       
   199             add("name",       new Setting(ALWAYS, "%1$s"));
   201             add("name",       new Setting(ALWAYS, "%1$s"));
   200             add("type",       new Setting(ALWAYS, "%2$s"));
   202             add("type",       new Setting(ALWAYS, "%2$s"));
   201             add("value",      new Setting(ALWAYS, "%3$s"));
   203             add("value",      new Setting(ALWAYS, "%3$s"));
   202             add("unresolved", new Setting(ALWAYS, "%4$s"));
   204             add("unresolved", new Setting(ALWAYS, "%4$s"));
   203             add("errors",     new Setting(ALWAYS, "%5$s"));
   205             add("errors",     new Setting(ALWAYS, "%5$s"));
   213 
   215 
   214         /**
   216         /**
   215          * Set up a copied mode.
   217          * Set up a copied mode.
   216          *
   218          *
   217          * @param name
   219          * @param name
   218          * @param commandFluff True if should display command fluff messages
       
   219          * @param m Mode to copy, or null for no fresh
   220          * @param m Mode to copy, or null for no fresh
   220          */
   221          */
   221         Mode(String name, boolean commandFluff, Mode m) {
   222         Mode(String name, Mode m) {
   222             this.name = name;
   223             this.name = name;
   223             this.commandFluff = commandFluff;
   224             this.commandFluff = m.commandFluff;
   224             cases = new HashMap<>();
   225             this.prompt = m.prompt;
   225 
   226             this.continuationPrompt = m.continuationPrompt;
       
   227             this.cases = new HashMap<>();
   226             m.cases.entrySet().stream()
   228             m.cases.entrySet().stream()
   227                     .forEach(fes -> fes.getValue()
   229                     .forEach(fes -> fes.getValue()
   228                     .forEach(ing -> add(fes.getKey(), ing)));
   230                     .forEach(ing -> add(fes.getKey(), ing)));
   229 
   231 
   230             this.prompt = m.prompt;
       
   231             this.continuationPrompt = m.continuationPrompt;
       
   232         }
   232         }
   233 
   233 
   234         /**
   234         /**
   235          * Set up a mode reconstituted from a preferences string.
   235          * Set up a mode reconstituted from a preferences string.
   236          *
   236          *
   254                     Setting ing = new Setting(Long.parseLong(bits), format);
   254                     Setting ing = new Setting(Long.parseLong(bits), format);
   255                     settings.add(ing);
   255                     settings.add(ing);
   256                 }
   256                 }
   257                 cases.put(field, settings);
   257                 cases.put(field, settings);
   258             }
   258             }
       
   259         }
       
   260 
       
   261         /**
       
   262          * Set if this mode displays informative/confirmational messages on
       
   263          * commands.
       
   264          *
       
   265          * @param fluff the value to set
       
   266          */
       
   267         void setCommandFluff(boolean fluff) {
       
   268             commandFluff = fluff;
   259         }
   269         }
   260 
   270 
   261         /**
   271         /**
   262          * Encodes the mode into a String so it can be saved in Preferences.
   272          * Encodes the mode into a String so it can be saved in Preferences.
   263          *
   273          *
   680                 fluffmsg("jshell.msg.see", "/help /set prompt");
   690                 fluffmsg("jshell.msg.see", "/help /set prompt");
   681             }
   691             }
   682             return valid;
   692             return valid;
   683         }
   693         }
   684 
   694 
   685         // For /set newmode <new-mode> [-command|-quiet [<old-mode>]]
   695         /**
   686         boolean setNewMode() {
   696          * Set mode. Create, changed, or delete a feedback mode. For @{code /set
   687             String umode = at.next();
   697          * mode <mode> [<old-mode>] [-command|-quiet|-delete]}.
   688             if (umode == null || !at.isIdentifier()) {
   698          *
   689                 errorat("jshell.err.feedback.expected.new.feedback.mode");
   699          * @return true if successful
   690                 valid = false;
   700          */
   691             }
   701         boolean setMode() {
   692             if (modeMap.containsKey(umode)) {
   702             at.allowedOptions("-command", "-quiet", "-delete");
   693                 errorat("jshell.err.feedback.expected.mode.name", umode);
   703             String umode = nextModeIdentifier();
   694                 valid = false;
       
   695             }
       
   696             String[] fluffOpt = at.next("-command", "-quiet");
       
   697             boolean fluff = fluffOpt == null || fluffOpt.length != 1 || "-command".equals(fluffOpt[0]);
       
   698             if (fluffOpt != null && fluffOpt.length != 1) {
       
   699                 errorat("jshell.err.feedback.command.quiet");
       
   700                 valid = false;
       
   701             }
       
   702             Mode om = null;
   704             Mode om = null;
   703             String omode = at.next();
   705             String omode = at.next();
   704             if (omode != null) {
   706             if (valid && omode != null) {
   705                 om = toMode(omode);
   707                 om = toMode(omode);
   706             }
   708             }
       
   709             checkOptionsAndRemainingInput();
       
   710             boolean commandOption = at.hasOption("-command");
       
   711             boolean quietOption = at.hasOption("-quiet");
       
   712             boolean deleteOption = at.hasOption("-delete");
       
   713             // Only one (or zero) of the options can be used
       
   714             if (valid && at.optionCount() > 1) {
       
   715                 errorat("jshell.err.conflicting.options");
       
   716                 valid = false;
       
   717             }
   707             if (valid) {
   718             if (valid) {
   708                 Mode nm = (om != null)
   719                 Mode m = modeMap.get(umode);
   709                         ? new Mode(umode, fluff, om)
   720                 if (m != null && m.readOnly) {
   710                         : new Mode(umode, fluff);
   721                     // Cannot make changes to a the built-in modes
   711                 modeMap.put(umode, nm);
   722                     errorat("jshell.err.not.valid.with.predefined.mode", m.name);
   712                 fluffmsg("jshell.msg.feedback.new.mode", nm.name);
   723                     valid = false;
   713             } else {
   724                 } else if (deleteOption) {
   714                 fluffmsg("jshell.msg.see", "/help /set newmode");
   725                     if (m == null) {
       
   726                         // Cannot delete a mode that does not exist
       
   727                         errorat("jshell.err.mode.unknown", umode);
       
   728                         valid = false;
       
   729                     } else if (mode.name.equals(m.name)) {
       
   730                         // Cannot delete the current mode out from under us
       
   731                         errorat("jshell.err.cannot.delete.current.mode", umode);
       
   732                         valid = false;
       
   733                     } else {
       
   734                         // Remove the mode
       
   735                         modeMap.remove(umode);
       
   736                     }
       
   737                 } else {
       
   738                     if (om != null || m == null) {
       
   739                         // We are copying and existing mode and/or creating a
       
   740                         // brand-new mode -- in either case create from scratch
       
   741                         m = (om != null)
       
   742                                 ? new Mode(umode, om)
       
   743                                 : new Mode(umode);
       
   744                         modeMap.put(umode, m);
       
   745                         fluffmsg("jshell.msg.feedback.new.mode", m.name);
       
   746                         // Set the current mode by name, in case we just smashed
       
   747                         // the current mode
       
   748                         if (umode.equals(mode.name)) {
       
   749                             mode = modeMap.get(mode.name);
       
   750                         }
       
   751                     }
       
   752                     if (commandOption || quietOption || om == null) {
       
   753                         // set command fluff, if explicit, or wholly new
       
   754                         m.setCommandFluff(!quietOption);
       
   755                     }
       
   756                 }
       
   757             }
       
   758             if (!valid) {
       
   759                 fluffmsg("jshell.msg.see", "/help /set mode");
   715             }
   760             }
   716             return valid;
   761             return valid;
   717         }
   762         }
   718 
   763 
   719         // For /set feedback <mode>
   764         // For /set feedback <mode>
   734             Mode m = nextMode();
   779             Mode m = nextMode();
   735             if (valid && m.readOnly) {
   780             if (valid && m.readOnly) {
   736                 errorat("jshell.err.not.valid.with.predefined.mode", m.name);
   781                 errorat("jshell.err.not.valid.with.predefined.mode", m.name);
   737                 valid = false;
   782                 valid = false;
   738             }
   783             }
   739             String field = at.next();
   784             String field = valid
   740             if (field == null || !at.isIdentifier()) {
   785                     ? toIdentifier(at.next(), "jshell.err.missing.field", "jshell.err.field.name")
   741                 errorat("jshell.err.feedback.expected.field");
   786                     : null;
   742                 valid = false;
       
   743             }
       
   744             String format = valid ? nextFormat() : null;
   787             String format = valid ? nextFormat() : null;
   745             return installFormat(m, field, format, "/help /set format");
   788             return installFormat(m, field, format, "/help /set format");
   746         }
   789         }
   747 
   790 
   748         // For /set truncation <mode> <length> <selector>...
   791         // For /set truncation <mode> <length> <selector>...
   770         }
   813         }
   771 
   814 
   772         String retainFeedback() {
   815         String retainFeedback() {
   773             String umode = at.next();
   816             String umode = at.next();
   774             if (umode != null) {
   817             if (umode != null) {
   775                 Mode m = toMode(umode);
   818                 toModeIdentifier(umode);
       
   819                 Mode m = valid ? toMode(umode) : null;
   776                 if (valid && !m.readOnly && !retainedMap.containsKey(m.name)) {
   820                 if (valid && !m.readOnly && !retainedMap.containsKey(m.name)) {
   777                     errorat("jshell.err.retained.feedback.mode.must.be.retained.or.predefined");
   821                     errorat("jshell.err.retained.feedback.mode.must.be.retained.or.predefined");
   778                     valid = false;
   822                     valid = false;
   779                 }
   823                 }
   780                 if (valid) {
   824                 if (valid) {
   781                     mode = m;
   825                     mode = m;
       
   826                     retainedCurrentMode = m;
   782                     fluffmsg("jshell.msg.feedback.mode", mode.name);
   827                     fluffmsg("jshell.msg.feedback.mode", mode.name);
   783                 } else {
   828                 } else {
   784                     fluffmsg("jshell.msg.see", "/help /retain feedback");
   829                     fluffmsg("jshell.msg.see", "/help /retain feedback");
   785                     return null;
   830                     return null;
   786                 }
   831                 }
   787             }
   832             }
   788             return mode.name;
   833             return mode.name;
   789         }
   834         }
   790 
   835 
       
   836         /**
       
   837          * Retain (or delete from retention) a previously set mode.
       
   838          *
       
   839          * @return all retained modes encoded into a String
       
   840          */
   791         String retainMode() {
   841         String retainMode() {
   792             Mode m = nextMode();
   842             at.allowedOptions("-delete");
   793             if (valid && m.readOnly) {
   843             String umode = nextModeIdentifier();
   794                 errorat("jshell.err.not.valid.with.predefined.mode", m.name);
   844             // -delete is the only valid option, fail for anything else
       
   845             checkOptionsAndRemainingInput();
       
   846             boolean deleteOption = at.hasOption("-delete");
       
   847             // Lookup the mode
       
   848             Mode m;
       
   849             if (!valid) {
       
   850                 m = null;
       
   851                 // Skip this stuff, we have failed already
       
   852             } else if (deleteOption) {
       
   853                 // If delete, allow for deleting, from retention, a mode that
       
   854                 // has been locally deleted but is retained.
       
   855                 // Also require the full name.
       
   856                 m = modeMap.get(umode);
       
   857                 if (m == null && !retainedMap.containsKey(umode)) {
       
   858                     errorat("jshell.err.mode.unknown", umode);
       
   859                     valid = false;
       
   860                 }
       
   861             } else {
       
   862                 // For retain do normal lookup and checking
       
   863                 m = toMode(umode);
       
   864             }
       
   865 
       
   866             // Built-in modes cannot be retained or deleted
       
   867             if (valid && m != null && m.readOnly) {
       
   868                 errorat("jshell.err.not.valid.with.predefined.mode", umode);
   795                 valid = false;
   869                 valid = false;
   796             }
   870             }
   797             if (valid) {
   871             if (valid) {
   798                 retainedMap.put(m.name, m.encode());
   872                 if (deleteOption) {
       
   873                     if (mode.name.equals(umode)) {
       
   874                         // Cannot delete the current mode out from under us
       
   875                         errorat("jshell.err.cannot.delete.current.mode", umode);
       
   876                         valid = false;
       
   877                     } else if (retainedCurrentMode != null && retainedCurrentMode.name.equals(umode)) {
       
   878                         // Cannot delete the retained mode or re-start has error
       
   879                         errorat("jshell.err.cannot.delete.retained.mode", umode);
       
   880                         valid = false;
       
   881                     } else {
       
   882                         // Delete the mode
       
   883                         modeMap.remove(umode);
       
   884                         retainedMap.remove(umode);
       
   885                     }
       
   886                 } else {
       
   887                     // Retain the current encoding
       
   888                     retainedMap.put(m.name, m.encode());
       
   889                 }
       
   890             }
       
   891             if (valid) {
       
   892                 // Join all the retained encodings
   799                 return String.join(RECORD_SEPARATOR, retainedMap.values());
   893                 return String.join(RECORD_SEPARATOR, retainedMap.values());
   800             } else {
   894             } else {
   801                 fluffmsg("jshell.msg.see", "/help /retain mode");
   895                 fluffmsg("jshell.msg.see", "/help /retain mode");
   802                 return null;
   896                 return null;
   803             }
   897             }
   804         }
   898         }
   805 
   899 
   806         boolean restoreEncodedModes(String allEncoded) {
   900         boolean restoreEncodedModes(String allEncoded) {
   807             // Iterate over each record in each encoded mode
   901             try {
   808             String[] ms = allEncoded.split(RECORD_SEPARATOR);
   902                 // Iterate over each record in each encoded mode
   809             Iterator<String> itr = Arrays.asList(ms).iterator();
   903                 String[] ms = allEncoded.split(RECORD_SEPARATOR);
   810             while (itr.hasNext()) {
   904                 Iterator<String> itr = Arrays.asList(ms).iterator();
   811                 // Reconstruct the encoded mode
   905                 while (itr.hasNext()) {
   812                 Mode m = new Mode(itr);
   906                     // Reconstruct the encoded mode
   813                 modeMap.put(m.name, m);
   907                     Mode m = new Mode(itr);
   814                 // Continue to retain it a new retains occur
   908                     modeMap.put(m.name, m);
   815                 retainedMap.put(m.name, m.encode());
   909                     // Continue to retain it a new retains occur
   816             }
   910                     retainedMap.put(m.name, m.encode());
   817             return true;
   911                 }
       
   912                 return true;
       
   913             } catch (Throwable exc) {
       
   914                 // Catastrophic corruption -- clear map
       
   915                 errorat("jshell.err.retained.mode.failure", exc);
       
   916                 retainedMap.clear();
       
   917                 return false;
       
   918             }
   818         }
   919         }
   819 
   920 
   820         // install the format of a field under parsed selectors
   921         // install the format of a field under parsed selectors
   821         boolean installFormat(Mode m, String field, String format, String help) {
   922         boolean installFormat(Mode m, String field, String format, String help) {
   822             String slRaw;
   923             String slRaw;
   842                 fluffmsg("jshell.msg.see", help);
   943                 fluffmsg("jshell.msg.see", help);
   843             }
   944             }
   844             return valid;
   945             return valid;
   845         }
   946         }
   846 
   947 
       
   948         void checkOptionsAndRemainingInput() {
       
   949             if (!valid) {
       
   950                 return;
       
   951             }
       
   952             String junk = at.remainder();
       
   953             if (!junk.isEmpty()) {
       
   954                 errorat("jshell.err.unexpected.at.end", junk);
       
   955                 valid = false;
       
   956             } else {
       
   957                 String bad = at.badOptions();
       
   958                 if (!bad.isEmpty()) {
       
   959                     errorat("jshell.err.unknown.option", bad);
       
   960                     valid = false;
       
   961                 }
       
   962             }
       
   963         }
       
   964 
       
   965         /**
       
   966          * Check that the specified string is an identifier (Java identifier).
       
   967          * If null display the missing error. If it is not an identifier,
       
   968          * display the error.
       
   969          *
       
   970          * @param id the string to check, MUST be the most recently retrieved
       
   971          * token from 'at'.
       
   972          * @param missing the resource error to display if null
       
   973          * @param err the resource error to display if not an identifier
       
   974          * @return the identifier string, or null if null or not an identifier
       
   975          */
       
   976         String toIdentifier(String id, String missing, String err) {
       
   977             if (id == null) {
       
   978                 errorat(missing);
       
   979                 valid = false;
       
   980                 return null;
       
   981             }
       
   982             if (at.isQuoted() ||
       
   983                     !id.codePoints().allMatch(cp -> Character.isJavaIdentifierPart(cp))) {
       
   984                 errorat(err, id);
       
   985                 valid = false;
       
   986                 return null;
       
   987             }
       
   988             return id;
       
   989         }
       
   990 
       
   991         String toModeIdentifier(String id) {
       
   992             return toIdentifier(id, "jshell.err.missing.mode", "jshell.err.mode.name");
       
   993         }
       
   994 
       
   995         String nextModeIdentifier() {
       
   996             return toModeIdentifier(at.next());
       
   997         }
       
   998 
   847         Mode nextMode() {
   999         Mode nextMode() {
   848             String umode = at.next();
  1000             String umode = nextModeIdentifier();
   849             return toMode(umode);
  1001             return toMode(umode);
   850         }
  1002         }
   851 
  1003 
   852         Mode toMode(String umode) {
  1004         Mode toMode(String umode) {
   853             if (umode == null || !at.isIdentifier()) {
  1005             if (!valid) {
   854                 errorat("jshell.err.feedback.expected.mode");
  1006                 return null;
       
  1007             }
       
  1008             if (umode == null) {
       
  1009                 errorat("jshell.err.missing.mode");
   855                 valid = false;
  1010                 valid = false;
   856                 return null;
  1011                 return null;
   857             }
  1012             }
   858             Mode m = modeMap.get(umode);
  1013             Mode m = modeMap.get(umode);
   859             if (m != null) {
  1014             if (m != null) {