<oXygen/> XML Editor © 2008 syncRO soft ltd. | Advanced Customization Tutorial - Document Type AssociationsoXygen Author is highly customizable. Practically you can associate an entire
class of documents (grouped logically by some common features like namespace, root
element name or filename) to a bundle consisting of a CSS stylesheets, validation
schemas, catalog files, templates for new files, transformation scenarios and even
custom actions. This is called a Document Type
Association. Creating the Basic AssociationIn this section we will create a Document Type
Association for a set of documents. As an example we will create
a light documentation framework, similar to DocBook and create a complete
customization of the Author editor. You can find the complete files that were used in this tutorial in the Example Files Listings. Our documentation framework will be very simple. The documents will be
either articles or books, both composed of
sections. The sections may contain
titles, paragraphs, figures,
tables and other sections. To complete the
picture, each section will include a def element from another
namespace. The first schema file:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.oxygenxml.com/sample/documentation"
xmlns:doc="http://www.oxygenxml.com/sample/documentation"
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts"
elementFormDefault="qualified">
<xs:import namespace=
"http://www.oxygenxml.com/sample/documentation/abstracts"
schemaLocation=
"abs.xsd"/>
The namespace of our documents will be
http://www.oxygenxml.com/sample/documentation. The
namespace of the def element is
http://www.oxygenxml.com/sample/documentation/abstracts. Now let's define the structure of the sections. They all start with a
title, then have the optional def element then either a
sequence of other sections, or a mixture of paragraphs, images and
tables. <xs:element name="book" type="doc:sectionType"/>
<xs:element name="article" type="doc:sectionType"/>
<xs:element name="section" type="doc:sectionType"/>
<xs:complexType name="sectionType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element ref="abs:def" minOccurs="0"/>
<xs:choice>
<xs:sequence>
<xs:element ref="doc:section" maxOccurs="unbounded"/>
</xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element ref="doc:para"/>
<xs:element ref="doc:image"/>
<xs:element ref="doc:table"/>
</xs:choice>
</xs:choice>
</xs:sequence>
</xs:complexType>
The paragraph contains text and other styling markup, such as bold
(b) and italic (i) elements. <xs:element name="para" type="doc:paragraphType"/>
<xs:complexType name="paragraphType" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="b"/>
<xs:element name="i"/>
</xs:choice>
</xs:complexType>
The image element has an attribute with a reference to the
file containing image data. <xs:element name="image">
<xs:complexType>
<xs:attribute name="href" type="xs:anyURI" use="required"/>
</xs:complexType>
</xs:element>
The table contains a header row and then a sequence of rows
(tr elements) each of them containing the cells. Each cell
has the same content as the paragraphs. <xs:element name="table">
<xs:complexType>
<xs:sequence>
<xs:element name="header">
<xs:complexType>
<xs:sequence>
<xs:element name="td" maxOccurs="unbounded"
type="doc:paragraphType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="tr" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="td" type="doc:tdType"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="tdType">
<xs:complexContent>
<xs:extension base="doc:paragraphType">
<xs:attribute name="row_span" type="xs:integer"/>
<xs:attribute name="column_span" type="xs:integer"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
The def element is defined as a text only element in the
imported schema abs.xsd: <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace=
"http://www.oxygenxml.com/sample/documentation/abstracts">
<xs:element name="def" type="xs:string"/>
</xs:schema>Now that we defined our XML data structure, let's start styling
it... If you read the Simple
Customization Tutorial then you already have some basic notions
about creating simple styles. Our document contains elements from different
namespaces, so we will use CSS Level 3 extensions supported by the oXygen
layout engine to associate specific properties with that element. ![[Note]](img/note.gif) | Note |
|---|
Please note that the CSS Level 3 is a standard under development, and
has not been released yet by the W3C. However, it addresses several
important issues like selectors that are namespace aware and values for
the CSS properties extracted from the attributes of the XML documents.
Although not (yet) conforming with the current CSS standard these are
supported by the oXygen Author. |
Defining the General Layout.We are now creating the basic layout of the rendered documents. Elements that are stacked one on top of the other are:
book, article, section,
title, figure, table,
image. These elements are marked as having
block style for display. Elements that are placed one
after the other in a flowing sequence are: b,
i. These will have inline display.
/* Vertical flow */
book,
section,
para,
title,
image,
ref {
display:block;
}
/* Horizontal flow */
b,i {
display:inline;
}
![[Important]](img/important.gif) | Important |
|---|
Having block display children in an
inline display parent, makes oXygen Author change
the style of the parent to block display. |
Styling the section Element.The title of any section must be bold and smaller than the title of
the parent section. To create this effect we have to create a sequence
of CSS rules. The * operator matches any element, so we can
use it to match titles having progressive depths in the document.
title{
font-size: 2.4em;
font-weight:bold;
}
* * title{
font-size: 2.0em;
}
* * * title{
font-size: 1.6em;
}
* * * * title{
font-size: 1.2em;
}
![[Note]](img/note.gif) | Note |
|---|
CSS rules are combined as follows: All the rules that match an element are kept as a
list. The more specific the rule is, the further it will
be placed to the end of the list. If there is no difference in the specificity of the
rules, they are placed in the list in the same order as
they appear in the CSS document. The list is then iterated, and all the properties from
the rules are collected, overwriting the already
collected values from the previous rules. That is why
the font-size is changed depending on the depth of the
element, while the font-weight property remains
unchanged - no other rule is overwriting it.
|
It's useful to have before the title a constant text, indicating that
it refers to a section. This text can include also the current section
number. To achieve we have to use the :before and
:after pseudo elements, plus the CSS counters. We declare a counter named sect for each
book or article. The counter is set to
zero at the beginning of each such element:
book,
article{
counter-reset:sect;
}
The sect counter is incremented with each
section, that is the a direct child of a
book or an article element.
book > section,
article > section{
counter-increment:sect;
}
The "static" text that will prefix the section title is composed of
the constant "Section ", followed by the decimal value of the
sect counter and a dot.
book > section > title:before,
article > section > title:before{
content: "Section " counter(sect) ". ";
}
To make the documents easy to read, we will add a margin to the
sections. In this way the higher nesting level, the larger the left side
indent. The margin is expressed relatively to the parent bounds:
section{
margin-left:1em;
margin-top:1em;
}
In the above screenshot you can see a sample XML document rendered by
our CSS. The selection "avoids" the text that is generated by the CSS
"content" property. This happens because the CSS generated text is not
present in the XML document and is just a visual aid. Styling the table Element.There are standard CSS properties used to indicate what elements are
tables, table rows and table cells. What CSS is missing is the
possibility to indicate the cell spanning. oXygen Author offers support
for adding an extension to solve this problem. This will be presented in
the next chapters. The table in this example is a simple one. The header must be
formatted in a different way than the ordinary rows, so it will have a
background color.
table{
display:table;
border:1px solid navy;
margin:1em;
}
tr, header{
display:table-row;
}
header{
background-color: silver;
color:inherit
}
td{
display:table-cell;
border:1px solid navy;
padding:1em;
}
![[Note]](img/note.gif) | Note |
|---|
Children elements with block or
table-caption display placed at the beginning or
the end of an element displayed as a table, will be
grouped and presented as blocks at the top or the bottom of the
table. |
![[Note]](img/note.gif) | Note |
|---|
Mixing elements having table-cell,
table-group, table-row, etc.. display
type with others that have block or inline
display or with text content breaks the layout of the table. In such
cases the table is shown as a block. |
![[Note]](img/note.gif) | Note |
|---|
Having child elements that do not have table-cell or
table display in a parent with
table-row display breaks the table layout. In this
case the table display is supported for the children of
the table-row element in order to allow sub-tables in
the parent table. |
![[Note]](img/note.gif) | Note |
|---|
oXygen Author can automatically detect the spanning of a cell,
without the need to write a Java extension for this. This happens if the span of the cell element is specified using
the colspan and rowspan attributes, just like in HTML, or
cols and rows attributes. For instance, the following XML code:
<table>
<tr>
<td>Cell 1.1</td>
<td>Cell 1.2</td>
<td>Cell 1.3</td>
</tr>
<tr>
<td>Cell 2.1</td>
<td colspan="2" rowspan="2">
Cell spanning 2 rows and 2 columns.
</td>
</tr>
<tr><td>Cell 3.1</td></tr>
</table>
using the CSS:
table{
display: table;
}
tr{
display: table-row;
}
td{
display: table-cell;
}
is rendered correctly:
Table 7.1. Built-in Cell Spanning | Cell 1.1 | Cell 1.2 | Cell 1.3 | | Cell 2.1 | Cell
spanning 2 rows and 2 columns | | Cell 3.1 |
|
Because in our schema the td tag has the attributes
row_span and column_span that are not automatically recognized by
oXygen Author, we will implement a Java extension which will provide
information about the cell spanning. See the section Configuring a Table Cell
Span Provider. Styling the Inline Elements.The "bold" style is obtained by using the font-weight CSS
property with the value bold, while the "italic" style is
specified by the font-style property:
b {
font-weight:bold;
}
i {
font-style:italic;
}
Styling Elements from other NamespaceIn the CSS Level 1, 2, and 2.1 there is no way to specify if an
element X from the namespace Y should be presented differently from the
element X from the namespace Z. In the upcoming CSS Level 3, it is
possible to differentiate elements by their namespaces. oXygen Author
supports this CSS Level 3 functionality. For more information see the
Namespace Selectors
section. To match our def element we will declare its namespace,
bind it to the abs prefix, and then
write a CSS rule:
@namespace abs "http://www.oxygenxml.com/sample/documentation/abstracts";
abs|def{
font-family:monospace;
font-size:smaller;
}
abs|def:before{
content:"Definition:";
color:gray;
}
The CSS 2.1 does not specify how an element can be rendered as an
image. To overpass this limitation, oXygen Author supports a CSS Level 3
extension allowing to load image data from an URL. The URL of the image
must be specified by one of the element attributes. ![[Note]](img/note.gif) | Note |
|---|
oXygen Author recognizes the following image file formats:
JPEG, GIF,
PNG and SVG. The oXygen
Author for Eclipse does not render the SVG
files. |
image{
display:block;
content: attr(href, url);
margin-left:2em;
}
Our image element has the required attribute
href of type xs:anyURI. The
href attribute contains an image location so the
rendered content is obtained by using the
function: attr(href, url) ![[Important]](img/important.gif) | Important |
|---|
The first argument is the name of the attribute pointing to the
image file. The second argument of the attr function
specifies the type of the content. If the type has the
url value, then oXygen identifies the content as
being an image. If the type is missing, then the content will be the
text representing the attribute value. |
![[Important]](img/important.gif) | Important |
|---|
oXygen Author handles both absolute and relative specified URLs.
If the image has an absolute URL
location (e.g:
"http://www.oasis-open.org/images/standards/oasis_standard.jpg")
then it is loaded directly from this location. If the image URL is
relative specified to the XML document
(e.g: "images/my_screenshot.jpg") then the location is obtained by
adding this value to the location of the edited XML document. |
An image can also be referenced by the name of a DTD entity which
specifies the location of the image file. For example if the document
declares an entity graphic which points
to a JPEG image file: <!ENTITY graphic SYSTEM "depo/keyboard_shortcut.jpg" NDATA JPEG> and the image is referenced in the XML document by specifying the name
of the entity as the value of an attribute: <mediaobject>
<imageobject>
<imagedata entityref="graphic" scale="50"/>
</imageobject>
</mediaobject>the CSS should use the functions url,
attr and unparsed-entity-uri for displaying the image in the
Author mode: imagedata[entityref]{
content: url(unparsed-entity-uri(attr(entityref)));
}
Marking elements as foldableYou can specify what elements are collapsible. The collapsible
elements are rendered having a small triangle icon in the top left
corner. Clicking on this icon hides or shows the children of the
element. In our case, we will mark the section elements as
foldable. We will leave only the title child elements
visible.
section{
foldable:true;
not-foldable-child: title;
}
Marking elements as linksYou can specify what elements are links. The text content specified in
the :before pseudo element will be underlined. When
hovering the mouse over that content the mouse pointer will change to
indicate that it can follow the link. Clicking on a link will result in
the referred resource being opened in an editor. In our case we will
mark the link elements as links with the href
attribute indicating the referred location.
link[href]:before{
display:inline;
link:attr(href);
content: "Click to open: " attr(href);
}
![[Note]](img/note.gif) | Note |
|---|
If you plan to use IDs as references for links, the value of the
link property should start with a sharp sign(#). This will ensure
that the default link target reference finder implementation will
work and clicking on the link will send you to the indicated
location in the document. For more details about the link target
reference finder read the section Configuring a
Link target reference finder. Example 7.1. IDs as references for links link[linkend]:before{
display:inline;
link: "#" attr(linkend);
content: "Click to open: " attr(linkend);
}
|
Third Step. The Association.Now that we have the XML Schema and the CSS stylesheet for the documents
we intend to edit, we can proceed to create a distributable framework
package for our content authors.
Organizing the Framework FilesFirst create a new folder called sdf (from "Simple Documentation Framework") in {oXygen_installation_directory}/frameworks. We will use
this folder to store all files related to our documentation framework.
Let's organise it a bit, by creating the following folder
structure:
oxygen
frameworks
sdf
schema
css
![[Important]](img/important.gif) | Important |
|---|
The frameworks directory is the container
where all the oXygen framework customizations are located. Each subdirectory contains files related to a specific type of XML
documents: schemas, catalogs, stylesheets, CSS files, etc. Distributing a framework means delivering a framework
directory. |
![[Important]](img/important.gif) | Important |
|---|
We assume you have the right to create files and folder inside the
oXygen installation directory. If you do not have this right, you
will have to install another copy of the program in a folder you
have access to, the home directory for instance, or your desktop.
You can download the "all platforms" distribution from the oXygen
website and extract it in the chosen folder. To test your framework distribution you will need to copy it in
the frameworks directory of the newly installed
application and start oXygen by running the provided start-up script
files. |
We should copy the created schema files abs.xsd
and sdf.xsd, sdf.xsd being the
master schema, to the schema directory and the CSS
file sdf.css to the css
directory. We must specify when oXygen should use the files created in the
previous section by creating a document type association. Open the
Document Type dialog by following the procedure: Open the Options Dialog, and select the Document Types
Association option pane. Select the Developer user
role from the User role combo
box at the top of the dialog. This is important, because it will
allow us to save the document type association in a file on
disk, instead of oXygen options. Click on the New
button.
In the displayed dialog, fill in the following data: - Name
Enter SDF - This is the
name of the document type. - Description
Enter Simple Documentation
Framework - This is a short description
helping the other users understand the purpose of the
Document Type. - Storage
The storage refers to the place where the Document Type
settings are stored. Internal means
the Document Types are stored in the default oXygen
preferences file. Since we want to share the Document Type
to other users, we must select External, and choose a file. The file must be in the {oXygen_installation_directory}/frameworks/sdf
directory. A possible location is /Users/{user_name}/Desktop/oxygen/frameworks/sdf/sdf.framework.
The framework directory structure will be: oxygen
frameworks
sdf
sdf.framework
schema
sdf.xsd
css
sdf.css
- Rules
If a document opened in oXygen matches one of the rules
defined for the Document Type, then it is activated. Press the
Add button from the Rules section. Using
the newly displayed dialog, we add a new rule that matches
documents with the root from the namespace:
http://www.oxygenxml.com/sample/documentation.
The root name, file name or PublicID are not
relevant.
A document matches a rule when it fulfills the conditions
imposed by each field of the rule: - Namespace
the namespace of the root element declared in
the XML documents of the current document type. A
value of <ANY> matches any namespace in an XML
document. - Root local name
The local name of the root element of the XML
documents of the current document type. A value of
<ANY> matches any local name of the root
element. - File name
The file name of the XML documents of the
current document type. A value of <ANY> matches
any file name. - Public ID
The public ID of the XML documents of the
current document type (for a document validated
against a DTD). A value of <ANY> matches any
public ID. - Java class
The full name of a Java class that has access
to all root element attributes and the above 4
values in order to decide if the document matches
the rule.
Java API: Rules implemented in JavaAn alternative to the rule we defined for our association is to
write the entire logic in Java. Create a new Java project, in your IDE. Create the lib directory in the Java
project directory and copy there the oxygen.jar file from the {oXygen_installation_directory}/lib. The
oxygen.jar contains the Java
interfaces we have to implement and the available Author API
needed to access its features. Create the class
simple.documentation.framework.CustomRule.
This class must implement the
ro.sync.ecss.extensions.api.DocumentTypeCustomRuleMatcher
interface. The interface defines two methods:
matches, and
getDescription. The matches method is
the one that is invoked when the edited document
must be checked against the document type
association. It takes as arguments the root local
name, its namespace, the document location URI,
the PublicID and the root element attributes. It
must return true when the document
matches the association. The getDescription
method returns a description of the rule.
Here is the implementation of these two methods. The
implementation of matches is just a
Java equivalent of the rule we defined earlier. public boolean matches(
String systemID,
String rootNamespace,
String rootLocalName,
String doctypePublicID,
Attributes rootAttributes) {
return "http://www.oxygenxml.com/sample/documentation"
.equals(rootNamespace);
}
public String getDescription() {
return "Checks if the current Document Type Association"
+ " is matching the document.";
}The complete source code is found in the Example Files
Listings, the Java Files section. Package the compiled class into a jar file. Here is an example of an ANT
script that packages the classes
directory content into a jar archive named sdf.jar:
<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="dist">
<target name="dist">
<jar destfile="sdf.jar" basedir="classes">
<fileset dir="classes">
<include name="**/*"/>
</fileset>
</jar>
</target>
</project>
Copy the sdf.jar file into the
frameworks/sdf directory. Add the sdf.jar to the Author
classpath. To do this select SDF Document Type from the Document Type Association options
page and press the Edit
button. Select the Classpath tab in the lower
part of the dialog. Press the
Add button . In the displayed dialog enter
the location of the jar file, relative to the oXygen
frameworks directory. If you are in
the process of developing the extension actions you can also
specify a path to a directory which holds compiled Java
classes.
Clear the rules we defined before by using the
Remove button. Press the
Add button from the Rules section. Press the Choose button that
follows the Java class value. The
following dialog is displayed:
To test the association, open the sdf.xml
sample and validate it. In the dialog for editing the Document Type properties, in the bottom
section there are a series of tabs. The first one refers to the schema
that is used for validation of the documents that match the defined
association Rules. ![[Important]](img/important.gif) | Important |
|---|
If the document refers a schema, using for instance a
DOCTYPE declaration or a
xsi:schemaLocation attribute, the schema from the
document type association will not be used when validating. |
- Schema Type
Select from the combo box the value XML Schema. - Schema URI
Enter the value ${frameworks}/sdf/schema/sdf.xsd. We should
use the ${frameworks} editor variable
in the schema URI path instead of a full path in order to be
valid for different oXygen installations. ![[Important]](img/important.gif) | Important |
|---|
The ${frameworks} variable is
expanded at the validation time into the absolute
location of the directory containing the
frameworks. |
Select the Author tab from the Document Type
edit dialog. By clicking on the CSS label in the
right part of the tab the list of associated CSS files is shown. Here you can also specify how should the CSSs defined in the document
type be treated when there are CSSs specified in the document(with
xml-stylesheet processing instructions). The CSSs from
the document can either replace the CSSs defined in the document type
association or merge with them. Add the URI of the CSS file sdf.css we already
defined. We should use the ${frameworks}
editor variable in the file
path. The Title text field refers to a symbolic name
for the stylesheet. When adding several stylesheets with different
titles to a Document Type association, the content author can select
what CSS will be used for editing from the Author
CSS Alternatives toolbar. This combo-box from the toolbar is also populated in case your XML
document refers CSS files directly using xml-stylesheet
processing instructions, and the processing instructions define titles
for the CSS files. ![[Note]](img/note.gif) | Note |
|---|
The CSS settings dialog allows to create a
virtual
xml-stylesheet processing instructions. The CSS files
defined in the Document Type Association dialog and the
xml-stylesheet processing instructions from the XML
document are processed together, as being all a list of processing
instructions. |
oXygen Author fully implements the W3C recommendation regarding
"Associating Style Sheets with XML documents". For more information see:
http://www.w3.org/TR/xml-stylesheet/
http://www.w3.org/TR/REC-html40/present/styles.html#h-14.3.2 Testing the Document Type AssociationTo test the new Document Type create an XML instance that is
conforming with our Simple Document Format. We will not specify an XML
Schema location directly in the document, using an
xsi:schemaLocation attribute; oXygen will detect
instead its associated document type and use the specified
schema.
<book xmlns="http://www.oxygenxml.com/sample/documentation"
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts">
<title>My Technical Book</title>
<section>
<title>XML</title>
<abs:def>Extensible Markup Language</abs:def>
<para>In this section of the book I will
explain different XML applications.</para>
</section>
</book>
When trying to validate the document there should be no errors. Now
modify the title to title2. Validate again.
This time there should be one error:
cvc-complex-type.2.4.a: Invalid content was found starting with element
'title2'. One of '{"http://www.oxygenxml.com/sample/documentation":title}'
is expected.
Undo the tag name change. Press on the Author
button at the bottom of the editing area. oXygen should load the CSS
from the document type association and create a layout similar to
this: Using a file explorer, go to the oXygen frameworks directory. Select the sdf directory and make an archive from it. Move it to
another oXygen installation (eventually on another computer). Extract it
in the frameworks directory. Start oXygen and test
the association as explained above. If you create multiple document type associations and you have a
complex directory structure it might be easy from the deployment point
of view to use an oXygen all platforms distribution. Add your framework
files to it, repackage it and send it to the content authors. ![[Warning]](img/warning.gif) | Warning |
|---|
When deploying your customized sdf directory please make sure that
your sdf directory contains the sdf.framework file (that is the file
defined as External Storage in Document Type Association dialog
shall always be stored inside the sdf directory). If your external
storage points somewhere else <oXygen/> will not be able to update the
Document Type Association options automatically on the deployed
computers. |
© 2008 syncRO soft ltd. |