Skip to content

ediarum

Christian Lück edited this page Aug 6, 2024 · 15 revisions

Comparison with ediarum

tl;dr: oXbytei does one thing and does it well: editing TEI documents. In contrast, ediarum is a full-fledged editing and publication environment. Metaphorically speaking: oXbytei is an highly configurable next generation speedboat from a flotilla of tools, while ediarum is a huge tanker fully loaded with fossils.

oXbytei has learned a lot from ediarum by TELOTA/BBAW. In an early phase, it even had dependencies on the ediarum.JAR component. However, oXbytei is based on a fundamentally different philosophy than ediarum. So let's compare them!

Do one thing and do it well!

Ediarum is a full-fledged working environment, i.e., it intends to cover the whole workflow from transcribing manuscripts in a TEI editor, storing them in an XML database, generating indices and the apparatus, generating a print edition and deploying the digital edition to the web.

In contrast, oXbytei is simply an extension of the ≶oXygen/> editor. It supports TEI editing, and that's the only thing it does. But in this domain, it does an excellent job.

This entails freedom of choice in the to domains not covered.

You might think: "Lack of support is the real name of what you want to sell as freedom of choice!" True. However, this leads straight to the core of the design principle: Do one thing and do it well! aka the UNIX philosophy. This means: Further requirements are to be covered by other solutions. And: We can't know all your requirements in advance and thus do not have a valid concept of a full-fledged working environment.

So in the end, taking the oXbytei path, will lead you to an excellent collection of tools, each of them chosen to fit your requirements. You can focus on the tool that is required in the current phase of your project, and you gain momentum. Taking the ediarum path, means that you have to setup a so-called full-fledged working environment and adapt it in many aspects in order to cover your needs, and all this before you get your hands on your first TEI document. A flotilla of specialized agile boats, oXbytei amongst them, vs. the huge ediarum tanker.

Freedom of choice

Storing documents

Using oXbytei does not entail a decision, where your documents are stored, unlike ediarum, which forces you to have an eXist database. So, with oXbytei you have the freedom to choose the local file system, a webdav server, an eXist database, a shared web folder, or a version control system like git. <oXygen/> gives you excellent options to connect to all these storing and collaboration solutions and oXbytei does not constrain the options.

Here at SCDH, we advice digital edition projects to put TEI documents under version control with git because of its robustness against data loss and its unique collaboration features, but we do not force them in this direction by technical constraints.

Publishing the digital edition

How long will your edition be on the web? When you take the ediarum path: Are you sure that you can lift the weights of operating a server, of regularly updating the eXist-db instance and sometimes adjusting your special solutions to the requirements of the updates for the next 10 years or so? Looking for a solution that is operationally leaner a thus more sustainable on the long run?

Print, Indices, etc.

Having the documents in an eXist-db like required by ediarum does not add any value to the problem of generating a print edition. This is about XSLT and LaTeX and––as is known––project-specific adaptions. No advantange of ediarum compared to self-contained solutions for generating PDF from TEI. The approach of generating PDF from HTML as ediarum sells can not provide results known from critical editions in print.

Similar about generating indices, registers etc.

oXbytei + ediarum

If you are convinced of both, can go with the ediarum environment, but use oXbytei as the <oXygen/> framework instead of setting up ediarum.BASE.edit.

oXbytei vs. ediarum.BASE.edit

Let's compare ediarum.BASE.edit, which is the <oXygen/> part of ediarum on the one hand and oXbytei on the other. Both are <oXygen/> frameworks, i.e., extensions for the awesome author mode of the XML Editor, which gives you a MSWord-like editing feeling, where you can work on your text without the triangle brackets in the way. Both are designed for editing TEI files. Both support editing register files like a personography or a register of geographic places, and both support linking named entities to these register files.

It's possible, to get the same editing support out of oXbytei and ediarum.BASE.edit. As you will see from the following, oXbytei is much simpler to set up. It's an agile speedboat in a flotilla.

Arbitrary tall stacks!

Framework development has changed fundamentally in <oXygen/> 23. Before, you had to make changes to a framework-file in tiny inaccessible framework widgets and––more important––the framework architecture was constrained to 2 bricks on each other, a base framework and optionally a derived framework, that inherits all the base's functions. Since <oXygen/> 23, frameworks can be defined in exf-files and may be stacked to arbitrary height! The inaccessible framework widgets are also gone; there's superb support for editing the exf-files directly. All this has been a real game changer.

As a result, oXbytei is installed with a click. It comes with reasonable defaults. Adaption to your project's requirements can be done through a configuration file, which can be edited in normal operation just along with your TEI documents without the need to restart the editor. Further requirements can be covered by defining a framework based on oXbytei inheriting all its functions. oXbytei even uses the TEI P5 framework by TEI-C as a base framework, so the awesome facsimile editor is present, too! SCDH provides the community with other frameworks, build on top of oXbytei, e.g., oXbytao with generic support for your templates and some common CSS styles on top of TEI-C's CSS; or ALEA NG for editing arabic literature.

In contrast, ediarum.BASE.edit sticks to the pre-version 23 framework-files. Because architecture is constraint to 2 bricks (and other reasons discussed below), nothing can be installed with a click, but you have to go through a complicated setup. If the defaults do not exactly fit your requirements, you will find yourself in a longer development process. Adaptation to project needs often involves editing author action files and the changes are only evaluated after restarting the editor. Ediarum also does not build on top of the TEI P5 framework by TEI-C, obviously due to the height 2 constraint.

Yes, ediarum.BASE.edit obviously offers more actions in its tool bar than oXbytei does. The smaller extend of actions is well considered. On top of its Java code base for building framework actions, oXbytei offers only author actions, that are considered really generic. Things that tend to flavoured (DTA, BBAW, SCDH Münster etc.) have to go into other frameworks on top. The philosophy is to provide generic bricks, that can be stacked and finally topped with a thin layer of project specific needs.

framework stack

Configuration inside or outside the framework!

In many aspects, ediarum sticks to <oXygen/> old-school framework customization. where one has to define customized author mode actions.

Example: https://github.com/ediarum/ediarum.BASE.edit/blob/master/ediarum.BASE.framework#L983C1-L1061C1

         <action>
          <field name="id">
           <String>persName</String>
          </field>
          <field name="name">
           <String>Personenname</String>
          </field>
          <field name="description">
           <String>Im Manuskript explizit erwähnte Person kennzeichnen</String>
          </field>
          <field name="largeIconPath">
           <String></String>
          </field>
          <field name="smallIconPath">
           <String></String>
          </field>
          <field name="accessKey">
           <String></String>
          </field>
          <field name="accelerator">
           <null/>
          </field>
          <field name="actionModes">
           <actionMode-array>
            <actionMode>
             <field name="xpathCondition">
              <String>ancestor-or-self::text
or ancestor-or-self::abstract
or ancestor-or-self::correspDesc/note
or ancestor-or-self::note[parent::notesStmt]
or ancestor-or-self::p[parent::physDesc]</String>
             </field>
             <field name="argValues">
              <serializableOrderedMap>
               <entry>
                <String>URL</String>
                <String>${ediarum_project_domain}${ediarum_projects_directory}${ediarum_project_name}${ediarum_index_person}</String>
               </entry>
               <entry>
                <String>element</String>
                <String>&lt;persName xmlns='http://www.tei-c.org/ns/1.0'  key='$ITEMS'>&lt;/persName></String>
               </entry>
               <entry>
                <String>item rendering</String>
                <String>$XPATH{/span}</String>
               </entry>
               <entry>
                <String>item separator</String>
                <String> </String>
               </entry>
               <entry>
                <String>item variable</String>
                <String>$XPATH{@xml:id}</String>
               </entry>
               <entry>
                <String>multiple selection</String>
                <String>true</String>
               </entry>
               <entry>
                <String>namespaces</String>
                <String>tei:http://www.tei-c.org/ns/1.0</String>
               </entry>
               <entry>
                <String>node</String>
                <String>//li</String>
               </entry>
              </serializableOrderedMap>
             </field>
             <field name="operationID">
              <String>org.bbaw.telota.ediarum.RegisterSurroundWithElementOperation</String>
             </field>
            </actionMode>
           </actionMode-array>
          </field>
          <field name="enabledInReadOnlyContext">
           <Boolean>false</Boolean>
          </field>
         </action>

It get's a bit technical. Yes, but that's where you have to go through!

Let's have a closer look: This is the action for wrapping a selected range of text in a <persName> element and setting the @key attribute to an @xml:id in the personography. If you want to change the element or attribute name, you have to change this file. If your personography does not fit, you have to change the XPaths. You have to restart the editor, to get the effect of your changes. Then, you have to re-distribute the customized framework to your staff, since these things only take effect when they are distributed inside a framework. I admit: Having the author mode operation org.bbaw.telota.ediarum.RegisterSurroundWithElementOperation and parsing a register file with XPaths is a huge advance over using ${ask()} with nested ${xpath_eval()} like in plain <oXygen/> framework customization––you really do not want to know details about them ;-).

oXbytei is similar, but different. A chain of editing atoms is involved that manipulates the XML tree step by step. First step: surround the selected range with an element. Second step: add an attribute, the value of which contains a selection from a user dialog. Let's look at the second step:

<?xml version="1.0" encoding="UTF-8"?>
<!-- editing atom -->
<a:authorAction xmlns:a="http://www.oxygenxml.com/ns/author/external-action"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.oxygenxml.com/ns/author/external-action http://www.oxygenxml.com/ns/author/external-action/authorAction.xsd"
  id="sel.person">
  <a:name>Select Person</a:name>
  <a:description>Select a person from suggestions.</a:description>
  <a:operations>
    <a:operation id="de.wwu.scdh.oxbytei.SelectAttributeValueOperation">
      <a:xpathCondition>true()</a:xpathCondition>
      <a:arguments>
        <a:argument name="name">${teilspProp(oxbytei.action.sel.person.name)}</a:argument>
        <a:argument name="elementLocation">self::*</a:argument>
        <a:argument name="message">Choose a person</a:argument>
        <a:argument name="rollbackOnCancel">true</a:argument>
        <a:argument name="icon">${framework(oXbytei)}/images/person-24.png</a:argument>
      </a:arguments>
    </a:operation>
  </a:operations>
  <a:accessKey/>
</a:authorAction>

The only interesting thing here is <a:argument name="name">${teilspProp(oxbytei.action.sel.person.name)}</a:argument>, which sets the attribute name to – a variable. This variable is defined in the configuration file. And the configuration file can exist inside or outside the framework. So you can leave the oXbytei framework untouched. For setting the attribute name, all you have to do is change a line in the config file and pass to your staff:

<property name="oxbytei.action.sel.person.name">ref</property>

Generating a list of persons from a register file or what ever resource you have, is even better. There is nothing like an XPath in the action definition above. But how does oXbytei know, how to generate the list? Answer: The de.wwu.scdh.oxbytei.SelectAttributeValueOperation delegates it to a powerful abstraction layer, that binds together the generation of lists of items (like person IDs) and a user dialog (check boxes or radio boxes or multi- or single select lists etc.) And all this is configured in the configuration file. The following generates the list from the personography given in a <prefixDef> in the current file, defining psn: URIs.

<plugin>
 <class>de.wwu.scdh.teilsp.extensions.LabelledEntriesXSLTWithContext</class>
    <type>de.wwu.scdh.teilsp.services.extensions.ILabelledEntriesProvider</type>
    <configurations>
        <configuration>
            <conditions>
                <condition domain="context">(self::*:persName | self::*:person | self::*:rs[@type eq 'person']) and //*:teiHeader//*:prefixDef[@ident eq 'psn']</condition>
                <condition domain="priority">10</condition>
                <condition domain="nodeType">attributeValue</condition>
                <condition domain="nodeName">ref</condition>
            </conditions>
            <arguments>
                <argument name="script">${pdu}/resources/xsl/entries-persons.xsl</argument>
                <argument name="parameters">prefix=psn</argument>
            </arguments>
        </configuration>
 </configurations>
</plugin>

The list is generated for an editing context defined in <conditions>: the attribute value of @ref in the context (self::*:persName | self::*:person | self::*rs:[@type eq 'person']) ... given that a certain prefix definition is present in the current document. As you see, you do not have to repeat yourself for different editing contexts.

In this example, the list is generated by an XSLT stylesheet. But there are numerous plugins for generating lists:

  • by XPath from local or remote files
  • by XSLT from local or remote files
  • by XQuery from local or remote files
  • from values written into the configuration file (handy for small fixed sets of values like for the rs/@type attribute)
  • from CSV files
  • from an SQL database
  • by a SPARQL query over a remote endpoint
  • by a SPARQL query over a set of RDF files

You can also define multiple sources for the same editing context, which will then be merged to the same user dialog, e.g., resources from the SPARQL endpoints of LoC and GND.

What about the user dialog? User dialogs are defined for editing contexts. In the example project, we wanted a ComboBox Dialog for the same editing context (<conditions>) like in the example above:

<plugin>
 <class>de.wwu.scdh.teilsp.ui.ComboBoxSelectDialog</class>
 <type>de.wwu.scdh.teilsp.ui.ISelectionDialog</type>
    <configurations>
        <configuration>
            <conditions>
                <condition domain="context">self::*:persName  | self::*:person | self::*:rs[@type eq 'person']</condition>
                <condition domain="priority">10</condition>
                <condition domain="nodeName">ref</condition>
                <condition domain="nodeType">attributeValue</condition>
            </conditions>
            <arguments>
                <argument name="title">Select person</argument>
                <argument name="icon">${framework(oXbytei)}/images/person-24.png</argument>
            </arguments>
        </configuration>
    </configurations>
</plugin>

In other editing contexts, like rdg/@wit, a check box dialog would be nice, and having one in oXbytei is straight forward! Simply change <class>.

The best news is: The configuration can live inside or outside the framework. So if you need to change the default configuration, which ships inside the framework, you can override it with a configuration outside of the framework. As a result, the framework can stay untouched. And that's why we are able to distribute it for easy installation with <oXygen/>'s add-on manager and why we can provide updates that the add-on manager automatically offers to you. Your adaptation to your project-specific needs won't be overwritten, since it leaves outside the framework's code base. When even the configuration needs an update, the extra framework for editing the configuration file has a solution.

Making generic software that can easily be adapted to specific requirements is one of the principles that drive software development at SCDH. Thus, developing an abstraction layer that first introduces a configuration file to <oXygen/> frameworks is a central feature, that makes oXbytei a next generation speedboat.

Overloaded actions

The critical apparatus is a domain, where oXbytei shines with its overloaded actions, which are unknown in other frameworks.

TEI knows three methods of variant encoding in the critical apparatus:

  1. parallel segmentation
  2. double end-point attached
  3. location referenced

While parallel segmentation can only be internal, both other methods can either be internally or externally realized. An external apparatus is kept outside of the main text and references it by some pointing mechanism.

Ediarum has actions for encoding the critical apparatus, but only following the parallel segmentation method. If you want to follow the double end-point method, which comes with great benefits, you will have to rewrite a bunch of actions in tiny framework widgets. No fun!

With oXbytei, you have support for parallel segmentation, internal and external double end-point, and internal location referenced encodings out of the box. (Wondering about an external location referenced apparatus? We think, that it might be encoded in such a great variety of ways, that we did not want to erect a norm by an implementation.) The actions simply evaluate, what you declared in your TEI header. E.g., with <variantEncoding method="double-end-point" location="internal"/>, oXbytei's actions will generate correct markup for an internal double end-point apparatus.

Here's an example of an overloaded editing atom, responsible for adding the markup of an apparatus entry. It defines several actions, and only the right one is activated by the XPath in <a:xpathCondition>, which evaluates the <variantEncoding> given in the header.

<?xml version="1.0" encoding="UTF-8"?>
<!-- editing atom -->
<a:authorAction xmlns:a="http://www.oxygenxml.com/ns/author/external-action"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.oxygenxml.com/ns/author/external-action http://www.oxygenxml.com/ns/author/external-action/authorAction.xsd"
  id="markup.apparatus">
  <a:name>Markup apparatus</a:name>
  <a:description>Make an apparatus entry on the current user selection.</a:description>
  <a:operations>
    <a:operation id="SurroundWithFragmentOperation">
      <a:xpathCondition>//teiHeader/encodingDesc/variantEncoding[@method eq 'parallel-segmentation' and @location eq 'internal']</a:xpathCondition>
      <a:arguments>
        <a:argument name="fragment"><![CDATA[<app xmlns="http://www.tei-c.org/ns/1.0"><lem/><rdg>${caret}</rdg></app>]]></a:argument>
      </a:arguments>
    </a:operation>
    <a:operation id="de.wwu.scdh.oxbytei.ExpandingXSLTOperation">
      <a:xpathCondition>//teiHeader/encodingDesc/variantEncoding[@method eq 'double-end-point' and @location eq 'internal']</a:xpathCondition>
      <a:arguments>
        <a:argument name="script">${framework(oXbytei)}/xsl/app-dep-int.xsl</a:argument>
        <a:argument name="sourceLocation">${teilspProp(oxbytei.action.app.dep.int.sourceLocation)}</a:argument>
        <a:argument name="targetLocation">${teilspProp(oxbytei.action.app.dep.int.targetLocation)}</a:argument>
        <a:argument name="action">${teilspProp(oxbytei.action.app.dep.int.action)}</a:argument>
        <a:argument name="externalParams">container=${anchorsContainer},startId=${startAnchorId},endId=${endAnchorId},${teilspProp(oxbytei.action.app.dep.int.externalParams)}</a:argument>
      </a:arguments>
    </a:operation>
    <a:operation id="de.wwu.scdh.oxbytei.ExpandingXSLTOperation">
      <a:xpathCondition>//teiHeader/encodingDesc/variantEncoding[@method eq 'double-end-point' and @location eq 'external']</a:xpathCondition>
      <a:arguments>
        <a:argument name="script">${framework(oXbytei)}/xsl/app-dep-ext.xsl</a:argument>
        <a:argument name="sourceLocation">${teilspProp(oxbytei.action.app.dep.ext.sourceLocation)}</a:argument>
        <a:argument name="targetLocation">${teilspProp(oxbytei.action.app.dep.ext.targetLocation)}</a:argument>
        <a:argument name="action">${teilspProp(oxbytei.action.app.dep.ext.action)}</a:argument>
        <a:argument name="externalParams">container=${anchorsContainer},startId=${startAnchorId},endId=${endAnchorId},${teilspProp(oxbytei.action.app.dep.ext.externalParams)}</a:argument>
      </a:arguments>
    </a:operation>
    <a:operation id="InsertFragmentOperation">
      <a:xpathCondition>//teiHeader/encodingDesc/variantEncoding[@method eq 'location-referenced' and @location eq 'internal']</a:xpathCondition>
      <a:arguments>
        <a:argument name="fragment"><![CDATA[<app xmlns="http://www.tei-c.org/ns/1.0"><lem>${selection}</lem><rdg>${caret}</rdg></app>]]></a:argument>
      </a:arguments>
    </a:operation>
  </a:operations>
  <a:accessKey/>
</a:authorAction>

This is an example of the idea, that led to oXbytei's name: Use the TEI document as a configuration source for the framework, at least as much as possible. Overloaded actions are a key for making this happen.

In this vein, a framework becomes generic, since it provides not only functions for the one encoding technique, that some software developers are in favour of, but the encoding techniques that are specified in the TEI reference.

Yes, at SCDH Münster we also provide XSLT for handling all these encoding methods of the critical apparatus. But that's not part of the framework. oXbytei is for editing TEI. We have other software components, that also Do one thing and do it well!

Entity linking: @ref vs. @key

Ediarum favours using the @key attribute for linking named entities to the registers of your edition. E.g.:

... <persName key="pGundulf">Gundulf</persName>, the architect of the <placeName key="lRochester">Rochester</placeName> cathedral ...

As we've seen above, we can change the attribute to ref, if we need to.

By default, oXbytei uses the ref attribute. Markup will look like this:

... <persName ref="psn:Gundulf">Gundulf</persName>, the architect of the <placeName ref="plc:Rochester">Rochester</placeName> cathedral ...

It can be re-configured for using the key, but ref is default. Why?

The reason are the datatypes of the key and the ref attributes. Let's look them up in the TEI reference:

  • key: "teidata.text", and "The value may be a unique identifier from a database, or any other externally-defined string identifying the referent. No particular syntax is proposed for the values of the @key attribute, since its form will depend entirely on practice within a given project."

  • ref: "1–∞ occurrences of teidata.pointer separated by whitespace" and "The value must point directly to one or more XML elements or other resources by means of one or more URIs, separated by whitespace. If more than one is supplied the implication is that the name identifies several distinct entities."

This means: Using @key always requires some configuration and there is no means to express in TEI, how the values of the attribute are to be evaluated. So, @key is based on convention, that has to be configured for the editor and for all subsequent tools that process the TEI documents.

In contrast, the values of @ref are simply pointers, i.e., URIs, and URIs are to be processed in a well-defined way. Moreover, TEI provides a very elegant way for shortening URIs by <prefixDef> in the document header:

<listPrefixDef>
  <prefixDef matchPattern="(.+)" replacementPattern="persons.xml#$1" ident="psn"/>
  <prefixDef matchPattern="(.+)" replacementPattern="places.xml#$1" ident="plc"/>
  <!-- ... -->
</listPrefixDef>

This is where the psn: and plc: prefixes in the example above come from. With the prefix definition in place, the psn custom URI psn:Gundulf expands to the relative URI person.xml#Gundulf, i.e., the fragment with the ID Gundulf in the file person.xml.

@ref and <prefixDef> together provide a suitable configuration source, that can be evaluated by a framework to provide entity lists. The XML file, which serves as the personography is mentioned in the header and the framework can evaluate it. So the choice was clear: If there's a way to get things working out of the box, that's the way to go. And it even leads to an elegant encoding style!

Nice side effect: Since this uses URIs, all subsequent components that have to evaluate entity linking (XSLT for generating HTML, etc.) can use generic algorithms, since URIs must be evaluated as URIs.

There have been some discussions on the TEI mailing list on the ref vs. key issue. Whatever you decide to use: oXbytei can easily be configured to use key, and ediarum.BASE.edit can also be adapted to use ref.

Clone this wiki locally