OGNL (Object-Graph Navigation Language)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 02:25, 22 мая 2016.
OGNL
OGNLLogo.png
Парадигма Язык выражений
Спроектировано OGNL Technology
Язык физической реализации Java
Платформа Java Virtual Machine
OS Cross-platform
Лицензия BSD
Портал: http://commons.apache.org/ognl/

OGNL (Object Graph Navigation language - объектно-графовый язык навигации)[1] – язык выражений для получения и задания свойств java-объектов, имеющий также дополнительные возможности, такие как список проекций, выборка и лямбда выражения. Вы используете одни и те же выражения как для получения, так и для установления значения свойства.

OGNL класс содержит преимущественно методы для оценки OGNL выражений. Это делается в два этапа, разбор выражения во внутренние формы и последующие использование внутренней формы выражения для задания или получения значения свойства объекта; или вы можете сделать это в один этап через установление или получение значения свойства с использованием строчной формы выражения напрямую.

OGNL используется чаще всего язык связки данных между GUI представлениями и моделями объектов. Трансформации происходят проще за счет механизма автоматической конвертации типов. Большинство того, что вы можете сделать в Java, возможно и в OGNL.

Синтаксис

Основные OGNL[2] выражения являются очень простыми. Язык стал весьма богат функциями, но простые вещи остались простыми в реализации. Например, выражением, для получения имени свойства объекта в языке OGNL является просто property.name. Чтобы получить свойство text объекта, возвращенного свойством объекта headline, в языке OGNL существует выражение headline.text.

Что же такое свойство в языке OGNL? Грубо говоря, свойство OGNL похоже на свойство java-бинов (java-beans), определяющееся парой get/set методов.

Основной единицей в OGNL выражениях является навигационная цепочка, часто называемая просто "цепочкой". Простейшие цепи состоят из следующих частей:

Элемент выражения языка OGNL Пример
Имя свойства свойства name и headline.text, указанные выше
Вызовы методов hashCode() возвращает хэш-код текущего объекта
Массив индексов listeners[0] возвращает первый объект из списка слушателей (listeners)

Все выражения языка OGNL вычисляются в контексте текущего объекта, и цепь просто используется результат предыдущего элемента цепочки в качестве текущего объекта для следующего вычисления. Вы можете удлинять цепочки насколько вы хотите. К примеру:

name.toCharArray()[0].numericValue.toString()

Это выражение выполняет следующие действия:

  1. Извлекает свойство имени из начального или корневого объекта (который программист подает языку через контекст OGNL)
  2. Вызывает метод toCharArray() для результирующей строки
  3. Извлекает первый символ из результирующего массива символов
  4. Получить численный эквивалент символа (код символа). Символ представлен объектом класса Character, который содержит метод getNumericValue
  5. Вызов метода toString для полученного объекта типа Integer. Конечным результатом выражения является строка, возвращенная вызовом метода toString().

Выражения

В этой секции описаны детали элементов выражений языка OGNL.

Константы

OGNL имеет несколько типов констант:

  • Строковые литералы, как в Java (с возможностью описания строки посредством одиночных кавычек).
  • Символьные литералы, как в Java
  • Числовые литералы. В добавок к Java константам типов int, float и double, OGNL позволяет определить константы типа BigDecimal c суффиксами “b” или “B”, и константы типа BigInteger с суффиксами типа “h” или “H”.
  • Булевские литералы – true или false
  • Null литерал

Ссылки на свойства

OGNL обращается к различным типам объектов, различающихся в их обработке свойств ссылок. Ассоциативные массивы обрабатывают все свойства ссылок, как элемент поиска или хранения, со свойствами имени в качестве ключа. Списки и массивы обрабатывают числовые свойства аналогичным образом, со именем свойства в качестве индекса. Обычные объекты могут обрабатывать только свойства строк и делать это с помощью get и set методов, если объект имеет их, или поля с данными именами в противном случае.

Необходимо заметить, что имена свойств могут быть различных типов, не только строчные. Но для того, чтобы ссылаться на нестрочные свойства необходимо использовать так называемую индексную нотацию. К примеру для получения длины массива вы можете использовать выражение array.length Но для получения 0 элемента в массиве вы должны использовать следующее выражение: array[0]

Индексирование

Как упоминалось выше, индексная нотацию актуальна только для ссылок на свойства. К примеру OGNL обрабатывает выражение

array.length
в следующее выражение:
array[“length”]
(аналогично можно записать как
array[“len” + “gth”]
).

Индексация массивов и списков.

Для Java массивов и списосков индексация довольно проста, как и в Java. Целочисленный индекс возвращает элемент на который он ссылается. Если индекс находится за границами массива или списка то генерируется исключение IndexOutOfBoundsException (как и в Java).

Индексированные свойства JavaBeans.

JavaBeans поддерживает концепцию индексированный свойств. В частности это означает, что объект имеет множество методов, которые соответствуют следующему шаблону:

  • public PropertyType[] getPropertyName();
  • public void setPropertyName(PropertyType[] anArray);
  • public PropertyType getPropertyName(int index);
  • public void setPropertyName(int index, PropertyType value);

OGNL может обрабатывать эти методы и обеспечивать беспрепятственный доступ к свойству через индексную нотацию. Ссылка по типу someProperty[2] автоматически будет перенаправленно к методам доступа корректно индексированного свойства (в случае выше через методы getSomeProperty(2) или setSomeProperty(2, value)). Если методов доступа соответствующего индексированного свойства не будет найдено, то создается соответствующее свойство с именем someProperty и индекс прикрепляется к нему.

Индексирование свойств объекта OGNL.

OGNL расширяет концепцию индексируемых свойств на индексирование с произвольных объектов, не только целых, как в случае индексированных свойств JavaBeans. При нахождении свойств в качестве кандидатов для индексации объекта, OGNL просматривает шаблоны методов со следующей сигнатурой:

  • public PropertyType getPropertyName(IndexType index);
  • public void setPropertyName(IndexType index, PropertyTypevalue);

PropertyType and IndexType должны соответствовать их get и set методам. Хорошим примером будет использование индексированных свойств объекта на примере Servlet API: объект сессии имеет два метода для получения и установки свойств произвольных атрибутов.

public Object getAttribute(String name) public void setAttribute(String name, Object value)

OGNL выражение

session.attribute["foo"]

может одновременно устанавливать и получать значение свойства foo.

Вызов методов.

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

В частности, null аргумент соответствует всем не примитивным типам, и таким образом с большой долей вероятности это может привести к вызову неопределенного метода.

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

method( ensureLoaded(), name )

вызывает двухаргументный метод, в то время как

method( (ensureLoaded(), name) )

вызовет одноаргументный метод.

Ссылки на переменные

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

#var

OGNL также хранит текущий объект в каждой точке вычисления выражения в этой переменной, где он может упоминаться как любая другая переменная. К примеру, следующее выражение использует кол-во слушателей, возвращает его удвоенное значение если оно больше 100, иначе на 20 больше:

listeners.size().(#this > 100? 2*#this : 20+#this)

OGNL может быть вызван с ассоциативный массивом, который определяет начальные значения переменных. Стандартный способ вызова OGNL определяет корневые переменные и контекст в котором храниться ассоциативный массив.

Для явного присвоения некоторого значения переменной, достаточно написать оператор присвоения со ссылкой на переменную в левой части.

#var = 99

Выражения в скобках

Как и следовало ожидать, выражение, заключенное в круглые скобки рассматривается как единое целое, отдельно от любых окружающих операторов. Это может быть использовано, чтобы изменить приоритет операторов в выражении. Также единственным способом перечисления параметров метода является использования оператора запятой.

Последовательность подвыражений

Если вы используете выражение в скобках после точки, объект, который в точке является текущим используется в качестве текущего объекта по всех выражениях в скобках. Например,

headline.parent.(ensureLoaded(), name)

проходит через объект headline и свойство parent, гарантирует, что parent загружен, а затем возвращает (или задает) имя родителя.

Выражения верхнего уровня также могут быть соединены таким образом. Результатом выражения является самый правый элемент выражения.

ensureLoaded(), name

Сначала будет вызван ensureLoaded() для корневого объекта, затем будет получено свойствао имено корневого объекта как результат выражения.

Построение коллекций

Списки

Чтобы создать список объектов, заключите список выражений в фигурных скобках. Как и в случае с аргументами метода, эти выражения не могут использовать запятую, если она не в круглых скобках. Вот пример:

name in { null,"Untitled" }

Этот тест проверяет, соответствует ли свойство имени пустому объекту или равно "Untitled".

Синтаксис описано выше создаст экземпляром интерфейса List. Точная подкласс не определен.

Простые массивы.

Иногда вы хотите создать Java родные массивы, такие как int [] или integer []. OGNL поддерживает создание из них аналогично тому, как обычно вызываются конструкторы, но позволяет также инициализировать массив из существующего списка или заданного размера массива.

new int[] { 1, 2, 3 }

Это создает новую int массив, состоящий из трех чисел 1, 2 и 3.

Чтобы создать массив с null объектами или из элементов 0, используйте конструктор альтернативный, принимающий на вход размер массива.

new int[5]

Ассоциативные массивы

Ассоциативные массивы также могут быть созданы с помощью специального синтаксиса.

#{ "foo" : "foo value", "bar" : "bar value" }

Это выражение создает ассоциативный массив, инициализированный отображениями для "foo" и "bar".

Опытные пользователи, желающие выбрать определенный класс для реализации ассоциативного массива могут указать класс перед открывающей фигурной скобкой:

#@java.util.LinkedHashMap@{ "foo" : "foo value", "bar" : "bar value" }

Приведенный выше пример будет создать экземпляр класса LinkedHashMap JDK 1.4, обеспечивая, что заданный порядок элементов сохранится.

Проецирование всей коллекции

OGNL обеспечивает простой способ вызвать тот же метод или извлечь то же свойство из каждого элемента в коллекции и сохранить результат в новой коллекции. Это называется "проекция", определение из баз данных, обозначающее выбор подмножества столбцов из таблицы. Например, это выражение:

listeners.{delegate}

возвращает список всех делегатов слушателей.

Во время проекции #this переменная ссылается на текущий элемент итерации.

objects.{ #this instanceof String ? #this : #this.toString()}

В выражении выше будет создан новый список элементов из списка строковых объектов.

Выбор из коллекции

OGNL обеспечивает простой способ использования выражений, чтобы выбрать несколько элементов из коллекции и сохранять результаты в новой коллекции. Это называется "выборка" – понятие из баз данных для выбора подмножества строк из таблицы. Например, это выражение:

listeners.{? #this instanceof ActionListener}

возвращает список всех тех слушателей, которые являются экземплярами класса ActionListener.

Выбор первого совпадения.

Для того, чтобы получить первое совпадение из списка совпадений, вы могли бы использовать индексацию, например listeners.{? true }[0]. Тем не менее, это громоздкий способ, потому что если совпадение не даст никаких результатов (или, если результирующий список пуст), вы получите ArrayIndexOutOfBoundsException.

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

objects.{^ #this instanceof String }

Вернется первый элемент, содержащийся в objects и являющийся экземпляром класса String.

Выбор последнего совпадения.

Подобно получению первого совпадения, иногда вы хотите получить последний элемент, который соответствовал условию выборки.

objects.{$ #this instanceof String }
Это выражение вернет последний элемент, содержащийся в objects и являющийся экземпляром класса String.

Вызов конструкторов

Вы можете создавать новые объекты, как в Java, с новым оператором. Одно из отличий является то, что вы должны указать полное имя класса для всех классов кроме тех, что находятся в пакете java.lang.

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

Вызов статических методов

Вы можете вызвать статический метод, используя синтаксис @class@method(args. Если вы вышли из класса, то по умолчанию для java.lang.Math, чтобы сделать проще возможен вызов min и max методов. Если вы указали класс, вы должны дать полное имя.

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

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

Получение статического поля

Вы можете обратиться к статическим полем, используя синтаксис @class@field. У class должно быть указано полное имя.

Псевдо-лямбда выражения

OGNL имеет упрощенный синтаксис лямбда-выражений, которое позволяет писать простые функции. Это не полноценная лямбда-исчисление, потому что нет замыкания --- все переменные в OGNL имеют глобальную область применения.

Например, вот OGNL выражение объявляющее рекурсивную функцию факториала, а затем вызывает его:

#fact = :[#this<=1? 1 : #this*#fact(#this-1)], #fact(30H)
Лямбда-выражения это все, что есть внутри скобок. Переменная #this содержит аргумент для выражения, которое изначально имело значение 30, и затем уменьшается на один для каждого последующего вызова выражения.

OGNL обрабатывает лямбда-выражения как константы.

Примечания

Ссылки

  1. Википедия
  2. http://wiki.apache.org/commons/FrontPage