Как создать общее выражение MVEL

Я пишу общее выражение MVEL для объектов Java. Поэтому для понимания цели я беру один пример.

class Student {

   String name;

   String rollNo;

   List<Course> courses;
}

class Course {
   
   String courseName;

   String facultyName;

   String fee;

   String classRoom;
}

Я могу написать выражение MVEL. Если мой запрос: Проверить, дал ли учащийся имя и номер списка

MVEL.eval(" name == 'XYZ' && rollNo == '3456' ", Student)

Но если запрос: Проверить, посещает ли учащийся все курсы в одном классе?

MVEL.eval(" courses[0].classRoom == 'A' ", Student);

Но это проверяет classRoom только в одном курсе. Но я хочу проверить, проходят ли все курсы учащегося в классе «А». Я не мог найти никаких ресурсов для решения этой проблемы. Я совсем новичок в МВЭЛ. Если у вас есть какие-либо сомнения, пожалуйста, спросите меня.

Спасибо


person Shivam Kumar    schedule 24.12.2020    source источник


Ответы (1)


Для этого необходимо выполнить несколько шагов: вам нужно будет использовать переменные MVEL и его foreach оператор. .

Вот подход:

import org.mvel2.MVEL;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

public class App {
    public static void main(String[] args) {
        
        List<Course> courses = new ArrayList<>();
        courses.add(new Course("Math", "123"));
        courses.add(new Course("Physics", "123"));
        courses.add(new Course("Chemistry", "123"));
        
        Student student = new Student("XYZ", "3456", courses);
                
        // Check If a student has given name and rollNo:
        boolean b1 = MVEL.eval(" name == 'XYZ' && rollNo == '3456' ", student, Boolean.class);
        boolean b2 = MVEL.eval(" name == 'XYP' && rollNo == '3456' ", student, Boolean.class);
        //System.out.println(b1);
        //System.out.println(b2);
        
        // Check If a student attends all the courses in the same classroom:
        boolean allClassesInSameRoom = true;
        String prevRoom = null;
        
        Map vars = new HashMap();
        vars.put( "sameRoom", allClassesInSameRoom );
        vars.put( "prevRoom", prevRoom );
        
        String expression = String.join("\n", 
                "sameRoom = true;",
                "prevRoom = null;",
                "foreach (course : courses) {",
                "  if (sameRoom == true && prevRoom != null && course.classRoom != prevRoom) {",
                "    sameRoom = false;",
                "  }",
                "  prevRoom = course.classRoom;",
                "}",
                "sameRoom");
        
        allClassesInSameRoom = MVEL.eval(expression, student, vars, Boolean.class);
        
        System.out.println(allClassesInSameRoom);
        
    }
    
}

Сначала мы определяем две переменные Java, которые необходимы в скрипте MVEL. Они передаются оператору МВЭЛ eval через карту Java:

boolean allClassesInSameRoom = true;
String prevRoom = null;

Map<String, Object> vars = new HashMap<>();
vars.put( "sameRoom", allClassesInSameRoom );
vars.put( "prevRoom", prevRoom );

Затем мы создаем строку, содержащую сценарий MVEL, который нам нужно использовать:

String expression = String.join("\n", 
        "sameRoom = true;",
        "prevRoom = null;",
        "foreach (course : courses) {",
        "  if (sameRoom == true && prevRoom != null && course.classRoom != prevRoom) {",
        "    sameRoom = false;",
        "  }",
        "  prevRoom = course.classRoom;",
        "}",
        "sameRoom");

Последняя строка возвращает переменную sameRoom.

Оператор eval собирает нужные нам элементы:

allClassesInSameRoom = MVEL.eval(expression, student, vars, Boolean.class);

Обратите внимание, что класс Course должен быть определен как public:

public class Course {

    private final String courseName;
    private final String classRoom;

    public Course (String courseName, String classRoom) {
        this.courseName = courseName;
        this.classRoom = classRoom;
    }
    
    public String getCourseName() {
        return courseName;
    }

    public String getClassRoom() {
        return classRoom;
    }

}

Сценарии MVEL можно создавать как шаблоны, что, как мне кажется, может помочь избежать несколько запутанного процесса построчного построения Java String, как я делаю здесь.

Все это может потребовать больше работы, чем простая проверка объекта student без использования MVEL. Но MVEL также поддерживает функции и лямбда-выражения, так что, вероятно, есть способы рационализировать мой подход и упростить сценарий MVEL.


Обновить

Вот пример, который немного более компактен:

Map<String, String> rooms = new HashMap<>();
Map<String, Object> vars2 = new HashMap<>();
vars2.put( "rooms", rooms );
        
String expression2 = String.join("\n", 
        "foreach (course : courses) {",
        "  rooms.put(course.classRoom, course.classRoom);",
        "}",
        "(rooms.size() == 1) ? true : false;");
        
allClassesInSameRoom = MVEL.eval(expression2, student, vars2, Boolean.class);
System.out.println(allClassesInSameRoom);

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


В качестве альтернативы вы можете подсчитать количество разных названий комнат в списке курсов учащегося с помощью Java (Java 8 или выше):

import static java.util.stream.Collectors.toList;

...

long roomsCount = courses.stream()
        .map(Course::getClassRoom)    // get the room name from each course
        .collect(toList())            // build a list of these room names
        .stream().distinct().count(); // count the number of unique room names

Это одна строка кода. Но поскольку ваша цель — изучить и понять MVEL, это просто примечание.

person andrewjames    schedule 24.12.2020
comment
Спасибо за помощь. Но выражение выглядит сложным. Можем ли мы упростить его еще больше? Можем ли мы сделать что-то вроде, например: выражение = курсы[REGEX].classRoom == 'A'. Где REGEX — регулярное выражение. - person Shivam Kumar; 25.12.2020
comment
Чтобы получить доступ к списку, вы можете получить доступ к определенному элементу в списке, используя course[5] (см. здесь ) или вы можете использовать оператор foreach (как показано в моем ответе) и аналогичные итераторы. Я упомянул лямбда-выражения в своем ответе - и вы, вероятно, можете написать что-то на MVEL, который будет немного более компактным, но опять же, вы могли бы написать что-то эквивалентное и столь же компактное, используя лямбда-выражения Java. - person andrewjames; 25.12.2020
comment
Я добавил обновление, чтобы показать немного более простое выражение. - person andrewjames; 25.12.2020