package org.matheclipse.core.builtin; import java.awt.Color; import java.awt.Container; import java.awt.Desktop; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileSystemView; import org.apache.commons.lang3.StringUtils; import org.matheclipse.core.basic.Config; import org.matheclipse.core.convert.AST2Expr; import org.matheclipse.core.eval.EvalEngine; import org.matheclipse.core.eval.exception.ArgumentTypeException; import org.matheclipse.core.eval.exception.DialogReturnException; import org.matheclipse.core.eval.interfaces.AbstractCoreFunctionEvaluator; import org.matheclipse.core.eval.interfaces.AbstractEvaluator; import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; import org.matheclipse.core.expression.F; import org.matheclipse.core.expression.ID; import org.matheclipse.core.interfaces.IAST; import org.matheclipse.core.interfaces.IASTAppendable; import org.matheclipse.core.interfaces.IBuiltInSymbol; import org.matheclipse.core.interfaces.IExpr; import org.matheclipse.core.interfaces.IStringX; import org.matheclipse.core.interfaces.ISymbol; import org.matheclipse.core.patternmatching.IPatternMatcher; import org.matheclipse.parser.client.FEConfig; import org.matheclipse.parser.trie.SuggestTree; import org.matheclipse.parser.trie.SuggestTree.Node; public class IOFunctions { /** * * See <a href="https://pangin.pro/posts/computation-in-static-initializer">Beware of computation in static * initializer</a> */ private static class Initializer { private static void init() { if (Config.FILESYSTEM_ENABLED) { F.Button.setEvaluator(new Button()); F.DefaultButton.setEvaluator(new DefaultButton()); F.Dynamic.setEvaluator(new Dynamic()); F.CancelButton.setEvaluator(new CancelButton()); F.DialogInput.setEvaluator(new DialogInput()); F.DialogReturn.setEvaluator(new DialogReturn()); F.Input.setEvaluator(new Input()); F.InputString.setEvaluator(new InputString()); F.SystemDialogInput.setEvaluator(new SystemDialogInput()); } // F.General.setEvaluator(new General()); F.Message.setEvaluator(new Message()); F.Names.setEvaluator(new Names()); for (int i = 0; i < MESSAGES.length; i += 2) { F.General.putMessage(IPatternMatcher.SET, MESSAGES[i], F.stringx(MESSAGES[i + 1])); } } } private static class Button extends AbstractCoreFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { return F.NIL; } @Override public void setUp(ISymbol newSymbol) { newSymbol.setAttributes(ISymbol.HOLDALL); } } private static class CancelButton extends AbstractCoreFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { return F.NIL; } @Override public void setUp(ISymbol newSymbol) { newSymbol.setAttributes(ISymbol.HOLDALL); } } private static class DefaultButton extends AbstractCoreFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { return F.NIL; } @Override public void setUp(ISymbol newSymbol) { newSymbol.setAttributes(ISymbol.HOLDALL); } } private static class Dynamic extends AbstractCoreFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { return F.NIL; } @Override public void setUp(ISymbol newSymbol) { newSymbol.setAttributes(ISymbol.HOLDALL); } } private static class Message extends AbstractEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { if (ast.size() > 1) { if (ast.arg1().isString()) { String message = ast.arg1().toString(); for (int i = 2; i < ast.size(); i++) { message = message.replaceAll("`" + (i - 1) + "`", ast.get(i).toString()); } return F.stringx(": " + message); } if (ast.arg1().isAST(F.MessageName, 3)) { IAST messageName = (IAST) ast.arg1(); String messageShortcut = messageName.arg2().toString(); if (messageName.arg1().isSymbol()) { IExpr temp = message((ISymbol) messageName.arg1(), messageShortcut, ast); if (temp.isPresent()) { return temp; } } return message(F.General, messageShortcut, ast); } } return F.NIL; } @Override public void setUp(ISymbol newSymbol) { newSymbol.setAttributes(ISymbol.HOLDFIRST); } } private final static class InputString extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { return inputString(ast, engine); } public int[] expectedArgSize() { return IOFunctions.ARGS_0_1; } } private final static class SystemDialogInput extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { if (Desktop.isDesktopSupported() && ast.arg1().isString()) { String type = ast.arg1().toString().toLowerCase(); if (type.equals("fileopen")) { JFileChooser j = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); j.setApproveButtonText("Open"); // FileNameExtensionFilter restrict = new FileNameExtensionFilter("Only .txt files", "txt"); // j.addChoosableFileFilter(restrict); int r = j.showSaveDialog(null); if (r == JFileChooser.APPROVE_OPTION) { return F.stringx(j.getSelectedFile().getAbsolutePath()); } } else if (type.equals("filesave")) { JFileChooser j = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); j.setApproveButtonText("Save"); int r = j.showSaveDialog(null); if (r == JFileChooser.APPROVE_OPTION) { return F.stringx(j.getSelectedFile().getAbsolutePath()); } } else if (type.equals("directory")) { JFileChooser j = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); j.setApproveButtonText("Select"); j.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int r = j.showSaveDialog(null); if (r == JFileChooser.APPROVE_OPTION) { return F.stringx(j.getSelectedFile().getAbsolutePath()); } } else if (type.equals("color")) { Color color = JColorChooser.showDialog(null, "ColorChooser", null); if (color != null) { return F.stringx(Integer.toString(color.getRGB())); } } } return F.NIL; } public int[] expectedArgSize() { return IOFunctions.ARGS_1_2; } } private final static class Input extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { try { IExpr str = inputString(ast, engine); if (str.isPresent()) { return engine.evaluate(str.toString()); } } catch (final RuntimeException e1) { // } return F.NIL; } public int[] expectedArgSize() { return IOFunctions.ARGS_0_1; } } private static IExpr inputString(final IAST ast, EvalEngine engine) { final BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); try { if (ast.isAST1()) { engine.getOutPrintStream().print(ast.arg1().toString()); } final String str = in.readLine(); if (str != null) { return F.stringx(str); } } catch (final Exception e1) { // } return F.NIL; } private final static class DialogInput extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { if (Desktop.isDesktopSupported()) { IAST dialogNoteBook = null; if (ast.isAST2() && ast.arg2().isAST(F.DialogNotebook, 2)) { dialogNoteBook = (IAST) ast.arg2(); } else if (ast.isAST1() && ast.arg1().isAST(F.DialogNotebook, 2)) { dialogNoteBook = (IAST) ast.arg1(); } IAST list; if (dialogNoteBook == null) { if (ast.isAST1()) { if (ast.arg1().isList()) { list = (IAST) ast.arg1(); } else { list = F.List(ast.arg1()); } } else { return F.NIL; } } else { if (dialogNoteBook.arg1().isList()) { list = (IAST) dialogNoteBook.arg1(); } else { list = F.List(dialogNoteBook.arg1()); } } JDialog dialog = new JDialog(); dialog.setTitle("DialogInput"); dialog.setSize(320, 200); dialog.setModal(true); // dialog.setLayout(new FlowLayout(FlowLayout.LEFT)); // dialog.setLayout(new GridLayout(list.argSize(), 1)); IExpr[] result = new IExpr[] { F.NIL }; if (addComponents(dialog, list, dialog, engine, result)) { dialog.setVisible(true); if (result[0].isPresent()) { return result[0]; } } } return F.NIL; } private static boolean addComponents(Container container, IAST list, JDialog dialog, EvalEngine engine, IExpr result[]) { final Consumer<IExpr> consumer = x -> { try { engine.evaluate(x); } catch (DialogReturnException rex) { result[0] = rex.getValue(); dialog.dispose(); } catch (RuntimeException rex) { // } }; for (int i = 1; i < list.size(); i++) { IExpr arg = list.get(i); int headID = list.get(i).headID(); if (headID > 0) { switch (headID) { case ID.Button: if (arg.size() == 3) { final String buttonLabel = arg.first().toString(); createButton(dialog, container, buttonLabel, arg.second(), consumer, result, engine); } else { return false; } continue; case ID.CancelButton: String cancelLabel = "Cancel"; final IExpr cancelAction; if (arg.size() == 1) { cancelAction = F.DialogReturn(F.$Cancel); } else if (arg.size() == 2) { cancelAction = F.DialogReturn(arg.first()); } else if (arg.size() == 3) { cancelLabel = arg.first().toString(); cancelAction = F.DialogReturn(arg.second()); } else { return false; } createButton(dialog, container, cancelLabel, cancelAction, consumer, result, engine); dialog.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "CANCEL"); dialog.getRootPane().getActionMap().put("CANCEL", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { consumer.accept(cancelAction); } }); continue; case ID.DefaultButton: String buttonLabel = "Ok"; IExpr buttonAction = F.DialogReturn(); if (arg.size() == 1) { } else if (arg.size() == 2) { buttonAction = F.DialogReturn(arg.first()); } else if (arg.size() == 3) { buttonLabel = arg.first().toString(); buttonAction = F.DialogReturn(arg.second()); } else { return false; } JButton db = createButton(dialog, container, buttonLabel, buttonAction, consumer, result, engine); dialog.getRootPane().setDefaultButton(db); continue; case ID.Column: if (arg.size() == 2) { IAST column; if (arg.first().isList()) { column = (IAST) arg.first(); } else { column = F.List(arg.first()); } JPanel columnPanel = new JPanel(); columnPanel.setLayout(new GridLayout(column.argSize(), 1)); container.add(columnPanel); if (!addComponents(columnPanel, column, dialog, engine, result)) { return false; } } else { return false; } continue; case ID.InputField: IExpr input = F.Null; int inputType = ID.String; if (arg.size() == 1) { } else if (arg.size() == 2) { input = arg.first(); } else if (arg.size() == 3) { input = arg.first(); if (arg.second().isBuiltInSymbol()) { inputType = ((IBuiltInSymbol) arg.second()).ordinal(); } } else { return false; } createInputField(dialog, container, input, inputType, result, engine); continue; case ID.Row: if (arg.size() == 2) { IAST row; if (arg.first().isList()) { row = (IAST) arg.first(); } else { row = F.List(arg.first()); } JPanel rowPanel = new JPanel(); rowPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); container.add(rowPanel); if (!addComponents(rowPanel, row, dialog, engine, result)) { return false; } } else { return false; } continue; case ID.TextCell: if (arg.size() == 2) { JLabel label = new JLabel(arg.first().toString()); container.add(label); } else { return false; } continue; default: } } // fallback JLabel label = new JLabel(arg.toString()); container.add(label); } return true; } private static JButton createButton(JDialog dialog, Container container, String label, IExpr action, final Consumer<IExpr> consumer, IExpr[] result, EvalEngine engine) { JButton button = new JButton(label); container.add(button); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { consumer.accept(action); } }); return button; } private static class MyDocumentListener implements DocumentListener { JTextField inputField; ISymbol dynamic; int headID; public MyDocumentListener(JTextField inputField, ISymbol dynamic, int headID) { if (dynamic != null && // (!dynamic.isVariable() || dynamic.isBuiltInSymbol())) { // Cannot assign to raw object `1`. throw new ArgumentTypeException( IOFunctions.getMessage("setraw", F.List(dynamic), EvalEngine.get())); } this.inputField = inputField; this.dynamic = dynamic; this.headID = headID; } @Override public void insertUpdate(DocumentEvent e) { updateFieldState(); } @Override public void removeUpdate(DocumentEvent e) { updateFieldState(); } @Override public void changedUpdate(DocumentEvent e) { updateFieldState(); } protected void updateFieldState() { if (dynamic != null) { String text = inputField.getText(); IExpr expr = F.NIL; if (headID == ID.String) { expr = F.stringx(text); } else if (headID == ID.Expression) { expr = F.eval(F.ToExpression(F.stringx(text))); } else if (headID == ID.Number) { expr = F.eval(F.ToExpression(F.stringx(text))); if (!expr.isNumber()) { expr = F.NIL; } } if (expr.isPresent()) { dynamic.assign(expr); } // System.out.println(F.eval(dynamic).toString()); } } }; private static void createInputField(JDialog dialog, Container container, final IExpr action, int headID, IExpr[] result, EvalEngine engine) { String defaultInput = action.toString(); ISymbol dynamic = null; if (action == F.Null) { defaultInput = ""; } else if (action.isAST(F.Dynamic, 2) && action.first().isSymbol() && !action.first().isBuiltInSymbol()) { dynamic = (ISymbol) action.first(); defaultInput = dynamic.toString(); } JTextField inputField = new JTextField(defaultInput, 10); container.add(inputField); MyDocumentListener dl = new MyDocumentListener(inputField, dynamic, headID); inputField.getDocument().addDocumentListener(dl); // inputField.addActionListener(new ActionListener() { // @Override // public void actionPerformed(ActionEvent e) { // try { // System.out.println(inputField.getText()); // } catch (DialogReturnException rex) { // result[0] = rex.getValue(); // dialog.dispose(); // } catch (RuntimeException rex) { // // // } // } // }); } public int[] expectedArgSize() { return IOFunctions.ARGS_1_2; } } private final static class DialogReturn extends AbstractCoreFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { if (ast.isAST1()) { IExpr arg1 = ast.arg1(); if (arg1.isFalse()) { throw DialogReturnException.DIALOG_RETURN_FALSE; } if (arg1.isTrue()) { throw DialogReturnException.DIALOG_RETURN_TRUE; } arg1 = engine.evaluate(arg1); if (arg1.isFalse()) { throw DialogReturnException.DIALOG_RETURN_FALSE; } if (arg1.isTrue()) { throw DialogReturnException.DIALOG_RETURN_TRUE; } throw new DialogReturnException(arg1); } throw new DialogReturnException(); } public int[] expectedArgSize() { return IOFunctions.ARGS_0_1; } @Override public void setUp(final ISymbol newSymbol) { } } private final static class Names extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { if (ast.isAST0()) { return getAllNames(); } if (ast.arg1() instanceof IStringX) { return getNamesByPrefix(ast.arg1().toString()); } return F.NIL; } public int[] expectedArgSize() { return IOFunctions.ARGS_0_1; } } public static final int[] ARGS_0_0 = new int[] { 0, 0 }; public static final int[] ARGS_0_1 = new int[] { 0, 1 }; public static final int[] ARGS_0_2 = new int[] { 0, 2 }; public static final int[] ARGS_1_1 = new int[] { 1, 1 }; public static final int[] ARGS_1_2 = new int[] { 1, 2 }; public static final int[] ARGS_1_5 = new int[] { 1, 5 }; public static final int[] ARGS_2_2 = new int[] { 2, 2 }; public static final int[] ARGS_1_3 = new int[] { 1, 3 }; public static final int[] ARGS_1_4 = new int[] { 1, 4 }; public static final int[] ARGS_2_3 = new int[] { 2, 3 }; public static final int[] ARGS_2_4 = new int[] { 2, 4 }; public static final int[] ARGS_3_3 = new int[] { 3, 3 }; public static final int[] ARGS_3_4 = new int[] { 3, 4 }; public static final int[] ARGS_4_4 = new int[] { 4, 4 }; public static final int[] ARGS_5_5 = new int[] { 5, 5 }; public static final int[] ARGS_1_INFINITY = new int[] { 1, Integer.MAX_VALUE }; public static final int[] ARGS_2_INFINITY = new int[] { 2, Integer.MAX_VALUE }; public static final int[] ARGS_3_INFINITY = new int[] { 3, Integer.MAX_VALUE }; private final static String[] MESSAGES = { // "argillegal", "illegal arguments: \"`1`\" in `2`", // "argb", "`1` called with `2` arguments; between `3` and `4` arguments are expected.", // "argct", "`1` called with `2` arguments.", // "argctu", "`1` called with 1 argument.", // "argr", "`1` called with 1 argument; `2` arguments are expected.", // "argrx", "`1` called with `2` arguments; `3` arguments are expected.", // "argx", "`1` called with `2` arguments; 1 argument is expected.", // "argt", "`1` called with `2` arguments; `3` or `4` arguments are expected.", // "argtu", "`1` called with 1 argument; `2` or `3` arguments are expected.", // "base", "Requested base `1` in `2` should be between 2 and `3`.", // "boxfmt", "`1` is not a box formatting type.", // "coef", "The first argument `1` of `2` should be a non-empty list of positive integers.", // "color", "`1` is not a valid color or gray-level specification.", // "compat", "`1` and `2` are incompatible units", // "cxt", "`1` is not a valid context name.", // "divz", "The argument `1` should be nonzero.", // "digit", "Digit at position `1` in `2` is too large to be used in base `3`.", // "drop", "Cannot drop positions `1` through `2` in `3`.", // "eqf", "`1` is not a well-formed equation.", // "exact", "Argument `1` is not an exact number.", // "fftl", "Argument `1` is not a non-empty list or rectangular array of numeric quantities.", // "fpct", "To many parameters in `1` to be filled from `2`.", // "fnsym", "First argument in `1` is not a symbol or a string naming a symbol.", // "heads", "Heads `1` and `2` are expected to be the same.", // "ilsnn", "Single or list of non-negative integers expected at position `1`.", // "indet", "Indeterminate expression `1` encountered.", // "infy", "Infinite expression `1` encountered.", // "innf", "Non-negative integer or Infinity expected at position `1`.", // "int", "Integer expected at position `2` in `1`.", // "intjava", "Java int value greater equal `1` expected instead of `2`.", // "intlevel", "Level specification value greater equal `1` expected instead of `2`.", // "intp", "Positive integer expected.", // "intnn", "Non-negative integer expected.", // "intnm", "Non-negative machine-sized integer expected at position `2` in `1`.", // "intm", "Machine-sized integer expected at position `2` in `1`.", // "intpm", "Positive machine-sized integer expected at position `2` in `1`.", // "intrange", "Integer expected in range `1` to `2`.", // "iterb", "Iterator does not have appropriate bounds.", // "itform", "Argument `1` at position `2` does not have the correct form for an iterator.", // "itlim", "Iteration limit of `1` exceeded for `2`.", // "itlimpartial", "Iteration limit of `1` exceeded. Returning partial results.", // "itendless", "Endless iteration detected in `1` in evaluation loop.", // "ivar", "`1` is not a valid variable.", // "level", "Level specification `1` is not of the form n, {n}, or {m, n}.", // "list", "List expected at position `1` in `2`.", // "listofbigints", "List of Java BigInteger numbers expected in `1`.", // "listofints", "List of Java int numbers expected in `1`.", // "listoflongs", "List of Java long numbers expected in `1`.", // "locked", "Symbol `1` is locked.", // "lvlist", "Local variable specification `1` is not a List.", // "lvws", "Variable `1` in local variable specification `2` requires assigning a value", // "lvset", "Local variable specification `1` contains `2`, which is an assignment to `3`; only assignments to symbols are allowed.", // "matrix", "Argument `1` at position `2` is not a non-empty rectangular matrix.", // "matsq", "Argument `1` at position `2` is not a non-empty square matrix.", // "nil", "unexpected NIL expression encountered.", // "noneg", "Surd is not defined for even roots of negative values.", // "noopen", "Cannot open `1`.", // "nonopt", "Options expected (instead of `1`) beyond position `2` in `3`. An option must be a rule or a list of rules.", // "nord", "Invalid comparison with `1` attempted.", // "normal", "Nonatomic expression expected at position `1` in `2`.", // "notent", "`2` is not a known entity, class, or tag for `1`.", // "nquan", "The Quantile specification `1` should be a number between `2` and `3`.", "nvld", "The expression `1` is not a valid interval.", // "notunicode", "A character unicode, which should be a non-negative integer less than 1114112, is expected at position `2` in `1`.", // "noval", "Symbol `1` in part assignment does not have an immediate value.", // "nsmet", "This system cannot be solved with the methods available to `1`", // "openx", "`1` is not open.", // "optb", "Optional object `1` in `2` is not a single blank.", // "ovfl", "Overflow occurred in computation.", // "padlevel", "The padding specification `1` involves `2` levels; the list `3` has only `4` level.", // "partd", "Part specification `1` is longer than depth of object.", // "partw", "Part `1` of `2` does not exist.", // "pilist", "The arguments to `1` must be two lists of integers of identical length, with the second list only containing positive integers.", // "plld", "Endpoints in `1` must be distinct machine-size real numbers.", // "plln", "Limiting value `1` in `2` is not a machine-size real number.", // "pspec", "Part specification `1` is neither an integer nor a list of integer.", // "poly", "`1` is not a polynomial.", // "polynomial", "Polynomial expected at position `1` in `2`.", // "pkspec1", "The expression `1` cannot be used as a part specification.", // "precsm", "Requested precision `1` is smaller than `2`.", // "range", "Range specification in `1` does not have appropriate bounds.", // "reclim2", "Recursion depth of `1` exceeded during evaluation of `2`.", // "rectt", "Rectangular array expected at position `1` in `2`.", // "rvalue", "`1` is not a variable with a value, so its value cannot be changed.", // "rubiendless", "Endless iteration detected in `1` for Rubi pattern-matching rules.", // "seqs", "Sequence specification expected, but got `1`.", // "setp", "Part assignment to `1` could not be made", // "setraw", "Cannot assign to raw object `1`.", // "setps", "`1` in the part assignment is not a symbol.", // "sing", "Matrix `1` is singular.", // "span", "`1` is not a valid Span specification.", // "stream", "`1` is not string, InputStream[], or OutputStream[]", // "string", "String expected at position `1` in `2`.", // "sym", "Argument `1` at position `2` is expected to be a symbol.", // "tdlen", "Objects of unequal length in `1` cannot be combined.", // "tag", "Rule for `1` can only be attached to `2`.", // "take", "Cannot take positions `1` through `2` in `3`.", // "toggle", "ToggleFeature `1` is disabled.", // "unsupported", "`1` currently not supported in `2`.", // "usraw", "Cannot unset object `1`.", // "vloc", "The variable `1` cannot be localized so that it can be assigned to numerical values.", // "vpow2", "Argument `1` is restricted to vectors with a length of power of 2.", // "vrule", "Cannot set `1` to `2`, which is not a valid list of replacement rules.", // "write", "Tag `1` in `2` is Protected.", // "wrsym", "Symbol `1` is Protected.", // "ucdec", "An invalid unicode sequence was encountered and ignored." // }; public static void initialize() { Initializer.init(); } public static IExpr message(ISymbol symbol, String messageShortcut, final IAST ast) { IExpr temp = symbol.evalMessage(messageShortcut); String message = null; if (temp.isPresent()) { message = temp.toString(); } else { temp = F.General.evalMessage(messageShortcut); if (temp.isPresent()) { message = temp.toString(); } } if (message != null) { message = rawMessage(ast, message); return F.stringx(symbol.toString() + ": " + message); } return F.NIL; } public static IExpr printArgMessage(IAST ast, int[] expected, EvalEngine engine) { final ISymbol topHead = ast.topHead(); int argSize = ast.argSize(); if (expected[0] == expected[1]) { if (expected[0] == 1) { return printMessage(topHead, "argx", F.List(topHead, F.ZZ(argSize), F.ZZ(expected[0])), engine); } if (argSize == 1) { return printMessage(topHead, "argr", F.List(topHead, F.ZZ(expected[0])), engine); } return printMessage(topHead, "argrx", F.List(topHead, F.ZZ(argSize), F.ZZ(expected[0])), engine); } return printMessage(topHead, "argt", F.List(topHead, F.ZZ(argSize), F.ZZ(expected[0]), F.ZZ(expected[1])), engine); } /** * * @param symbol * @param messageShortcut * the message shortcut defined in <code>MESSAGES</code> array * @param listOfArgs * a list of arguments which should be inserted into the message shortcuts placeholder * @param engine * @return always <code>F.NIL</code> */ public static IAST printMessage(ISymbol symbol, String messageShortcut, final IAST listOfArgs, EvalEngine engine) { IExpr temp = symbol.evalMessage(messageShortcut); String message = null; if (temp.isPresent()) { message = temp.toString(); } else { temp = F.General.evalMessage(messageShortcut); if (temp.isPresent()) { message = temp.toString(); } } if (message == null) { message = "Undefined message shortcut: " + messageShortcut; engine.setMessageShortcut(messageShortcut); engine.printMessage(symbol.toString() + ": " + message); } else { for (int i = 1; i < listOfArgs.size(); i++) { message = StringUtils.replace(message, "`" + (i) + "`", listOfArgs.get(i).toString()); } engine.setMessageShortcut(messageShortcut); engine.printMessage(symbol.toString() + ": " + message); } return F.NIL; } public static String getMessage(String messageShortcut, final IAST listOfArgs, EvalEngine engine) { IExpr temp = F.General.evalMessage(messageShortcut); String message = null; if (temp.isPresent()) { message = temp.toString(); } if (message == null) { message = "Undefined message shortcut: " + messageShortcut; engine.setMessageShortcut(messageShortcut); return message; } for (int i = 1; i < listOfArgs.size(); i++) { message = StringUtils.replace(message, "`" + (i) + "`", listOfArgs.get(i).toString()); } engine.setMessageShortcut(messageShortcut); return message; } public static IAST printMessage(ISymbol symbol, Exception ex, EvalEngine engine) { String message = ex.getMessage(); if (message != null) { engine.printMessage(symbol.toString() + ": " + message); } else { engine.printMessage(symbol.toString() + ": " + ex.getClass().toString()); } return F.NIL; } private static String rawMessage(final IAST ast, String message) { for (int i = 2; i < ast.size(); i++) { message = StringUtils.replace(message, "`" + (i - 1) + "`", ast.get(i).toString()); } return message; } public static IAST getNamesByPrefix(String name) { if (name.length() == 0) { return F.List(); } boolean exact = true; if (name.charAt(name.length() - 1) == '*') { name = name.substring(0, name.length() - 1); if (name.length() == 0) { return getAllNames(); } exact = false; } SuggestTree suggestTree = AST2Expr.getSuggestTree(); name = FEConfig.PARSER_USE_LOWERCASE_SYMBOLS ? name.toLowerCase() : name; Node n = suggestTree.getAutocompleteSuggestions(name); if (n != null) { IASTAppendable list = F.ListAlloc(n.listLength()); for (int i = 0; i < n.listLength(); i++) { if (exact) { if (name.equals(n.getSuggestion(i).getTerm())) { list.append(F.$s(n.getSuggestion(i).getTerm())); } } else { list.append(F.$s(n.getSuggestion(i).getTerm())); } } return list; } return F.List(); } public static List<String> getAutoCompletionList(String namePrefix) { List<String> list = new ArrayList<String>(); if (namePrefix.length() == 0) { return list; } SuggestTree suggestTree = AST2Expr.getSuggestTree(); namePrefix = FEConfig.PARSER_USE_LOWERCASE_SYMBOLS ? namePrefix.toLowerCase() : namePrefix; Node n = suggestTree.getAutocompleteSuggestions(namePrefix); if (n != null) { for (int i = 0; i < n.listLength(); i++) { list.add(n.getSuggestion(i).getTerm()); } } return list; } public static IAST getAllNames() { int size = AST2Expr.FUNCTION_STRINGS.length; IASTAppendable list = F.ListAlloc(size); return list.appendArgs(0, size, i -> F.$s(AST2Expr.FUNCTION_STRINGS[i])); // for (int i = 0; i < size; i++) { // list.append(F.$s(AST2Expr.FUNCTION_STRINGS[i])); // } // return list; } private IOFunctions() { } }