четверг, 29 ноября 2007 г.

Создание представлений на основе Common Navigator. Часть II: Добавление содержания

Автор оригинальной статьи : Michael D. Elder
Перевод: Евгений Флоров

В данном разделе, мы выполним добавление простого расширения содержания в представление созданное в последнем примере. Для исключения возможности завязнуть в сложных провайдерах контента (content provider) и ярлыков (label provider), мы сконцентрируемся на файловой структуре с очень простой моделью, в этом случае - плоские старые *.properties файлы. По-завершению, наше расширение контента позволит нам раскрыть любой *.properties файл в Example viewer и отобразить данные файла в представлении.

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

Во-первых, давайте посмотрим на что будет похоже расширение в plugin.xml файле. Если вы читали предыдущий раздел, вы знаете, что вы можете строить расширение (подобное последующему примеру) используя закладку Extensions в Plug-in Manifest Editor. Сначала путем выбора Add... на странице Extensions, выберите org.eclipse.ui.navigator.navigatorContent, затем в меню по правой кнопке выберите New > navigatorContent. Другой способ - это создание этого расширения в закладке plugin.xml того-же редактора. Оба варианта хороши, но первая возможность использует меню New из Extension Point Schema так, что вы увидете другие опции, которые довтупны в закладке plugin.xml.


Расширение объявляет расширение содержания с id - "org.eclipse.ui.examples.navigator.propertiesContent", с отображаемым именем "Properties File Contents". Имя - это текстовая строка используемая в закладке "Available Extensions" диалогах Filters. Мы разберемся с кодом для провайдеров содержимого и ярлыков позднее, а на данный момент запомните только, что каждый из них должен быть специфицирован; вы не можете описать только один или другой.

В итоге мы устанавливаем некоторые атрибуты для того, чтобы сказать каркасу как нам хотелось бы, чтобы наше расширение отображалось в представлении.
  • activeByDefault определяет должно ли расширение быть активным в конфигурации по-умолчанию (то есть в новом workspace).
  • icon определяет какая исонка должна быть использована, когда ссылаются на расширение в пользовательском интерфейсе
  • priority используется в нескольких различных случаях. Наиболее известным является определение относительного порядка элементов в представлении (высокоприоритетные элементы подымаются вверх представления, низкоприоритетные - опускаются вниз). В основном, вариантов "normal"или "high" должно быть достаточно для большинства расширений, показывая, что они должны смешиваться с ресурсными расширениями (когда приоритеты одинаковы, то для сортировки элементов используется алфавтная сортировка их ярлыков) или поместить их выше ресурсов под проектами.
Внутри каждого элемента, мы можем указать множество типов расширений, но до того, как расширить пример, мы должны описать каркасу - когда наше расширение должно быть вызвано. Мы должны описать когда мы можем предоставить дочерние, родительские узлы или ярлыки и иконки узлов дерева. Мы делаем это используя Eclipse Core Expressions. Пока я предложу вам обратиться к документации по org.eclipse.core.expressions для изучения подробностей об этом или вы можете использовать New > ...меню в закладке Extensions для простого создания их.

Для расширений содержания существует два важных выражения: triggerPoints и possibleChildren.

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



Выражение possibleChildren показывает для каких типов узлов дерева наше расширение может предоставить ярлыки или родителя. Для ваших сценариев, которые могут поддерживать связь с редактором, или setSelection() на представлении, данное выражение должно быть аккуратным и полным.



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

Например, мы привяжем наше расширение содержания свойств (с id "org.eclipse.ui.examples.navigator.propertiesContent") к нашему Example View (с id "org.eclipse.ui.examples.navigator.view").



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

Во-первых нам нужна модель. Файл свойств имеет черезвычайно простую структуру и мы смоделируем это используя только один тип объекта модели, называемый PropertiesTreeData, который будет иметь три поля: name (имя свойства), значение (значение свойства), и container (файл содержащий модель свойств). Один из этих элементов модели будет создан для каждого свойства в *.properties файле.

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

Поставщик содержания в нашем примере реализует implements org.eclipse.jface.viewers.ITreeContentProvider для предоставления информации о структуре дерева. Каркас Common Navigator также поддерживает реализации новых org.eclipse.jface.viewers.ITreePathContentProvider, но это уже выходит за рамки этого примера.

PropertiesContentProvider также обрабатывает некоторые другие функции требуемые нашему расширению, такие как отслеживание (listening) изменений ресурсов и соответственно обновление модели (и представления). Мы не будем смотреть это в текущем разделе, но вы можете смотреть как это работает в полных исходниках.

Теперь, мы сконцентрируемся на интеграционных методах представления, определенных в ITreeContentProvider.


ITreeContentProvider должен релизовать getElements(Object input), getChildren(Object parent), hasChildren(Object element) и getParent(Object element).

Метод getElements() запрашивается для элементов в корне представления. Многие реализации просто перенаправляют этот вызов в getChildren(), и это то, что мы сделаем.



getChildren() принимает объект (в нашем случае либо *.properties IFile либо экземпляр объекта нашей модели PropertiesTreeData так как мы описали это в нашем расширении.

В последующей реализации, мы выполняем проверку на то является ли переданный элемент экземпляром org.eclipse.core.resources.IFile и если да, то имеет ли файл расширение - *.properties. Если переданные элемент удовлетворяет этим условиям, то мы проверяем наш кэш загруженной модели или пытаемся загрузить модель, если она не кеширована. Метод updateModel() будет создавать объект PropertiesTreeData для каждого свойства и кэшировать то, что найдет в cachedModelMap. Загрузите полный текст исходного кода для того, чтобы посмотреть как реализован updateModel().



Метод hasChildren() оптимизирован для того, чтобы вернуть "истина" если он вызван с IFile с расширением *.properties. Другой альтернативой является ранняя загрузка содержимого для выполнения вычислений, но эта возможность привносит с собой дополнительные накладные расходы.

При вызове с элементом модели PropertiesTreeData - hasChildren() вернет "ложь", что означает, что наши элементы модели не могут иметь детей (свойства не могут иметь детей, но остальные модели могут).



Метод getParent() вернет IFile, который содержит элемент PropertiesTreeData или null если вызывается для любого другого элемента. Каркас Common Navigator продолжит опрос остальных расширений пока не обнаружится не нулевой родитель или пока все связанные расширения не будут опрошены. Запомните, что некоторое расширение может быть запрошено на предоставление родителя для некоторого элемента, если этот элемент соотвествует его выражению.



В конце концов, поставщик ярлыков (label provider) нашего расширения содержания скажет представлению как отобразить иконку и ярлык для наших элементов модели (PropertiesTreeData). Так как мы только начали изучение этих элементов, мы не должны беспокоиться о предоставлении иконок и ярлыков для любых других элементов. Другие расширения предоставят ярлыки и иконки для других элементов в дереве.

PropertiesLabelProvider реализует org.eclipse.jface.viewers.ILabelProvider и также org.eclipse.ui.navigator.IDescriptionProvider.

ILabelProvider является интерфейсом по-умолчанию требуемым JFace для предоставления ярлыков и иконок.

IDescriptionProvider специфичен для каркаса Common Navigator и используется для предоставления текста для стори состояния в левом нижнем углу окна Eclipse.

Методы требуемые ILabelProvider - это getText() и getImage(). Мы отобразим ярлык для наших элементов модели как строку "name= value"для свойства (быз кавычек). Для иконки, мы просто используем одну из доступных иконок предоставляемых Platform/UI.



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

Финальный вид отображающий файлы свойств доступен в нашем Example View.

Комментариев нет: