Хавьер Торрес, разработчик ПО, IBM
Обычно ресурсы проекта EMF (например, модели EMF Ecore) отображаются как единичные объекты в программе просмотра (см. левую часть рисунка 1). Ограничением данного подхода является то, что мы не можем исследовать модель, не открывая ассоциированный редактор. Это может быть особенно неприятно, когда разработка зависит от доменной модели. Одним из способов обхода этого ограничения является создание специализированного вида, который будет обеспечивать доступ к содержимому нужной доменной модели. Мы могли бы создать этот плагин с нуля или использовать существующие интегрированные среды для облегчения разработки. В данной статье подробно рассматривается процесс создания такого плагина. По завершении работы со статьей вы получите плагин просмотра, который можно использовать для навигации по модели Ecore (см. правую часть рисунка 1).
Рисунок 1. Типы систем навигации
Нам известно, что мы хотим получить от плагина ModelNavigator, но до начала его разработки мы должны четко понимать основы Eclipse-компонентов, которые будут использоваться. Мы хотим создать древовидное представление, отображающее иерархию модели. Подробная информация о древовидном представлении выходит за рамки данной статьи. Нужно отметить один важный аспект - древовидные представления обращаются к объектам модели через адаптер, называемый провайдером содержимого, и определяют, как объект должен визуализироваться через провайдер меток. В следующих разделах подробно описывается, как можно обратиться к данным провайдеров содержимого и меток, а затем демонстрируется навигатор по модели.
Интегрированная среда EMF.Edit обычно используется для создания редакторов EMF-моделей. Чтобы создать эти редакторы, интегрированная среда предоставляет возможности генерирования исходного кода команд и других классов для обеспечения программного доступа к моделям. Другим набором функциональных возможностей, предоставляемых данной интегрированной средой (и наиболее важными для нас), являются удобные классы, позволяющие отображать EMF-модели в программах просмотра. Интегрированная среда предоставляет эту возможность доступа через встроенные провайдеры содержимого и меток, использующие адаптер EMF-объектов специфического типа для отображения модели. Это классы AdapterFactoryContentProvider
и AdapterFactoryLabelProvider
, которые предоставляют объекты, метки и изображения для EMF-объектов путем делегирования работы адаптерам провайдеров элементов, знающих, как выполнять навигацию по EMF-модели. Эта схема работы изображена на рисунке 2. Она особенно полезна для нашего проекта, поскольку позволяет нам не знать, как настроить модель для просмотра. Мы просто делегируем эту задачу встроенным провайдерам.
Рисунок 2. Адаптер EMF.Edit из справочной информации по Eclipse
Интегрированная среда содержит эти провайдеры элементов для различных типов EMF-модели. Однако большинству из нас не очень хочется создавать отдельный навигатор для каждого типа модели. Мы хотим обращаться ко всему содержимому модели из одного навигатора. Вот где нужна сборная фабрика адаптеров. Она позволит нам отобразить объекты более чем из одной EMF-модели, предоставляя фабрику адаптеров, способную адаптировать объединение объектов из нескольких моделей.
Класс ComposedAdapterFactory
- это еще один из удобных классов EMF.Edit, выступающий в роли общего интерфейса для других фабрик адаптеров. Его преимущество, как мы увидим далее, заключается в том, что в наш навигатор нужно лишь включить провайдеры элементов для интересующих нас типов моделей как части сборной фабрики адаптеров. Затем сборная фабрика адаптеров просто делегирует их реализации другим провайдерам. Например, мы можем сделать так, чтобы наш навигатор отображал модели генератора, Ecore-модели, UML-модели и т.д. Когда наш вид пытается отобразить эти модели, провайдеры содержимого и меток будут просто делегировать эту функцию фабрике адаптеров, которая затем делегирует ее соответствующему провайдеру элемента, облегчая разработку и позволяя расширить наш ModelNavigator для поддержки нескольких доменных моделей.
Теперь, когда у нас есть полная интегрированная среда, позволяющая обращаться к содержимому модели в ModelNavigator, другим нужным нам компонентом является реальная программа просмотра, в которой будет размещаться наш навигатор. Можно расширить плагин views и реализовать нашу собственную часть этого вида, которая будет отображать нужное содержимое в виде дерева. Однако уже существует интегрированная среда для комбинирования содержимого различных доменов в один вид, позволяя пользователям управлять и осуществлять навигацию по модели редактора в программе просмотра. Такая интегрированная среда была представлена в Eclipse V3.2 как CNF (org.eclipse.ui.navigator
). Она позволяет разработчикам вносить содержимое, метки, действия, фильтры и другие возможности в один навигатор. Она обеспечивает способ интегрирования навигационных программ просмотра и унификацию работы пользователей.
CNF поддерживает одну программу просмотра для всех интеграторов моделей редакторов, позволяет использовать содержимое не управляемой ресурсами модели, а также позволяет пользователям выбирать, что они хотят увидеть в своих интегрированных программах просмотра. Плагин org.eclipse.ui.navigator.resources
является примером такой интегрированной среды в действии, работа которой отображается в виде Project Explorer. Он предоставляет декларативные расширения программы просмотра для модели IResource
. Эта интегрированная среда обеспечивает самый быстрый способ создания своей программы просмотра, управляя деталями реализации программы просмотра, а от нас требуется только реализация провайдеров содержимого и меток для нашей модели. Кроме того, она предоставляет пространство для расширения плагина на содержимое других моделей, а также позволяет использовать расширенные возможности интегрированной среды в нашей программе просмотра (например, сортировщики, фильтры, функциональность "перетащи и отпусти" и т.д.).
Теперь мы готовы увидеть эти компоненты в работе и начать создавать наш плагин навигации по модели.
Для создания ModelNavigator мы применим двухэтапный подход. Сначала мы настроим базовую структуру плагина и определим его поведение. Затем мы расширим этот простой плагин для отображения содержимого EMF-модели.
Первым шагом в создании ModelNavigator является создание его проекта. Для этого будем использовать мастер создания нового проекта: в меню File > New > Project выберите Plug-in Project, а затем нажмите кнопку Next.
Назовите проект плагина ModelNavigator
, установите ID плагина в значение com.ibm.navigator.example.modelnavigator
, активатор в значение com.ibm.navigator.example.modelnavigator.ModelNavigatorPlugin
и нажмите кнопку Finish.
Рисунок 3. Создание проекта плагина
Мы готовы начать разработку плагина. Первым шагом является создание вида, в котором будет размещаться наш навигатор. Для этого мы выполним необходимые расширения в плагине, чтобы создать view-часть и категорию, под которой вид будет появляться. В xml-файле плагина добавим расширение view.
В закладке Extensions:
- Нажмите кнопку Add.
- В закладке Extension points выберите org.eclipse.ui.views.
- Создайте категорию:
- Щелкните правой кнопкой мыши на расширении org.eclipse.ui.views.
- Выберите New > Category.
- Установите поле name в значение
com.ibm.navigator.example.modelnavigator.mncategory
.
- Установите поле ID в значение
Model Navigator
.
- Создайте view-часть:
- Щелкните правой кнопкой мыши на расширении org.eclipse.ui.views.
- Выберите New > View.
- Установите поле ID в значение
com.ibm.navigator.example.modelnavigator.mnview
.
- Установите поле name в значение
Model Navigator View
.
- Установите поле class в значение
org.eclipse.ui.navigator.CommonNavigator
.
- Установите поле category в значение
com.ibm.navigator.example.modelnavigator.mncategory
.
- Сохраните изменения в проекте.
Как уже упоминалось, мы передаем некоторую рутинную работу по реализации в CNF. Что касается нашего способа настройки вида, важно отметить, что вместо создания собственного класса, который будет расширять ViewPart
для нашего вида, мы сделали так, что вид использует общий класс navigator. Это также означает, что общий класс navigator, который мы указываем в поле класса, нужно добавить в class path нашего плагина. Поэтому добавьте org.eclipse.ui.navigator
в нужные плагины в закладке Dependencies.
Следующим шагом является настройка CNF-атрибутов для плагина, которые будут указывать поведение по умолчанию нашего навигатора.
Сначала мы ассоциируем вид с программой просмотра общего навигатора. В закладке Extensions:
- Нажмите кнопку Add.
- В закладке Extension points выберите org.eclipse.ui.navigator.viewer и нажмите кнопку Finish.
- Создайте программу просмотра:
- Щелкните правой кнопкой мыши на расширении org.eclipse.ui.navigator.viewer.
- Выберите New > viewer.
- Установите поле
viewerid
в значение ID вида, определенного выше (com.ibm.navigator.example.modelnavigator.mnview
).
Поведение плагина навигатора будет зависеть от содержимого, которое мы свяжем с ним, действий, которые мы свяжем с ним, и другими параметрами, которые мы установим в плагине во время использования CNF (фильтры, сортировщики и т.д.). В плагине элементы include выбирают, какие расширения видимы для программы просмотра. Расширения содержимого указывают интегрированной среде, как отображать содержимое в виде, а расширения действий указывают интегрированной среде, какие параметры доступны виду (меню содержимого и т.д.). Для запуска этой пустой версии навигатора мы добавим связывания содержимого и действий из модели IResource
, давая плагину привычное поведение Package Explorer. Поэтому создадим для нашего навигатора viewerContentBinding
, чтобы добавить расширение содержимого ресурса, и viewerActionBinding
, чтобы добавить расширения действий ресурса.
Для добавления расширения содержимого IResource
в закладке Extensions:
- Щелкните правой кнопкой мыши на расширении
org.eclipse.ui.navigator.viewer
.
- Выберите New > viewerContentBinding.
- Установите
viewerid
в значение ID view-части плагина (com.ibm.navigator.example.modelnavigator.mnview
).
- Добавьте связывание содержимого:
- Щелкните правой кнопкой мыши на
viewerContentBinding
, который мы только что создали.
- Выберите New > includes.
- Щелкните правой кнопкой мыши на элементе includes, который мы только что создали.
- Выберите New > contentExtension.
- Установите поле pattern в значение
org.eclipse.ui.navigator.resourceContent
.
Для связывания расширений действий IResource
в закладке Extensions:
- Щелкните правой кнопкой мыши на расширении org.eclipse.ui.navigator.viewer.
- Выберите New > viewerActionBinding.
- Установите поле viewerid в значение ID view-части плагина (
com.ibm.navigator.example.modelnavigator.mnview
).
- Добавьте связывание действий:
- Щелкните правой кнопкой мыши на viewerContentBinding, который мы только что создали.
- Выберите New > includes.
- Щелкните правой кнопкой мыши на элементе includes, который мы только что создали.
- Выберите New > actionExtension.
- Установите поле pattern в значение
org.eclipse.ui.navigator.resources
.*
На данном этапе у нас имеется настроенная структура навигатора. Мы увидели, как указать интегрированной среде навигатора тип содержимого, которое хотим отображать, и каких действий мы хотим от навигатора. Хотя мы еще не добавили эти действия, наш вид теперь определенно более полезен. Кроме того, мы уже знаем, как связать содержимое с программой просмотра, а потому могли бы даже расширить его и добавить специализированные или предопределенные фильтры. Это пока не совсем то, чего мы хотим, поэтому нужно добавить способность отображать EMF-модель в программе просмотра.
Для отображения модели домена EMF в навигаторе нам необходимо информировать интегрированную среду об используемой модели - как можно получить информацию об этой модели и как/когда отображать ее. Для этого нужно создать расширение содержимого, которое может использоваться сервисом содержимого навигатора для отображения модели домена EMF. Эта работа завершается созданием расширения к плагину org.eclipse.ui.navigator.navigatorContent
.
Расширение содержимого навигатора определяет провайдер содержимого и провайдер меток, которые могут использоваться для предоставления дочерних и родительских объектов для элемента EMF-модели. Расширение также определяет, когда оно может быть активировано для предоставления дочерних объектов, известных под названием triggerPoints
, или родительских объектов, известных под названием possibleChildren
. Имеется также множество других атрибутов содержимого навигатора, которые мы можем изменить для изменения поведения плагина, например, провайдеры действий, общие мастера, фильтры и т.д. Однако для создания плагина ModelNavigator эти дополнительные функциональные возможности нам не нужны.
Сначала мы должны создать новое расширение содержимого, где сможем указать провайдеров содержимого и меток, которые будут использоваться общим навигатором. Позже мы реализуем эти классы.
Для добавления расширений navigatorContent
в закладке Extensions:
- Нажмите кнопку Add.
- В закладке Extension points выберите
org.eclipse.ui.navigator.navigatorContent
и нажмите кнопку Finish.
- Создайте Navigator Content:
- Щелкните правой кнопкой мыши на расширении org.eclipse.ui.navigator.navigatorContent.
- Выберите New > navigatorContent.
- Установите поле ID в значение
com.ibm.navigator.example.modelnavigator.emfModelContent
.
- Установите поле name в значение
EMF Model Content
.
- Установите поле Content provider в значение
com.ibm.navigator.example.modelnavigator.MNViewContentProvider
.
- Установите поле Label provider в значение
com.ibm.navigator.example.modelnavigator.MNViewLabelProvider
.
- Установите поле Priority в значение
normal
.
- Установите поле activeByDefault в значение
true
.
Далее мы реализуем классы, которые будут выступать в роли провайдеров содержимого и меток для содержимого этого навигатора.
Мы будем использовать интегрированную среду EMF.Edit для выполнения реальной работы провайдеров содержимого и меток. Следовательно, нам нужно создать классы провайдеров содержимого и меток как подклассы фабрик адаптеров из интегрированной среды редактирования EMF. При созданиеи классов провайдеров содержимого и меток, которые делегируют свои функции AdapterFactoryContentProvider
и AdapterFactoryLabelProvider
соответственно, мы должны помнить о том, что экземпляры этих фабрик адаптеров создаются с использованием списка провайдеров элементов для различных EMF-элементов. Это означает, что мы должны создать сначала этот список провайдеров как сборную фабрику адаптеров. Создайте новый класс (File > New > Class) под названием MNComposedAdapterFactory
.
Рисунок 4. Создание классов реализации
Мы также должны добавить org.eclipse.emf.codegen.ecore.ui
в нужные плагины в закладке Dependencies. Это сделает провайдеры элементов ComposedAdapterFactory
для различных типов EMF-модели и другие удобные классы EMF.Edit доступными для плагина. Реализация класса MNComposedAdapterFactory
может выглядеть примерно так, как показано в листинге 1.
Листинг 1. Наш класс ComposedAdapterFactory
...
public class MNComposedAdapterFactory
{
private static ComposedAdapterFactory mnCompAdapterFactory;
public final static ComposedAdapterFactory getAdapterFactory()
{
if (mnCompAdapterFactory == null)
mnCompAdapterFactory = new ComposedAdapterFactory(createFactoryList());
return mnCompAdapterFactory;
}
public final static ArrayList<AdapterFactory> createFactoryList()
{
ArrayList<AdapterFactory> factories = new ArrayList<AdapterFactory>();
factories.add(new ResourceItemProviderAdapterFactory());
factories.add(new EcoreItemProviderAdapterFactory());
factories.add(new ReflectiveItemProviderAdapterFactory());
return factories;
}
}
|
Важно отметить, что мы используем static-метод для создания списка провайдеров элементов. Мы хотим, чтобы фабрика адаптеров делегировала работу провайдеру EcoreItemProviderAdapterFactory
, для того чтобы предоставить содержимое и метки и для модели Ecore.
Начиная с провайдера содержимого (MNViewContentProvider
), создайте класс, нажав ссылку рядом с названием провайдера на панели деталей расширения, либо через диалоговые окна File > New > Class. Вместо реализации интерфейса ITreeContentProvider
для провайдера содержимого мы используем классы EMF.Edit. Для этого измените класс MNViewContentProvider
так, чтобы он расширял класс AdapterFactoryContentProvider
. Класс AdapterFactoryContentProvider
уже знает, как обработать реализацию интерфейса ITreeContentProvider
, что на самом деле он выполняет путем делегирования этой функции соответствующему провайдеру элемента, предоставляющему содержимое программе просмотра.
Класс AdapterFactoryContentProvider
ожидает в своем конструкторе фабрику адаптеров. Поэтому мы не можем использовать неявный конструктор для нашего провайдера содержимого. Мы должны явно объявить конструктор содержимого и вызвать конструктор суперкласса с нужными параметрами. После объявления конструктора добавьте в него super(MNComposedAdapterFactory.getAdapterFactory());
. Другими методами, которые нам понадобятся в этом классе, являются getChildren
, getParent
, hasChildren
и getElements
.
Метод getChildren
вызывается, когда программа просмотра должна отобразить дочерние элементы доменного объекта. Он возвращает массив доменных объектов, являющихся потомками элемента параметра. Аналогично, метод getElements
вызывается для получения доменного объекта элемента параметра. Эти два метода выполняют аналогичную функцию; просто они вызываются в разное время. Для реализации метода getChildren
мы просто запрашиваем AdapterFactoryContentProvider
возвратить потомков данного URI предка. Метод getChildren
показан в листинге 2. Для метода getElements
мы просто возвращаем результат вызова метода getChildren
.
Листинг 2. Реализация getChildren
...
public Object[] getChildren(Object parentElement)
{
if (parentElement instanceof IFile)
{
String path = ((IFile)parentElement).getFullPath().toString();
URI uri = URI.createPlatformResourceURI(path, true);
parentElement = resourceSet.getResource(uri, true);
}
return super.getChildren(parentElement);
}
...
|
Двумя следующими важными шагами по отображению содержимого в древовидном представлении являются способность определить, когда объект в виде имеет потомков, которых нужно отобразить, и способность ассоциировать нескольких потомков с родительским объектом. Это позволяет программе просмотра управлять состоянием доменных объектов, независимо от того, в каком состоянии они находятся - развернутом или свернутом. В нашем примере это можно перевести в модель Ecore, имеющую несколько дочерних пакетов. В этих пакетах есть объекты классов, ассоциированные с одним и тем же родительским пакетом. Реализация приведена в листинге 3.
Листинг 3. Реализация getParent
...
public Object getParent(Object element)
{
if (element instanceof IFile)
return ((IResource)element).getParent();
return super.getParent(element);
}
public boolean hasChildren(Object element)
{
if (element instanceof IFile)
return true;
return super.hasChildren(element);
}
...
|
Здесь есть некоторые ошибки, связанные с неспособностью разрешить используемые ресурсы. Чтобы исправить ситуацию, добавьте org.eclipse.core.resources
в нужные плагины в закладке Dependencies. Также, чтобы позволить провайдеру содержимого получить URI файловых ресурсов в виде, мы должны использовать набор ресурсов из платформы. Мы создаем реализацию одного набора статических ресурсов, который может использоваться для получения этих ресурсов путем добавления private static ResourceSetImpl resourceSet = new ResourceSetImpl();
в класс MNViewContentProvider
.
Провайдер меток следует тому же формату, что и провайдер содержимого - мы просто делегируем работу классу AdapterFactoryLabelProvider
из интегрированной среды EMF.Edit. Как и провайдер содержимого, провайдер меток принимает доменные объекты в качестве своих аргументов, но возвращает Image
или String
, которые должны быть ассоциированы с этим объектом в программе просмотра. Чтобы сделать программу просмотра несколько более специализированной, можно программно управлять тем, когда делегировать задачу получения названий и пиктограмм, а когда предоставлять свои собственные пиктограммы для объекта. Но мы не будем делать какую-либо специальную обработку, а просто используем изображения и текст, которые предоставляет EMF-модель. Для реализации провайдера меток мы вызываем суперкласс с тем же списком провайдеров элементов, который использовали для провайдера содержимого, а затем делегируем все вызовы на получение изображений или описаний объектов суперклассу AdapterFactoryLabelProvider
.
Листинг 4. Провайдер меток
...
public MNViewLabelProvider()
{
super(MNComposedAdapterFactory.getAdapterFactory());
}
public Image getImage(Object element)
{
return super.getImage(element);
}
public String getText(Object element)
{
return super.getText(element);
}
...
|
На данном этапе мы завершили работу по реализации расширения содержимого в нашем плагине. Чтобы разрешить нашему виду использовать это расширение содержимого, нужно выполнить еще два действия. Сначала мы должны определить события, которые будут сигнализировать об использовании конкретного расширения содержимого. Затем мы должны связать содержимое навигатора с видом.
Мы сигнализируем CNF, когда можем отобразить описанную в наших классах доменную модель, путем добавления элементов <possibleChildren />
и <triggerPoints />
в расширение содержимого навигатора. Эти два элемента легко определить как базовые расширения Eclipse в xml-файле плагина. В нашем случае содержимое навигатора будет активироваться, когда программа просмотра содержит объект, являющийся экземпляром EMF-модели. Поскольку в данный момент мы хотим только выполнять навигацию по Ecore-модели, можем сделать простое расширение, которое будет проверять, является ли расширение ресурса Ecore-моделью. Возможный дочерний элемент указывает, когда наше расширение может предоставить предков объекта в программе просмотра - в нашем случае это объекты, являющиеся экземплярами объектов или ресурсов EMF-модели.
Листинг 5. Триггерные точки и возможные дочерние элементы
<triggerPoints>
<or>
<and>
<instanceof value="org.eclipse.core.resources.IResource"/>
<test
forcePluginActivation="true"
property="org.eclipse.core.resources.extension"
value="ecore"/>
</and>
</or>
</triggerPoints>
<possibleChildren>
<or>
<instanceof value="org.eclipse.emf.ecore.resource.Resource"/>
<instanceof value="org.eclipse.emf.ecore.EObject"/>
</or>
</possibleChildren>
|
Наконец, для связывания созданного содержимого навигатора с нашим видом мы будем использовать такой же метод, который использовали ранее для включения расширения содержимого модели IResource
в связывание содержимого программы просмотра.
Для связывания содержимого навигатора com.ibm.navigator.example.modelnavigator.emfModelContent
с реальным видом, в закладке Extensions:
- Разверните элемент
org.eclipse.ui.navigator.viewer
.
- Разверните элемент
viewerContentBinding
.
- Создайте новое расширение содержимого:
- Щелкните правой кнопкой мыши на элементе includes.
- Выберите New > contentExtension.
- Установите поле pattern в значение
navigatorContent
, которое мы определили (com.ibm.navigator.example.modelnavigator.emfModelContent
).
- Нажмите кнопку Save.
Это практически все, что нужно для добавления содержимого EMF-модели в общий навигатор. У нас есть плагин ModelNavigator, который может отображать объекты из модели IResource
и модели EMF Ecore.
|
Тестирование на EMF-проекте Для выполнение данного теста можно выбрать импортирование существующего EMF-проекта в рабочую область времени исполнения Eclipse или создать новый проект. В "Руководстве разработчика EMF" рассказывается, как создать новый проект, импортирующий UML-модель, которую можно загрузить и установить. | |
Мы должны запустить проект и отобразить Ecore-модель в нашем новом виде для проверки работы ModelNavigator. Для этого теста необходимо создать EMF-проект, который будет отображаться в ModelNavigator. Будем использовать пример, основанный на UML-файле SchoolLibrary
.
Запустите проект ModelNavigator как приложение Eclipse, а затем:
- В новой рабочей среде отобразите вид ModelNavigator, выбрав Window > Show View > Other.
- Найдите категорию Model Navigator и щелкните на Model Navigator View.
- Нажмите кнопку OK.
Для создания EMF-проекта в рабочей области времени исполнения:
- Создайте новый проект, выбрав File > New > Project.
- Разверните элемент Eclipse Modeling Framework.
- Щелкните EMF project и нажмите кнопку Next.
- Назовите проект
SchoolLibrary
и нажмите кнопку Next.
- Выберите Rose class model в model importer и нажмите кнопку Next.
- Для model URI найдите месторасположение, куда загрузили файл schoollibrary.mdl.
- Нажмите кнопку Next.
- Выберите library и пакеты SchoolLibrary, как показано на рисунке 5.
- Нажмите кнопку Finish.
Рисунок 5. Создание тестового EMF-проекта
Если все прошло по плану, должен отобразиться новый проект SchoolLibrary
в нашем виде Model Navigator. Более того, если наш навигатор работает так, как нужно, мы должны суметь развернуть каталог моделей проекта и выполнить навигацию по содержимому Library
и файлам SchoolLibrary
Ecore в виде нашего навигатора. Навигатор должен выглядеть примерно так, как показано на рисунке 6.
Рисунок 6. Тестирование ModelNavigator
Мы завершили то, что наметили: отобразили в программе просмотра содержимое EMF-модели. Что произойдет, когда мы действительно откроем Ecore-файл в редакторе и начнем выполнять изменения? На данном этапе ничего не произойдет. Наш ModelNavigator не обращает внимания на изменения, сделанные в отображаемых им моделях, и не будет обновлять их. Он не будет их обновлять до тех пор, пока мы не дадим знать об этих изменениях программе просмотра и не заставим ее обновить себя. Одним способом создания такой синхронизации является добавление прослушивателя изменений ресурса в наш провайдер содержимого. В следующих разделах рассматриваются необходимые действия.
Для того чтобы наш провайдер содержимого реагировал на изменения в модели, мы разрешаем ему прослушивать изменения в файловом ресурсе модели (в данном случае Ecore-файл). Для этого необходимо реализовать интерфейсы IResourceChangeListener
и IResourceDeltaVisitor
в нашем классе MNViewContentProvider
.
При изменении ресурса вызывается метод resourceChanged(IResourceChangeEvent event)
и передается набор событий, описывающих изменения в ресурсах. Этот набор изменений предоставляет нам различия в дереве ресурсов как разницу во времени. Мы можем использовать это для прохода по изменениям в дереве ресурсов и определения действий, которые необходимо выполнить на основе типа измененного ресурса. Мы реализуем метод resourceChanged(IResourceChangeEvent event)
, показанный в листинге 6, который предоставляет нам набор измененных ресурсов и позволяет просмотреть разницу (delta).
Листинг 6. Метод resourceChanged
...
try
{
IResourceDelta delta = event.getDelta();
delta.accept(this);
}
catch (CoreException e)
{
...
|
Метод visit(IResourceDelta delta)
активизируется после принятия разницы вызывающей стороной (visitor). Для нашего плагина мы интересуемся только изменениями ресурса - файла IResource, имеющего расширение Ecore. Если измененным ресурсом была Ecore-модель, необходимо обновить ModelNavigator для отражения этих изменений. Для этого мы берем измененный файл, получаем его ресурс, затем перезагружаем модель, которую представляет этот ресурс.
Листинг 7. Метод visit
...
IResource changedResource = delta.getResource();
if (changedResource.getType() == IResource.FILE
&& changedResource.getFileExtension().equals("ecore"))
{
try
{
String path = ((IFile)changedResource).getFullPath().toString();
URI uri = URI.createPlatformResourceURI(path, true);
Resource res = resourceSet.getResource(uri, true);
res.unload();
res.load(resourceSet.getLoadOptions());
}
catch(IOException ie)
{
System.out.println("Error reloading resource - " + ie.toString());
}
return false;
}
return true;
...
|
Для использования прослушивателя изменений ресурсов необходимо добавить его в провайдер содержимого и удалить, когда нужно. В классе MNContentProvider
добавьте прослушиватель, вставив ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
в конструктор. Также вставьте ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
в метод dispose
класса. Хотя таким образом мы позаботились об обновлениях при изменениях ресурсов, помните, что это упрощенный метод обработки изменений. Просто обновляя используемую модель в провайдере содержимого, мы не учитываем зависимостей модели при обновлении этих элементов. Это всего лишь базовый способ поддержки навигатора в обновленном состоянии.
Для тестирования того, что обновления в Ecore-модели отражаются в программе просмотра, мы просто перезапустим наше приложение и начнем выполнять некоторые изменения.
Выполните проект ModelNavigator как приложение Eclipse, а затем:
- Перейдите к schoollibrary.ecore в каталоге модели.
- Откройте файл schoollibrary.ecore в его редакторе.
- Разверните узел schoollibrary.ecore.
- Выполните щелчок правой кнопкой мыши на узле пакета SchoolLibrary.
- Выберите New Child > EClass.
- В закладке Properties дайте название новому классу (
Test
).
- Сохраните файл.
Это изменение должно активизировать код прослушивателя изменений ресурсов и заставить наш ModelNavigator обновить модель. Мы должны увидеть, что вид ModelNavigator перезагрузил себя. Если развернуть дерево и перейти к файлу schoollibrary.ecore, мы должны увидеть выполненные изменения, как показано на рисунке 7.
Рисунок 7. ModelNavigator с обновленными ресурсами
Последним (и довольно простым) расширением, которое мы можем реализовать для нашего ModelNavigator, является использование Composed Adapter Factories. Созданный нами навигатор будет отображать Ecore-модели, но как насчет других типов EMF-моделей? На самом деле это превращается в простую задачу, благодаря способу реализации интегрированной среды EMF.Edit. Мы можем добавить поддержку большего числа доменных моделей с минимальными изменениями путем добавления соответствующих фабрик адаптеров к нашей ComposedAdapterFactory
и установки триггеров для этих новых моделей.
Например, для добавления объектов genmodel
в наш навигатор мы просто добавляем провайдер элемента модели генератора в список фабрик адаптеров, который используют наши провайдеры содержимого и меток. В классе MNComposedAdapterFactory
добавьте factories.add(new GenModelItemProviderAdapterFactory());
в метод createFactoryList()
. Единственное, что нужно изменить в навигаторе по модели, - указать содержимому навигатора, когда можно активировать этот провайдер содержимого. Используем ту же стратегию проверки расширения ресурса - триггерную точку (см. листинг 8).
Листинг 8. Триггерная точка для genmodel
<and>
<instanceof value="org.eclipse.core.resources.IResource"/>
<test
forcePluginActivation="true"
property="org.eclipse.core.resources.extension"
value="genmodel"/>
</and>
|
Добавляя это новое расширение триггера прямо под нашим текущим триггером, мы делаем содержимое навигатора релевантным в случае, когда объект является экземпляром IResource
и имеет расширение Ecore, или когда он является экземпляром IResource
и имеет расширение genmodel
. Если мы выполним этот проект опять, то увидим, что можем осуществлять навигацию по моделям Ecore и Generator EMF (см. рисунок 8). Однако мы не планировали такое поведение в нашей упрощенной схеме обновления ресурсов, поэтому обновления различных моделей не будут синхронизироваться в виде ModelNavigator.
Рисунок 8. ModelNavigator с несколькими моделями
Мы завершили выполнение задачи по созданию плагина, отображающего содержимое EMF-модели в программе просмотра. Мы продемонстрировали, как упростить этот процесс, используя интегрированные среды, предоставляемые Eclipse, и даже пошли несколько дальше, добавив в плагин некоторые простые функциональные возможности. Мы рассмотрели только небольшую часть из возможностей, предоставляемых данными интегрированными средами, поэтому настоятельно рекомендуем детально исследовать эти компоненты Eclipse.
Файлы для загрузки