Статьи

Оценка точки расширения Eclipse — это просто

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

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

Оценка точки расширения Eclipse

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

расширение точка-определение

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ContributionEvaluation {
   
  private static final String EP_ID
    = "com.codeaffine.post.contribution";
 
  public void evaluate() {
    IExtensionRegistry registry = Platform.getExtensionRegistry();
    IConfigurationElement[] elements
      = registry.getConfigurationElementsFor( EP_ID );
    Collection<Runnable> contributions = new ArrayList<Runnable>();
    for( IConfigurationElement element : elements ) {
      Object extension;
      try {
        extension = element.createExecutableExtension( "class" );
      } catch( CoreException e ) {
        throw new RuntimeException( e );
      }
      contributions.add( ( Runnable )extension );
    }
    for( Runnable runnable : contributions ) {
      runnable.run();
    }
  }
}

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

В сочетании с лямбда-выражениями Java 8 я смог создать вспомогательное средство, которое сводит функциональность evaluate к:

1
2
3
4
5
6
public void evaluate() {
  new RegistryAdapter()
    .createExecutableExtensions( EP_ID, Runnable.class )
    .withConfiguration( ( runnable, extension ) -> runnable.run() )
    .process();
}

По общему признанию, я немного обманул, поскольку можно также немного улучшить первый пример, используя Collection#forEach java 8 Collection#forEach вместо явного зацикливания. Но я думаю, что это все равно не сделает код действительно великолепным!

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

RegistryAdapter

Основным классом вспомогательной реализации является RegistryAdapter , который инкапсулирует системный экземпляр IExtensionRegistry и предоставляет набор методов, чтобы определить, какие операции должны выполняться в отношении конкретной точки расширения. На данный момент адаптер позволяет читать конфигурации вклада или создавать исполняемые расширения.

Множественные вклады оцениваются, как показано выше, с использованием методов, обозначаемых во множественном числе — для оценки ровно одного элемента вклада подходят методы, обозначенные в единственном числе. Это означает, что для работы с конкретным выполняемым вкладом вы должны использовать createExecutableExtension вместо createExecutableExtension s .

В зависимости от выбранной операции доступны различные параметры конфигурации. Это стало возможным благодаря тому, что свободный API реализует своего рода грамматику для улучшения руководства и безопасности программирования. Например, операция readExtension не позволяет зарегистрировать ExecutableExtensionConfigurator , поскольку это будет неумелая комбинация.

Метод withConfiguration позволяет настроить или инициализировать каждое исполняемое расширение после его создания. Но, как показано в приведенном выше примере, его также можно использовать для непосредственного вызова запускаемого расширения. Из-за безопасной реализации типа createExecutableExtension(s) можно получить доступ к экземпляру расширения в лямбда-выражении без приведения.

Наконец, метод process() выполняет указанную операцию и возвращает типизированную Collection созданных элементов, если они необходимы для дальнейшей обработки:

1
2
Collection<Extension> extensions
  = new RegistryAdapter().readExtensions( EP_ID ).process();

сказуемое

Но как можно выбрать один элемент вклада точки расширения затмения с помощью адаптера? Предположим, что мы добавили идентификатор атрибута в наше определение вклада выше. Свободный API RegistryAdapter позволяет указать Predicate который можно использовать для выбора конкретного вклада:

1
2
3
4
5
6
7
public void evaluate() {
  new RegistryAdapter()
    .createExecutableExtension( EP_ID, Runnable.class )
    .withConfiguration( ( runnable, extension ) -> runnable.run() )
    .thatMatches( attribute( "id", "myContribution" ) )
    .process();
}

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

1
.thatMatches( Predicates.attribute( "id", "myContribution" ) )

где «myContribution» обозначает уникальное значение id, объявленное в расширении:

1
2
3
4
<extension point="com.codeaffine.post.contribution">
  <contribution id="myContribution" class="com.codeaffine.post.MyContribution">
  </contribution>
</extension>

Конечно, можно реализовать пользовательские предикаты, если предустановок недостаточно:

1
2
3
4
5
6
public void evaluate() {
  Collection<Extension> extensions = new RegistryAdapter()
    .readExtensions( EP_ID, Description.class )
    .thatMatches( (extension) -> extension.getValue() != null )
    .process();
}

расширение

Обычно оценка точки расширения Eclipse большую часть времени работает с IConfigurationElement . API адаптера нечетко различает точку расширения и элемент конфигурации и обеспечивает простую инкапсуляцию, называемую Extension . Но для более сложных задач экземпляры Extension делают основной элемент конфигурации доступным.

В общем случае Extension предоставляет средства доступа к значениям атрибутов, именам вкладов, значениям вкладов, вложенным вкладам и позволяет создавать исполняемые расширения. Одной из основных причин введения этой абстракции было наличие API, CoreException неявно преобразует проверенный CoreException в исключения времени выполнения, поскольку я привык работать с подходом Fail Fast без громоздкой обработки проверенных исключений.

Обработка исключений

Однако в случае, если оценка расширения eclipse вызывается во время запуска плагина или выполняется в фоновом режиме, Fail Fast не является опцией. И, конечно, не разумно игнорировать оставшиеся вклады после того, как конкретный вклад вызвал проблему. Из-за этого API адаптера позволяет заменить механизм Fail Fast на явную обработку исключений:

01
02
03
04
05
06
07
08
09
10
11
12
public void evaluate() {
  Collection<Runnable> contributions = new RegistryAdapter()
    .createExecutableExtensions( EP_ID, Runnable.class )
    .withExceptionHandler( (cause) -> handle( cause ) )
    .process();
     
  [...]
}
   
private void handle( CoreException cause ) {
  // do what you gotta do
}

Обратите внимание, что возвращенный сборник содержит, конечно, только те элементы, которые не столкнулись с какими-либо проблемами

Где его взять?

Для тех, кто хочет проверить это, есть репозиторий P2, который содержит функцию com.codeaffine.eclipse.core.runtime, предоставляющую RegistryAdapter и сопровождающие его классы. Хранилище находится по адресу:

и исходный код и трекер ошибок размещены по адресу:

Хотя в данный момент документация полностью отсутствует, начать объяснение этого поста довольно просто. Но имейте в виду, что маленький инструмент находится в очень раннем состоянии и, вероятно, претерпит некоторые изменения API. В частности, работать только с CoreException пока CoreException по вкладам все еще слишком слабы.

Вывод

В приведенных выше разделах представлены основные функции RegistyAdapter и основное внимание уделено тому, как он облегчает оценку точки расширения Eclipse. Я заменил старые реализации в моих текущих проектах на адаптер и не столкнулся с какими-либо проблемами, а это значит, что решение пока выглядит многообещающе для меня…

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

Однако эта тема выходит за рамки этого поста и будет освещена в следующий раз. Так что следите за обновлениями и не забывайте делиться знаниями, если вы найдете подход, описанный выше, полезным — спасибо!

Ссылка: Оценка точки расширения Eclipse стала проще от нашего партнера JCG Рудигера Херрманна в блоге Code Affine .