Статьи

Расширение EMP ItemProviders с помощью Google Guice — I

EMF ItemProvider — самый важный посредник в среде EMF, предоставляющий все интерфейсы, необходимые для просмотра или редактирования объектов модели. Они адаптируют объекты EMF, обеспечивая следующее:

1. Поставщики содержимого и меток, которые позволяют просматривать объекты модели в средствах просмотра Eclipse.
2. Источник свойств объектов модели, чтобы сделать их доступными в представлении свойств Eclipse.
3. Команды для редактирования объектов модели.
4. Пересылка уведомлений об изменениях в средства просмотра Eclipse.

EMF генерирует ItemProviders для вас, принимая некоторые значения по умолчанию. Хотя значения по умолчанию работают большую часть времени, бывают случаи, когда вы хотите изменить их. Вы можете вносить изменения непосредственно в сгенерированный код и добавлять  аннотации @generated NOT . Однако, делая это, вы приглашаете неприятности от гуру MDSD. Потому что принцип гласит: «Не трогай сгенерированный код!». В идеале вы хотели бы сохранить сгенерированные ItemProviders как есть и как-то дополнить их своими изменениями.

В учебнике показано, как сделать это элегантным способом с помощью Dependency Injection (DI). Мы будем использовать пример EMF «Модель расширенной библиотеки», чтобы провести нас через это.

Настроить

1. Создайте новое рабочее пространство Eclipse

2. Добавьте проекты «Пример расширенной библиотеки EMF», используя «Мастер новых проектов»

3. Запустите примеры проектов и создайте образец модели библиотеки.

Расширение ItemProviders в EMF

Сгенерированный редактор по умолчанию отображает название книги в древовидном редакторе. Это связано с реализацией getText () по умолчанию в BookItemProvider .

/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public String getText(Object object)
{
String label = ((Book)object).getTitle();
return label == null || label.length() == 0 ?
getString("_UI_Book_type") : //$NON-NLS-1$
getString("_UI_Book_type") + " " + label; //$NON-NLS-1$ //$NON-NLS-2$
}

Допустим, мы хотим изменить это так, чтобы количество страниц в книге также отображалось вместе с ее заголовком. Вы можете сделать это, изменив сгенерированный код и добавив аннотацию @generated NOT .

/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
@Override
public String getText(Object object)
{
String label = ((Book)object).getTitle() + " (" + ((Book) object).getPages() + ") ";
return label == null || label.length() == 0 ?
getString("_UI_Book_type") : //$NON-NLS-1$
getString("_UI_Book_type") + " " + label; //$NON-NLS-1$ //$NON-NLS-2$
}

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

Это, однако, нарушает Шаблон Пробела Поколения в генерации кода.

Расширение ItemProviders по расширению

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

EMF ItemProviders на самом деле являются адаптерами EMF. Они обеспечивают некоторые поведенческие расширения моделируемых объектов. Рекомендуемый способ создания адаптеров в EMF — использование AdapterFactories. ItemProviders также создаются таким образом, используя сгенерированный ItemProviderAdapterFactory, EXTLibraryItemProviderAdapterFactory в этом примере. Вы можете переопределить методы createXXXAdapter (), чтобы создать экземпляр вашего пользовательского ItemProvider и зарегистрировать свой расширенный ItemProviderAdapterFactory.

Давайте сначала сделаем это традиционным способом (без DI).

1. Следуя   статье Heiko Generation Gap Pattern , вы можете изменить extlibrary.genmode l для вывода сгенерированного кода в папку src-gen плагина редактирования и поместить свои расширения в папку src . В этом руководстве для дальнейшей изоляции наших изменений мы создадим новый плагин расширения  org.eclipse.example.library.edit.extensio n.

2. Создайте новый класс  BookItemProviderExtension, который расширяет  BookItemProvider в новом плагине.

public class BookItemProviderExtension extends BookItemProvider {

public BookItemProviderExtension(AdapterFactory adapterFactory) {
super(adapterFactory);
}

@Override
public String getText(Object object) {
return super.getText(object) + " (" + ((Book) object).getPages() + ") ";
}

}

3. Создайте  EXTLibraryItemProviderAdapterFactoryExtension, который расширяет  EXTLibraryItemProviderAdapterFactory

public class EXTLibraryItemProviderAdapterFactoryExtension extends
EXTLibraryItemProviderAdapterFactory {

@Override
public Adapter createBookAdapter() {
if (bookItemProvider == null)
{
bookItemProvider = new BookItemProviderExtension(this);
}
return bookItemProvider;
}

}

4. Измените код редактора, чтобы использовать новый ItemProviderAdapterFactoryExtension

public class EXTLibraryEditor extends MultiPageEditorPart
implements
IEditingDomainProvider,
ISelectionProvider,
IMenuListener,
IViewerProvider,
IGotoMarker
{
...
protected void initializeEditingDomain()
{
// Create an adapter factory that yields item providers.
//
adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new EXTLibraryItemProviderAdapterFactoryExtension());
adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
...
}
...
}

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

Расширение ItemProviders с помощью Google Guice

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

1. вам нужно несколько реализаций ItemProvider для одного и того же объекта EMF и вы хотите переключаться между ними
2. вы хотите расширить множество ItemProvider в иерархии наследования. Например, вам нужно изменить PersonItemProvider и WriterItemProvider  (который расширяет PersonItemProvider) .

Хотя вам не нужно использовать DI для решения этих проблем, DI сделает это для вас более простым и понятным способом. В этом уроке мы будем использовать Google Guice для достижения этой цели. Google Guice — это классная легковесная структура для инъекций. Вы можете внедрить свои зависимости, просто написав несколько строк кода и несколько аннотаций. Если вам не нравятся аннотации, вы можете даже использовать Guice без них. Если вы не знакомы с Google Guice, прочтите раздел « Начало работы» .

Давайте продолжим и «уловим» наш предыдущий пример. Мы начнем с простых модификаций и перейдем к подробным последующим шагам.

1. Во-первых, вам нужно добавить зависимость от Google Guice из нашего плагина org.eclipse.example.library.edit.extension .

В настоящее время Google Guice не является общедоступным плагином Eclipse. Существует ошибка, запрашивающая t добавить его в комплект Orbit. Однако он доступен с Xtext как плагин Eclipse. Поскольку в моей цели Xtext, я использую это в своем уроке. Если у вас этого нет, вам нужно добавить Google Guice в качестве внешнего файла  в ваш проект.

2. Следующим шагом будет избавление от «новых» операторов в расширенной ItemProviderFactory. Это то, что связывает ItemProviderAdapterFactory с конкретной реализацией ItemProvider. Мы используем Google Guice для инъекции в поле  BookItemProvider .

public class EXTLibraryItemProviderAdapterFactoryExtension extends
EXTLibraryItemProviderAdapterFactory {

@Inject
protected BookItemProvider bookItemProvider;

@Override
public Adapter createBookAdapter() {
return bookItemProvider;
}

}

3. Теперь нам нужно создать модуль Guice Google, чтобы связать расширенный ItemProvider. Итак, создайте модуль следующим образом:

public class LibraryModule extends AbstractModule implements Module{
@Override
protected void configure() {
bind(BookItemProvider.class).to(BookItemProviderExtension.class).in(Scopes.SINGLETON);
}

}

4. Вы также можете добавить расширенный ItemProviderAdapterFactory в наш редактор. Поскольку мы не хотим, чтобы редактор имел зависимость от Google Guice, мы вносим следующие изменения в модуль и расширенный ItemProvider.

public class LibraryModule extends AbstractModule implements Module{

private final AdapterFactory adapterFactory;

public LibraryModule(AdapterFactory adapterFactory) {
this.adapterFactory = adapterFactory;
}

@Override
protected void configure() {
bind(AdapterFactory.class).toInstance(adapterFactory);
bind(BookItemProvider.class).to(BookItemProviderExtension.class).in(Scopes.SINGLETON);
}

}
public class BookItemProviderExtension extends BookItemProvider {

@Inject
public BookItemProviderExtension(AdapterFactory adapterFactory) {
super(adapterFactory);
}

@Override
public String getText(Object object) {
return super.getText(object) + " (" + ((Book) object).getPages() + ") ";
}

}

5. Теперь нам нужно создать Guice Injector.

public class EXTLibraryItemProviderAdapterFactoryExtension extends
EXTLibraryItemProviderAdapterFactory {

public LibraryItemProviderAdapterFactoryExtension() {
Guice.createInjector(new LibraryModule(this));
}

@Inject
protected BookItemProvider bookItemProvider;

@Override
public Adapter createBookAdapter() {
return bookItemProvider;
}

}

6. Запустите его, и вы получите тот же результат, что и раньше.

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

Это был довольно тривиальный пример. Давайте рассмотрим более важный пример, в котором нам нужно внести изменения в  PersonItemProvider и WriterItemProvider ( который расширяет PersonItemProvider) .

Расширенный пример библиотеки модели, по умолчанию Отображает Lastname в Writer для атрибута Name . Это происходит из следующих строк кода в  PersonItemProvider , суперклассе  WriterItemProvider .

@Override
public String getText(Object object)
{
String label = ((Person)object).getLastName();
return label == null || label.length() == 0 ?
getString("_UI_Person_type") : //$NON-NLS-1$
getString("_UI_Person_type") + " " + label; //$NON-NLS-1$ //$NON-NLS-2$
}

Позволяет изменить это , чтобы отобразить ПгвЬЫате вместо   LastName .

1. Создайте новое расширение ItemProvider для PersonPersonItemProviderExtension и переопределите метод getText () следующим образом.

public class PersonItemProviderExtension extends PersonItemProvider {

@Inject
public PersonItemProviderExtension(AdapterFactory adapterFactory) {
super(adapterFactory);
}

@Override
public String getText(Object object) {
String label = ((Person) object).getFirstName();
return label == null || label.length() == 0 ? getString("_UI_Person_type") : //$NON-NLS-1$
getString("_UI_Person_type") + " " + label; //$NON-NLS-1$ //$NON-NLS-2$

}

}

2. Вставьте расширенное  PersonItemProviderExtension в расширение ItemProviderAdapterFactory.

public class EXTLibraryItemProviderAdapterFactoryExtension extends
EXTLibraryItemProviderAdapterFactory {
...

@Inject
protected PersonItemProvider personItemProvider;

@Override
public Adapter createPersonAdapter() {
return personItemProvider;
}

}

3. Обновите модуль Google Guice

@Override
protected void configure() {
bind(AdapterFactory.class).toInstance(adapterFactory);
bind(BookItemProvider.class).to(BookItemProviderExtension.class).in(Scopes.SINGLETON);
bind(PersonItemProvider.class).to(PersonItemProviderExtension.class).in(Scopes.SINGLETON);
}

Если вы запустите код сейчас, вы увидите, что мы еще не получили ожидаемых результатов. Это потому, что  WriterItemProvider все еще расширяет  PersonItemProvider, а не  PersonItemProviderExtension , где мы интегрировали изменения. Мы могли бы пойти дальше и создать новый  WriterItemProviderExtension, который расширяет  PersonItemProviderExtension . Но таким образом мы бы связали  WriterItemProviderExtension с  реализацией PersonItemProviderExtension . Мы хотели бы внедрить каждое из этих расширений без создания какой-либо взаимозависимости между любым из них.

4. Мы можем изменить наследование на делегирование и снова использовать инъекцию, то есть  вставить PersonItemProviderExtension в  WriterItemProviderExtension  и делегировать вызов getText () .

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

public class WriterItemProviderExtension extends WriterItemProvider {

@Inject
private PersonItemProvider personItemProvider;

@Inject
public WriterItemProviderExtension(AdapterFactory adapterFactory) {
super(adapterFactory);
}

@Override
public String getText(Object object) {
return personItemProvider.getText(object);
}

}

5. Не забудьте обновить модуль EXTLibraryItemProviderAdapterFactoryExtension и Guice, чтобы связать WriterItemProviderExtension .

public class EXTLibraryItemProviderAdapterFactoryExtension extends
EXTLibraryItemProviderAdapterFactory {

...

@Inject
protected WriterItemProvider writerItemProvider;

@Override
public Adapter createWriterAdapter() {
return writerItemProvider;
}

}
@Override
protected void configure() {
bind(AdapterFactory.class).toInstance(adapterFactory);
bind(BookItemProvider.class).to(BookItemProviderExtension.class).in(Scopes.SINGLETON);
bind(PersonItemProvider.class).to(PersonItemProviderExtension.class).in(Scopes.SINGLETON);
bind(WriterItemProvider.class).to(WriterItemProviderExtension.class).in(Scopes.SINGLETON);
}

Если вы запустите код сейчас, вы увидите, что FirstName отображается как атрибут Name в Writer , а не в Lastname .

Я расскажу об этом во второй части урока. Подожди!

 

От http://nirmalsasidharan.wordpress.com/2011/05/18/extending-emf-itemproviders-using-google-guice-i/