Формула Java: как запустить строковое выражение? !

Java задняя часть GitHub Java EE

Предыстория и введение

В повседневной разработке мы время от времени сталкиваемся с ситуацией запуска строковых выражений.Обычно такие требования будут дальше анализировать требования, затем дальше "специализироваться" и, наконец, записывать их прямо в хард-код.Расширять не очень просто, есть еще один способ справиться с этим — использовать встроенный в Java движок JavaScript для запуска строковых выражений, но встроенный движок также имеет недостатки, такие как эффективность часто выполняемых фрагментированных строк, очень низкая, а связь между Java и Java очень низкий.Взаимодействие с данными более хлопотно, поэтому возникла идея написать "движок вычисления строковых выражений"...

Процесс написания на самом деле не такой хлопотный как я себе представлял.Первоначальная версия наверное писалась в конце мая этого года,но структура довольно хаотичная.При написании в основном ремонтируется при написании.Наконец условия такие как будто. ..else... и встраивание Наборов так много, что я не могу до конца разобраться.К счастью, логика в принципе идеальна, и непредвиденных ситуаций в работе нет (может и было, но я не нашел это), и я использую его сам, так что мне все равно.

В первые две недели я потратил время на то, чтобы снова его реорганизовать, реорганизовал структуру, расширил некоторые функции и переопределил «семантические границы» различных символов, чтобы гарантировать, что операторы максимально совместимы с собственными операторами Java, и логическая структура тоже понятнее, без сюрпризов и т.д.

RunnerUtil в значительной степени относится к синтаксису JavaScript, например, использование фигурных скобок для представления пары «ключ-значение» «объект» (который фактически будет проанализирован в HashMap), имя ключа не нужно заключать в одинарные или двойные кавычки, одинарные кавычки и двойные кавычки Обе представляют собой обычные строки, заключенные в цепочку с помощью точек (.) и квадратных скобок. Так же удобнее писать студентам, которые занимаются JavaWeb-разработкой. Большинство функций уже реализовано, и реализованные функции также были протестированы, чтобы убедиться, что они могут «оправдать ожидания».Если у вас есть идеи и предложения, я надеюсь упомянуть больше.

Введение в основное использование

Строковые выражения выполняются через статический класс RunnerUtil, который можно запускать напрямую для получения результата выражения или запускать при необходимости после разбора выражения. RunnerUtil в основном имеет следующие методы:

  • RunnerUtil.run(/* выражение */); запустить выражение напрямую и получить результат;
RunnerUtil.run("1 + 1"); // 2
RunnerUtil.run(" 'Hello' + ' ' + 'World!' "); // "Hello World!"
  • RunnerUtil.run(/* выражение */, / * данные */); запустить выражение, содержащее переменные, за которыми следуют данные — это «значение», на которое будет указывать переменная;
  • RunnerUtil.parseRun(/* выражение */); запустить «другое» выражение напрямую и получить результат, например:
RunnerUtil.parseRun("Hello {{   'World!'   }}"); // "Hello World!"

Можно видеть, что #parseRun предназначен для запуска выражения, содержащего «синтаксис интерполяции», а обернутое содержимое выполняется только как выражение;


Строки могут содержать несколько выражений синтаксиса интерполяции, но они не могут быть вложенными и пересекаться, а также могут выполняться выражения, содержащие переменные.

  • Runner runner = RunnerUtil.parse(/* expression */);
// 更推荐这种先解析,再运行的方式
Runner runner = RunnerUtil.parse(" 1 + 2");
Integer result = runner.run(); // 3

Разобрать строковое выражение, получить "обработчик строковых выражений" - Runner, затем вызвать его метод run(/ * data */) для запуска и получить результат,Рекомендуется сначала разобрать Runner, а затем запустить его несколько раз..

Синтаксис и детали операции

Как вещь с определенными «языковыми характеристиками», он определяет некоторые собственные синтаксис, типы данных, типы операций и т. д., но большинство из них совместимы с Java и JavaScript, и одни и те же символы имеют одинаковые или близкие языковые значения.

тип данных:

  1. null: это ключевое слово, но поскольку оно соответствует правилам определения переменных, следует отметить, что true и false также определяются как ключевые слова.
  2. логическое значение: истина и ложь
RunnerUtil.run(" null   "); // null
RunnerUtil.run("   true "); // true
RunnerUtil.run("false"); // false
// 表达式中多余的空格自动忽略
  1. Числа: Числа в этом единообразно используются int и double данные в Java, и только эти два типа непосредственно участвуют в операции.Разница заключается в том, есть ли десятичная точка.
RunnerUtil.run(" 12  "); // 12
RunnerUtil.run(" 12.5 "); // 12.5
// 表示数字必须是连续,中间不能有空格的
// 否则将抛出异常,如
RunnerUtil.run(" 12. 5"); // 异常
RunnerUtil.run(" 1 2 "); // 异常

Символы, представляющие числа, должны быть непрерывными, например: 25, 36,9 и т. д. Если они не являются непрерывными, будет выдано исключение, например: 2 5, 36,9 и т. д.;

  1. Строка: Строки в Java заключаются в двойные кавычки. Здесь будут "реквизированы" одинарные кавычки, представляющие символы. Двойные кавычки и одинарные кавычки заключаются в прямые значения обычных строк. Это также для удобства запись (аналогично JavaScript Similar), и в то же время нет данных типа char...
RunnerUtil.run(" 'abcdef'  "); // "abcdef"
RunnerUtil.run(" \"abcdef\"  "); // "abcdef"
RunnerUtil.run(" 'abc   def'  "); // "abc   def"
  1. Список: на самом деле это ArrayList, который соответствует массиву в JavaScript. Массивы Java также соответствуют массивам JavaScript.
RunnerUtil.run(" { } "); 
// 总是返回一个空ArrayList

RunnerUtil.run(" {1,2,,4, } "); 
// 总是返回一个包含:1、2、null、4 这几项的 ArrayList

// 可以看出最后一个逗号之后如果是结束符号会自动忽略
// 中间的逗号与逗号之间若没有其他非空白符号会插入一个 null 值
  1. Карта: На самом деле HashMap, соответствующая объектам в JavaScript. Существуют также обычные POJO, соответствующие объектам JavaScript.

Карты соответствуют объектам в JavaScript, но здесь ключи Карты могут быть следующих типов данных:

null, true/false, числа (int/double), строки, никаких других объектов Java

RunnerUtil.run(" {:} "); // 总是返回一个空 HashMap,
// 注意与空 List 的异同,都是用花括号表示
// 但空 Map 里面需要有一个冒号,否则就是 List

RunnerUtil.run(" {key: 'value'}");
// 总是返回包含一个键值对的 HashMap
// 可以看出,对象的键名是字符串的话可以不用引号包裹
// 但是值必须被包裹
RunnerUtil.run(" {true: 'value'}"); // 键是 true
/*
 * 这里的 true 不是字符串,而是 boolean。
 * 同样,未被引号包裹的 null、false、数字都是对应类型的数据,而不是字符串
 * 其他符合变量命名规则的键都是普通字符串,被单引号或双引号包裹的也是
 */
RunnerUtil.run(" {'true': 'value', 25: false, 'name': \"张三\"}");

Тип операции поддержки:

  1. Общие четыре смешанные операции: +, -, *, /, %, ()
RunnerUtil.run(" 1 + 1 "); // 2
RunnerUtil.run(" 1 + (3 * 4)) "); // 13
RunnerUtil.run(" 'Hello ' + \"World!\" ");  // "Hello World!"
RunnerUtil.run(" true + false "); // "truefalse"
/*
 * true+false 在 Java 中是不允许的
 * 但如果是“+”运算的话,这里均作为普通字符串;
 * 相当于调用了 toString 方法
 */
  1. Побитовые операции: &, |, ^, >
RunnerUtil.run(" 1 ^ 1 "); 
RunnerUtil.run(" 1 & 1 "); 
RunnerUtil.run(" 1 | 1 "); 
RunnerUtil.run(" 1 << 1 "); 
RunnerUtil.run(" 1 >> 1 ");
  1. Операции сравнения: >, >=, ==,
RunnerUtil.run(" 1 + 1 == 2 "); // true
RunnerUtil.run(" 1 + 1 < 2 "); // false
  1. Логические операции: &&, ||, !
RunnerUtil.run("1+1==2 && 5 > 4"); // true
  1. Тернарная операция: assertExpression ? trueExpression : falseExpression
RunnerUtil.run("true ? 'name' : 'age'"); // name
RunnerUtil.run("false ? 'name' : 'age'"); // age
RunnerUtil.run("1 > 2 ? 'name' : 'age'"); // age
RunnerUtil.run("1 < 2 ? 'name' : 'age'"); // name
  1. Переменные: правила именования такие же, как и правила именования переменных Java, а null, true и false не могут использоваться в качестве переменных.

Если выражение содержит переменные, это означает, что выражение должно получить данные извне при выполнении, чтобы получить результат.Если данные не могут быть правильно прочитаны из источника данных, операция вызовет исключение;

RunnerUtil.run(" 'Hello, ' + name "); // 抛出异常

Map data = new HashMap();
data.put("name", "Li Lei!");

RunnerUtil.run(" 'Hello, ' + name ", data); // "Hello, Li Lei!"
  1. Цепочка: Синтаксис цепочки очень похож на JavaScript.
HashMap data = new HashMap(); 

ArrayList list = new ArrayList(); 
list.add(true); 
list.add(false); 
list.add(25); 
list.add('隔壁老王'); 

HashMap map = new HashMap(); 
map.put("name", "小四"); 
map.put("index", 2); 
map.put(true, "true 是 Boolean 类型作为键"); 

data.put("list", list); 
data.put("map", map); 

RunnerUtil.run("map.name", data); // "小四"

RunnerUtil.run("map['name']", data); 
// "小四" (也可以这样取值)

RunnerUtil.run("list[ 2 ]", data);
// 25 (索引取值需要用方括号包裹) 

RunnerUtil.run("list[3]", data);
// "隔壁老王" (索引取值需要用方括号包裹) 

RunnerUtil.run("list[map.index]", data); // 25
// (这是高级点的用法,方括号包含另一个表达式
// 返回值是一个索引,然后返回索引指向的值)

RunnerUtil.run("[true]", data); // "true 是 Boolean 类型作为键"
// 如果不用方括号包括,true 就是一个直接值,返回 true
// 那么问题来了:
// 如果传入的数据不是 Map 或 POJO,而是 List 或数组怎么办呢?
RunnerUtil.run(" [1] ", list); // false
// 啊……唐宗宋祖,略显风骚!

// 这种链式语法与 JavaScript 很相似
  1. Метод запуска: в настоящее время можно запустить только метод без параметров и с одним параметром, а метод с параметром переменной длины поддерживается не полностью, поэтому используйте его с осторожностью.
// 这里的数据 data 继续用上一条的 data,具体数据不写了

RunnerUtil.run("map.size()", data); // 3
RunnerUtil.run("map.get('name')", data); // "小四" 
RunnerUtil.run("map.get('name').length()", data); // 2
RunnerUtil.run("map.name.length()", data); // 2
RunnerUtil.run(" [3].length() ", list); // 4
// 唐宗宋祖,又显风骚!
  1. Запуск статического метода: @ ; Чтобы запустить статический метод, вам нужно использовать символ «@» в качестве маркера. Вызовы методов с несколькими аргументами также в настоящее время не поддерживаются.

Когда вы откроете исходный код, вы обнаружите, что это целая независимая библиотека инструментов, и многие методы аналогичны пакету commons-lang (лично я не думаю, что это не изобретать велосипед, там тоже много разных и более низкие)..., вы также можете запускать статические методы. Все методы инструментов в этой библиотеке инструментов на данный момент не были лишены RunnerUtil и не поддерживают вызовы пользовательских статических методов, но функции, предоставляемые этим инструментом библиотека

RunnerUtil.run("@System.currentTimeMillis() ");
// 15.....(一个毫秒数)
RunnerUtil.run("@Objects.toString(25) "); // "25"
  1. Пользовательский тип списка, тип карты, класс вызова статического метода.

public static class InnerObjects {
    public static String toString(Object o){
        return "123";
    }
}

RunnerSettings settings = RunnerSettings.builder()
    // 自定义生成的数组是 LinkedList,默认 ArrayList
    .setArrCreator(LinkedList::new)
    // 自定义生成的数组是 TreeMap,默认是 HashMap
    .setArrCreator(TreeMap::new)
    // 自定义静态方法类 Objects,将覆盖 java.util.Objects
    .addCaller("Objects", InnerObjects.class)
    .build();

Runner runner0 = RunnerUtil.parse("@Objects.toString('juejin shequ')");
Runner runner1 = RunnerUtil.parse("@Objects.toString('juejin shequ')", settings);

Object result0 = runner0.run(); // "juejin shequ"
Object result1 = runner1.run(); // "123"

Подводя итог, это операция формулы, поддерживаемая этим RunnerUtil.Перечисленные выше операции могут быть вложенными, связанными, но не пересекающимися. Здесь еще не опубликовано много сложных формул, цель состоит в том, чтобы запустить любую формулу, которая соответствует правилам выражений Java.

RunnerUtil изначально был написан для общего инструмента экспорта в Excel, сейчас функция экспорта реализована, функция импорта еще реализуется, и инструмент тоже находится в том же Github, который содержит практический пример и тестовую таблицу экспорта данных.

Следующее, что нужно сделать, это добавить многопараметрический вызов метода.Кстати, вам нужно убрать RunnerUtil? Если есть, пожалуйста, оставьте сообщение, и я найду время, чтобы снять его как можно скорее, в противном случае это займет некоторое время.

Я надеюсь, что это будет полезно для ежедневного развития каждого, и я также надеюсь, что вы дадите несколько мнений.Если есть ошибка, она должна быть исправлена ​​в кратчайшие сроки.Спасибо всем! !

Дополнительные примеры можно найти на Github.