Полезная информация

Заказывай стафф с атрибутикой Mozilla и... пусть все вокруг завидуют тебе! Быть уникальным - быть с Mozilla!

№111-10-2011 04:57:14

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 8.0

Источники данных: RDF vs. XML [vs SQLite]

Недавно понадобилось сделать одну функциональность, в которой используется отображение и редактирование иерархических данных в xul:tree. Поскольку реализация custom nsITreeView - дело нехитрое, и не раз пройденное, то, чтобы жизнь медом не казалась, я решил сделать дерево на основе rdf-источника. В процессе разбора документации я обнаружил, что, оказывается, на данный момент в качестве источников данных поддерживаются и шаблоны на основе XML, и даже SQLite-шаблоны. В свое время, пытаясь для себя уяснить специфику rdf-шаблонов, я был не особенно в восторге от её реализации (впечатление на тот момент - перегруженное достаточно неочевидной логикой надмножество XML). Теперь же источники данных вполне можно проектировать на основе XML, используя в качестве основы запросов давно известные и понятные XPath-выражения. Но здесь есть одно "но": RDF - фактически родная технология Mozilla, очень давно поддерживаемая, и хорошо обкатанная. Шаблоны и источники данных на других основах появились, насколько я понял, сравнительно недавно. Поэтому, если кому-то доводилось оценивать плюсы и минусы от использования перечисленных источников, то хотелось бы услышать мнение, какие источники данных всё же предпочтительнее использовать на данный момент.

Отсутствует

 

№212-10-2011 18:45:52

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

В результате экспериментов выяснилось следующее. Самый главный минус источников данных, отличных от rdf - очень скудная документация по работе c этими источниками. Если для rdf можно найти примеры кода почти на все случаи жизни, то для того же XML - только самые простейшие примеры.
Вот первая, и достаточно неприятная проблема, с которой я столкнулся. Допустим, у нас есть xml вот такого вида:

скрытый текст

Выделить код

Код:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <item name="ItemA" />
  <group type="GroupA">
    <item name="ItemAA" />
    <group type="GroupAB">
      <item name="ItemABA" />
      <item name="ItemABB" />
    </group>
    <item name="ItemAB" />
  </group>
  <item name="ItemB" />
  <group type="GroupB">
    <item name="ItemBA" />
    <item name="ItemBB" />
  </group>
  <item name="ItemC" />
</root>

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

Выделить код

Код:

<tree id="xmltree" flex="1" datasources="chrome://addondevhelper/content/items.xml" ref="*" querytype="xml"
      flags="dont-build-content">
  <treecols>
    <treecol id="name" primary="true" label="Name" flex="1"/>
  </treecols>
  <template>
    <query expr="*"/>
    <action>
      <treechildren>
        <treeitem uri="?">
          <treerow>
            <treecell label="?name"/>
          </treerow>
        </treeitem>
      </treechildren>
    </action>
  </template>
</tree>

В случае нашего XML этот пример работать не будет, т.к. родительские узлы не имеют атрибута name, который выводится в treecell. Таким образом, элементы разного типа нужно обрабатывать разными запросами - у нас получается шаблон с множественными запросами. И выглядеть это будет так (вот уже для этого я примеров кода нигде не нашел - на MDC в т.ч.):
скрытый текст

Выделить код

Код:

<tree id="xmltree" flex="1" datasources="chrome://xmltest/content/items.xml" ref="*" querytype="xml"
      flags="dont-build-content">
  <treecols>
    <treecol id="name" primary="true" label="Name" flex="1"/>
  </treecols>
  <template>
    <queryset>
      <query expr="group"/>
      <action>
        <treechildren>
          <treeitem uri="?">
            <treerow>
              <treecell label="?type"/>
            </treerow>
          </treeitem>
        </treechildren>
      </action>
    </queryset>
    <queryset>
      <query expr="item"/>
      <action>
        <treechildren>
          <treeitem uri="?">
            <treerow>
              <treecell label="?name"/>
            </treerow>
          </treeitem>
        </treechildren>
      </action>
    </queryset>
  </template>
</tree>

Но в таком случае выстроенное дерево будет выглядеть так:
скрытый текст
xml-tree.png

- т.е. нарушается очередность следования узлов одного уровня, но разных типов - контейнеры группируются вверху родительского узла, листья - внизу. Как избежать такой ситуации - не знаю. И если для дерева можно вернуться (хотя бы обработав начальный xml с помощью xslt) к первоначальному варианту с одним запросом, который сохраняет порядок узлов, то, например, в случае с генерируемым на основе XML-источника меню такой фокус уже не выйдет - там в любом случае нужно как минимум 2 запроса:
скрытый текст

Выделить код

Код:

<button label="xml menu" type="menu" datasources="chrome://xmltest/content/items.xml"
    ref="*" querytype="xml">
  <template>
    <queryset>
      <query expr="group"/>
      <action>
        <menupopup>
          <menu uri="?" label="?type"/>
        </menupopup>
      </action>
    </queryset>
    <queryset>
      <query expr="item"/>
      <action>
        <menupopup>
          <menuitem uri="?" label="?name"/>
        </menupopup>
      </action>
    </queryset>
  </template>
</button>


Единственное, что пока приходит в голову - на все ноды повесить атрибут порядкового номера со сквозной нумерацией сверху вниз. Но и тут тоже не всё так просто: если в rdf-документе можно указать атрибут parseType, то для xml этого сделать нельзя, даже если ввести переменную, в которой будет значение от атрибута порядкового номера, преобразованного в число - все равно сортировка таких чисел работает по алгоритму сортировки строк, и поэтому порядковый атрибут обязательно должен иметь лидирующие нули. Поэтому вопрос о нормальной генерации дерева пока остается открытым.

Отсутствует

 

№330-10-2011 08:27:49

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

Сухой остаток от данной темы: деревья с автоматически генерируемым по шаблону контентом достаточно удобны, если требуется только отображение контента, и не требуется редактирования. Потому что при заданном флаге dont-build-content внесенные изменения вообще не подхватываются деревом, и для элементов сгенерированного дерева не возникает событий drag'n'drop. А при не заданном флаге dont-build-content всего этого нет, но внесенные изменения не проецируются на builder datasource; метода для синхронизации datasource с данными в дереве я не нашел. Наверное, можно как-то спроецировать эти данные с помощью предоставляемых билдером nsITemplateResult, либо навесить на дерево листенеры, и в них менять исходные данные - но это мне видится очень муторным занятием; способа разом получить источник, содержащий измененные данные, нет, а из-за этого теряется весь смысл работы с таким типом деревьев - овчинка просто не стоит выделки. С помощью старого проверенного nsITreeView можно легко сделать всё то же самое. К тому же есть дополнительные проблемы с локализацией контента дерева, и проблемы, если в дереве нужно отображать сепараторы - это опять несколько запросов на дерево, проблемы с сортировкой, и т.д.
А сортировку сгенерированного многоуровневого меню, и с сепараторами в составе в т.ч. меню можно решить именно введением порядкового атрибута - просто надо применить с исходному XML копирующий XSLT, в котором дополнительно на каждый эелемент навешиваеатся атрибут с результатом отработки <xsl:number>:

Выделить код

Код:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml"/>
  <xsl:template match="*">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:attribute name="ord">
        <xsl:number count="*" level="any" format="000001"/>
      </xsl:attribute>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

и для контейнера меню дополнительно задать

sort="?ord" sortDirection="ascending"

Отсутствует

 

№411-11-2011 07:07:30

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 9.0

Re: Источники данных: RDF vs. XML [vs SQLite]

Всё-таки не дает покоя мне эта тема, и видимо, вернусь к ней я еще не раз.
На данном этапе удалось разобраться, как всё-таки нормально генерить деревья и меню на основе xml с разнородным содержимым. Основная идея - обрабатывать все элементы в одном запросе, чтобы не нарушать порядок их следования, а обработку тэгов отличающихся типов делать внутри, в зависимости от совокупности условий. Приведенный ниже пример, кроме того, показывает, как можно извлекать из XML и применять в шаблоне treeitem properties для стилизации дерева. Может быть, кому-нибудь пригодится.
Исходный XML:

Выделить код

Код:

<?xml version="1.0" encoding="UTF-8"?>
<root id="0">
  <item id="1" name="ItemA" color="green"/>
  <item id="2" name="GroupA" color="yellow" exticon="jolly">
    <item id="3" name="ItemAA" color="blue"/>
    <item id="4" name="GroupAB">
      <item id="5" name="ItemABA"  color="red"/>
      <separator id="13"/>
      <item id="6" name="ItemABB" />
    </item>
    <item id="7" name="ItemAB" color="lightgray"/>
  </item>
  <item id="8" name="ItemB" />
  <separator id="14"/>
  <item id="9" name="GroupB">
    <item id="10" name="ItemBA" color="dialog"/>
    <item id="11" name="ItemBB" />
  </item>
  <item id="12" name="ItemC" />
</root>

XUL:

Выделить код

Код:

<tree id="xmltree" flex="1" datasources="chrome://xmltemplatetest/content/items.xml" ref="*" querytype="xml"
      _flags="dont-build-content" draggable="true" editable="true">
  <treecols>
    <treecol id="name" primary="true" label="Name" flex="1"/>
  </treecols>
  <template>
    <query expr="*">
      <assign var="?nodename" expr="local-name(.)"/>
      <assign var="?props" expr="concat(@color, ' ', @exticon)"/>
    </query>
    <rule>
      <conditions>
        <where subject="?nodename" value="separator" rel="equals" ignorecase="true"/>
      </conditions>
      <action>
        <treechildren>
          <treeseparator uri="?" srcid="?id"/>
        </treechildren>
      </action>
    </rule>
    <rule>
      <conditions>
        <where subject="?nodename" value="item" rel="equals" ignorecase="true"/>
      </conditions>
      <action>
        <treechildren>
          <treeitem uri="?" srcid="?id">
            <treerow properties="?props">
              <treecell label="?name" properties="?props"/>
            </treerow>
          </treeitem>
        </treechildren>
      </action>
    </rule>
  </template>
</tree>
<button id="xml-menu-button" label="xml-generated menu" type="menu" querytype="xml"
  datasources="chrome://xmltemplatetest/content/items.xml" ref="*">
  <template>
    <query expr="*">
      <assign var="?count" expr="count(*)"/>
      <assign var="?nodename" expr="local-name(.)"/>
    </query>
    <rule>
      <conditions>
        <where subject="?count" value="0" rel="greater"/>
      </conditions>
      <action>
        <menupopup>
          <menu uri="?" label="?name" id="?id" color="?color"
            exticon="?exticon" class="menu-iconic"/>
        </menupopup>
      </action>
    </rule>
    <rule>
      <conditions>
        <where subject="?count" value="0" rel="equals"/>
        <where subject="?nodename" value="item" rel="equals" ignorecase="true"/>
      </conditions>
      <action>
        <menupopup>
          <menuitem uri="?" label="?name" id="?id" color="?color"
            exticon="?exticon" class="menuitem-iconic"/>
        </menupopup>
      </action>
    </rule>
    <rule>
      <conditions>
        <where subject="?nodename" value="separator" rel="equals" ignorecase="true"/>
      </conditions>
      <action>
        <menupopup>
          <menuseparator uri="?" id="?id"/>
        </menupopup>
      </action>
    </rule>
  </template>
</button>

Стиль:

Выделить код

Код:

treechildren::-moz-tree-row(green) {
  background-color: lime;
}
treechildren::-moz-tree-row(red) {
  background-color: red;
}
treechildren::-moz-tree-row(yellow) {
  background-color: yellow;
}
treechildren::-moz-tree-row(blue) {
  background-color: lightblue;
}
treechildren::-moz-tree-row(lightgray) {
  background-color: lightgray;
}
treechildren::-moz-tree-row(dialog) {
  background-color: -moz-dialog;
}
treechildren::-moz-tree-row(yellow, jolly) {
  background-color: magenta;
}

treechildren::-moz-tree-image(open) {
  list-style-image: url(folderopen.png);
}
treechildren::-moz-tree-image(open, jolly) {
  list-style-image: url(jr.png);
}
treechildren::-moz-tree-image(closed) {
  list-style-image: url(folder.png);
}

menu[color], menuitem[color] {
  -moz-appearance: none;
}
menu {
  list-style-image: url(folder.png);
}
menu[open] {
  list-style-image: url(folderopen.png);
}
menu[open][exticon="jolly"] {
  list-style-image: url(jr.png);
}
menu[color="green"], menuitem[color="green"] {
  background-color: lime;
}
menu[color="red"], menuitem[color="red"] {
  background-color: red;
}
menu[color="yellow"], menuitem[color="yellow"] {
  background-color: yellow;
  color: black;
}
menu[color="blue"], menuitem[color="blue"] {
  background-color: lightblue;
}
menu[color="lightgray"], menuitem[color="lightgray"] {
  background-color: lightgray;
}
menu[color="dialog"], menuitem[color="dialog"] {
  background-color: -moz-dialog;
}

Результат выглядит примерно так:
xml-template-result.png

Примечания к коду: во-первых, не получилось использовать treeseparator в дереве с флагом dont-build-content - что-то вроде бы появляется, но как сепаратор оно не выглядит. Без флага dont-build-content всё в порядке. Некоторым объяснением этому может служить упоминание в документации о том, что с флагом dont-build-content в дереве надо использовать не контентные элементы, а элемент treeitem.
Во-вторых, с xml-шаблонами не работает спец. правило rule iscontainer="true"/"false" - видимо, это правило относится к специфике RDF-шаблонов. Для своего случая я просто вытащил в отдельную переменную количество дочерних узлов текущего узла обработки, и далее использовал эту переменную в условии обработки.
И, как видно из вышеприведенного, уже не нужен хак с нумерацией узлов через XSLT.
Теперь надо попытаться как-то редактировать это дерево.

Отсутствует

 

№512-11-2011 12:52:13

tenshi
Участник
 
Группа: Members
Зарегистрирован: 30-03-2008
Сообщений: 47
UA: Chrome 15.0

Re: Источники данных: RDF vs. XML [vs SQLite]

через e4x как-то так:
group.substitutions.substitution+= <substitution>...</substitution>

Отсутствует

 

№612-11-2011 16:16:39

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

okkamas_knife пишет

отбой. вопрос решил через string.split

Плохое решение. C xml надо работать как с DOM-документом, а не как со строкой. Соответствующими методами - getElementById, appendChild, возможно - XPath, и т.д.

Отсутствует

 

№712-11-2011 20:56:25

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

okkamas_knife пишет

\n воспринимается не как текст а как перевод строки

Слэш заэкранировать нужно. Т.е. в строку вписывать "\\n".

Добавлено 12-11-2011 21:19:17
Кстати. В FF7 был исправлен Bug 663728 – XUL tree twisty state won't persist if XML template is used. И если дерево с флагом dont-build-content использует XML-шаблон, в котором каждый элемент имеет уникальный id, то FF сам будет запоминать состояние свернутости/развернутости узлов дерева, и восстанавливать это состояние.

Отредактировано hydrolizer (12-11-2011 21:22:14)

Отсутствует

 

№828-11-2011 20:04:04

YuryL
Участник
 
Группа: Members
Зарегистрирован: 24-08-2010
Сообщений: 31
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

hydrolizer

вопрос как к специалисту по nsITreeView

я сейчас  делаю приложение(на xulrunner), которое работает с древовидными данными.
Работа с данными происходит через XPCOM C++(спасибо еще раз за помощь с этим).
я нашел достаточно примеров как работать custom treeview, но все они на js.
В моем случае логично бы было некоторые функции реализовать в XPCOM компоненте на с++.
Например getCellText будет многократно вызываться для построения дерева. Внутри
этой функции я буду каждый раз обращаться к XPCOM компоненту за данными.
Возможно ли это?

Отсутствует

 

№929-11-2011 06:39:15

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 9.0

Re: Источники данных: RDF vs. XML [vs SQLite]

YuryL
nsITreeView - это же, по сути, не класс, а интерфейс. Его реализация в рамках заданного контракта может быть любой - на чистом js, на XPCOM как js + .xpt-описатель интерфейса, на XPCOM/c++ (например, для тех же деревьев на основе шаблонов модель и часть контракта nsITreeView реализуются в XPCOM-компоненте nsXULTreeBuilder). Здесь дело в другом: методы nsITreeView, подобные getCellText, с большой частотой вызываются при отрисовке дерева, поэтому стоит заранее проверить скорость отработки и ресурсоемкость вызова метода, который вы планируете вызывать внутри getCellText - если он достаточно ресурсоемок, то он просто вам подвесит всё окно с деревом. В таком случае придется данные, которые возвращает метод, формировать заранее, и включать их в данные, на основе которых строится модель (view).

Отсутствует

 

№1029-11-2011 20:05:33

YuryL
Участник
 
Группа: Members
Зарегистрирован: 24-08-2010
Сообщений: 31
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

не нашел описания nsXULTreeBuilder в MDN

ведь мне нужно свойству view дерева задать объект, который реализован на с++.
Как это сделать на js?

Либо в коде с++ каким-то образом это сделать. Где можно почитать об этом?
Или пример найти. Или я туплю, не могу найти в MDN ничего на эту тему...

Отсутствует

 

№1130-11-2011 06:30:40

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 9.0

Re: Источники данных: RDF vs. XML [vs SQLite]

YuryL пишет

не нашел описания nsXULTreeBuilder в MDN

Его там как такового нет, потому что это - реализация интерфейса, о чем я писал выше. Описания самих интерфейсов, и описания способов работы посредством спец. элементов (template/query/rule etc.) - есть. Но если вам хочется взглянуть на детали реализации - смотрите здесь: http://mxr.mozilla.org/mozilla-central/ … uilder.cpp

YuryL пишет

ведь мне нужно свойству view дерева задать объект, который реализован на с++.
Как это сделать на js?

А как вы обычно получаете экземпляр XPCOM-компонента? Точно так же и здесь:

Выделить код

Код:

var myView = Components.classes["..."].createInstance(Components.interfaces. ...);

Интерфейс экземпляра полученного класса должен соответствовать контракту интерфейса nsITreeView. И если это так, то далее просто

Выделить код

Код:

document.getElementById("my-custom-tree").view = myView;

Отсутствует

 

№1201-12-2011 01:06:24

YuryL
Участник
 
Группа: Members
Зарегистрирован: 24-08-2010
Сообщений: 31
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

спасибо, мне не верилось, что свойству view может быть присвоена, как ссылка на объект в двоичном коде, так и на js-объект, который будет интерпретироваться. ЗдОрово! Буду пробовать..
Может и методы класса написанного на с++ можно для начала реализовать на js, а потом по одному переписывать на с++?

Кстати, еще вопрос, позволяет ли VS 2010 отлаживать XPCOM компоненты, т.е. будут ли срабатывать точки останова в среде VS 2010?
hydrolizer, ваши ответы очень помогают разобраться, спасибо еще раз..

Отсутствует

 

№1301-12-2011 07:10:44

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 9.0

Re: Источники данных: RDF vs. XML [vs SQLite]

YuryL пишет

Кстати, еще вопрос, позволяет ли VS 2010 отлаживать XPCOM компоненты, т.е. будут ли срабатывать точки останова в среде VS 2010?

Не знаю - я пока что обходился простым отладочным выводом в консоль - не в консоль ошибок FF, а в консоль windows. Для этого достаточно обычного printf - например,

Выделить код

Код:

printf("skllib: NotifyObservers success: %d\n",lParam);

и при запуске FF с консолью (нужно добавить ключ -console в строку запуска FF) будет примерно следующее:

скрытый текст
ff-console.png

Разумеется, что достаточно сложный код таким образом отлаживать неудобно. Вот тут: http://www.iosart.com/firefox/xpcom/ человек пишет, что

BTW, debugging with Visual Studio 6.0 works great. Just set the exectuable to firefox, set your breakpoints, hit F5, and it works perfectly.

Здесь: http://www.codeproject.com/KB/miscctrl/ … ation.aspx автор также пишет, что

An easy and simple way of learning this is to just use the projects and debug with break point.

так что вполне возможно, что и VS2010 позволит пошаговую отладку. Попробуйте.

Отсутствует

 

№1406-12-2011 12:09:53

YuryL
Участник
 
Группа: Members
Зарегистрирован: 24-08-2010
Сообщений: 31
UA: Firefox 8.0

Re: Источники данных: RDF vs. XML [vs SQLite]

С файлом nsXULTreeBuilder.cpp не получается разобраться, даже откомпиллировать.
Хотелось бы найти рабочий пример для создания XPCOM компонента реализующего nsITreeView.

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]