We need a linking strategy for creating good, single-source, cross-references that work as well on paper as in a web page. There is more to the problem of linking than the semantics of the link, because the text surrounding a link should probably be different, depending on the medium of publication. Paper-based links, for example, generally work best with generated text, while hyperlinks are hotspots naturally inserted in the document content. A supportive authoring environment is needed for writers of single-source material.
I’m a big fan of FrameMaker, the desktop tool, and have been for many years. I’ve written thousands of pages with it, and it has rarely let me down. I love the tool, as long as I don’t have to use it for SGML or XML content. To be completely honest, what always does it for me is FrameMaker’s cross-referencing capabilities. It’s very easy to create cross-references as an author, and it’s equally easy to define new cross-referencing formats if you’re a template developer. Technically, it’s a marvel to behold.
It’s not particularly strange, then, that every new linking facility I use is measured against FrameMaker. Every feature, every way I can create, modify, or in other ways manipulate a cross-reference with a new tool is compared to FrameMaker. And you know what: in most cases, in spite of the apparent power of XLinks, FrameMaker wins. It’s sheer magic to have the generated text (e.g., “See Section 5.1.3 on Page 38”) appear, be updated, or traversed. It’s a bit perverted, I know, but if that’s what it takes, then fine; I’m going to continue doing it, in awe.
Now, FrameMaker is a desktop tool, and while it’s been marketed as a single-source publishing tool, it’s heavily geared towards paper output. It’s the nature of desktop publishing, really, and there’s nothing wrong with focusing on paper publishing. But in these times of XML, markup, and all things “X”, we’re moving away from paper, or at least expanding or extending from that particular field of publishing. Single-source publishing is hot, and so whatever tool you’re going to pick, you’ll have to think about how to best use your tool, not only for paper, but also simultaneously for other publishing media, such as CD-ROMs or the web.
Cross-references, of course, become quite different once you leave paper, or otherwise expand your horizons. Page references, obviously, are the first thing to go; while there are “pages” on the Internet, they are not the kind of pages you can or should number. Your cross-references become hyperlinks, “hot spots” that traverse a link once clicked on.
The question is, how do you create good, preferably single-source, cross-references that work just as well on paper as on a web page? Or more generically, what kind of linking strategy should you use to be able to write single-source and publish the results anywhere, without the awkwardness1 so typical to today’s rather limited reuse? And remember, we’re not just talking about cross-references; the same strategies should apply to fragment inclusions, image referencing, or just about any link imaginable.
This is an area where FrameMaker falls a bit short; actually, it’s a bit on the heavy side with the whole single-source concept, and there really isn’t a very good way to create links that works in both media. What a disappointment for me! Fortunately, there’s XML.
A paper-based cross-reference is basically a page reference, perhaps with the target’s title or number, or both, included. To a reader, it’s just text similar to other content and just a static pointer, at least until IBM finishes its “active paper” project. Here’s an example:
Rabbits are outside the scope of this section. For detailed information about these lovable creatures, see Section 7, Rabbits, Page 91.
In a reasonably well-implemented system, the word “Section” is generated and probably based on the target element type. “Rabbits”, on the other hand, is fetched from the target element’s title; it’s reasonable to assume that it’s the first child of a section element. The page reference is generated when producing the paper output, and the exact method of how to achieve it depends on the print process used.
The source XML might look like this:
<p>Rabbits are outside the scope of this section. For detailed information about these lovable creatures, see <ref target="id-rabbits"/>.</p>
For now, let’s simply assume that the ref element is an IDREF-based pointer. This particular link, then, requires that there’s an element with an ID attribute with the value "id-rabbits" somewhere else (probably in the aforementioned section element) in the same physical XML file.
An online reference, on the other hand, is a hyperlink that — ideally — is embedded in text. It’s an active link, traversed when clicking on it. Now, if we made a direct online translation of the above markup, the result might look like this, with a hyperlink instead of the node count, generated text, and page reference:
Rabbits are outside the scope of this section. For detailed information about these lovable creatures, see Rabbits.
While this works, it’s wordy and doesn’t really look like a sentence that a writer would have produced, had she been able to write for online publishing only. It’s clear that what we have here is a baddish case of single-source publishing. Preferable would be something like this:
Rabbits are outside the scope of this section.
Nice, clean, and well-adjusted to the online way of reading. But what if we preferred to exclude the link altogether when publishing online? If we simply instructed the conversion script to exclude the link, the result would be, well, less than adequate:
Rabbits are outside the scope of this section. For detailed information about these lovable creatures, see .
Whoops. I have a couple of manuals that contain sentences like this, and I’m subscribed to a number of news services on my PDA where references always seem to mysteriously disappear, so this is unfortunately not a rare occurrence. In almost every SGML or XML project I’ve participated in, where links have disappeared in one media or another, the DTD or schema has been to blame, sans direct bugs in stylesheets. What we need is a design approach to creating DTDs (or schemas, for that matter) that make conversion and other processing easier and more logical.
The problem with the above lies (mostly) in the scope of the linking element. A very common approach is to simply define a mixed content model, like this:
<!ELEMENT p (#PCDATA|ref|%inline.stuff;)* > <!ELEMENT ref EMPTY > <!ATTLIST ref target IDREF #REQUIRED >
In other words, there is a linking element but it’s only there to mark the spot where you want your generated text, page reference, and so on. It does not identify the whole semantics of the link. In fact, the whole sentence
For detailed information about these lovable creatures, see Section 7, Rabbits, Page 91.
is part of the link semantics. It says what the link is for, and it can survive without the ref element no better than the ref element can survive without it. Consequently, we need a wrapper element for not only the linking element, but for the whole semantic link:
<!ELEMENT p (#PCDATA|xref-wrapper|%inline.stuff;)* > <!ELEMENT xref-wrapper (#PCDATA|ref)* > ...
A markup that handles the link correctly then causes the following markup in our example:
<p>Rabbits are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref target="id-rabbits"/>.</xref-wrapper></p>
This means that if we don’t need the link (and its surrounding sentence) online, we can simply instruct the conversion script to exclude the xref-wrapper element and its contents.
But what if we need a hyperlink, like the one in our example above, where we removed the “paper-based link” and its paper-styled sentence and used a hyperlink element embedded in the first sentence instead? Well, I guess you can now see where this is going:
<p><hlink target="id-rabbits">Rabbits</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref target="id-rabbits"/>. </xref-wrapper></p>
This solution is pretty neat because on paper, the xref-wrapper element does everything needed. The ref element is used to create a node count, fetch the target element’s contents (i.e., the target’s title), and create a page count. The hlink element does not receive any special treatment; its contents appear as ordinary text. Online, however, the xref element is discarded, and the hlink element becomes a hypertext link.
The downside to this approach, of course, is that whenever we need to create a hyperlink version of our paper-based link, we need to create the link twice. It’s fairly logical, though, because while both links in the example serve the same purpose, they’re not the same; they’re not identical. It’s reasonable to expect that they’re created separately. After all, they don’t have to point to the same target; online, a different target (or no target at all) may be required.
There are other problems to our basic solution, too: for one thing, the ID/IDREF pair mechanism is clearly insufficient since it requires that both the source and the target are located in the same physical file. The ID/IDREF mechanism is largely a leftover from SGML, and not very useful when going online since it’s probable that we’ll use more than one file to achieve our goals. In fact, single-source document creation practically requires it.
Let’s refine what we have, then.
As I pointed out, in my mind, the ID/IDREF mechanism is mostly a leftover from the SGML days. It’s certainly a useful leftover for some applications, since any such link must be validated by an XML parser (well-formed XML documents lack the concept of the ID attribute type, a frequent source of criticism against XML 1.0). When moving to single-source publishing, XML fragments, and the like, this enforced validation is no longer practical since we cannot guarantee that the target resides in the same physical file as the source.
XLink (see [XLink Recommendation]) solves this particular problem.2 XLink is the W3C linking recommendation, intended from the very beginning to provide XML with a standardized way of expressing links, or more generically, relations between resources. We no longer have built-in link validation in the XML processor; on the other hand, such validation is often impractical.
XLink comes in two flavors: simple and extended. Simple XLink is really a glorified HTML link. It has a source and a target, and the link information always resides in the source element. Simple XLink is expressed using attributes, which makes it very useful. Here’s our ref element, expressed as a Simple XLink:
<!ELEMENT ref EMPTY > <!ATTLIST ref xlink:type="simple" xlink:href CDATA #IMPLIED xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink" >
Obviously, there’s more to XLinks, even the simple ones, but this will do just nicely for our purposes. So, applied on our basic single-source cross-referencing example, we get:
<p><hlink xlink:type="simple" xlink:href="id-rabbits"> Rabbits</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref xlink:type="simple" xlink:href="id-rabbits"/>. </xref-wrapper></p>
This is quite wordy, XML-wise, but essentially, it gives us what we need. However, it doesn’t solve the problem of having to create the link twice. A fancy customization of the XML editor can probably help out, but we might benefit from a different linking model, especially if the paper and online targets are always, or nearly always, the same.
Extended XLink is a way to create multi-ended links. Often, people interpret this as meaning links with multiple targets and envision some fancy application that offers a choice of link targets, perhaps available using a context menu (in the world of Windows applications, this would be a right-click menu). This is literally a half-truth since an extended XLink could just as easily consist of multiple sources but only a single target.3
Another nice thing with extended XLink is that the links can be expressed out-of-line, that is, independently from the resources that participate in the actual link. Allow me to give a crude example:
If I say “Tokyo is the capital of Japan”, I’ve created a link (or rather, a relation) between the two. However, I’m not in Tokyo as I write this. I’m not even in Japan, which means that neither Tokyo nor Japan knows that I’ve created this particular relation (even though I suspect that some Japanese fellows are nevertheless aware of it anyway). I’ve expressed the whole relation out-of-line.
But herein lies a problem: Both my source and my target locations need to be uniquely identified, with unique names. In my little (admittedly mediocre) example, neither “Toyko” nor “Japan” are truly unique names even though most people will associate them with the same places that I do. But if they don’t, then they’ll probably not know it, and I, the creator of the link (relation), certainly won’t know it either. In other words, my link is really not expressed well enough. See “A word about existential issues and uniqueness” for more on this.
Now, getting back to our little cross-referencing example, if we want to express it using out-of-line extended XLinks, we can start out by identifying the source elements (and the target, or targets) using ID attribute values since the nice thing about them is that they at least ensure their uniqueness within a physical XML document. We can get by with something like:
<p><hlink id="source-1">Rabbits</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref id="source-2"/>. </xref-wrapper></p>
Of course, we need a bit more than that to ensure the uniqueness of the ID attribute values, but for now, this will do. I’ll get back to this whole uniqueness thing in “A word about existential issues and uniqueness”, though.
Note that the linking elements above no longer contain any explicit linking information; they’re merely elements that happen to have identifier attributes. They no longer know that they participate in a link. This relation we can now define in a separate, unrelated, XML document, like this (leaving out some XLink attributes that, although required, aren’t needed in this example):
<link xlink:type="extended"> <locator xlink:href="source-1" xlink:label="source" xlink:type="locator"/> <locator xlink:href="source-2" xlink:label="source" xlink:type="locator"/> <locator xlink:href="target" xlink:label="target" xlink:type="locator"/> <arc xlink:from="source" xlink:to="target" xlink:type="arc"/> </link>
This is a basic out-of-line, multi-ended link. What it says is that the two source identifiers, identified using locator elements, point to (have a relation with) the same target, identified with another locator element. The links use a level of abstraction by identifying the participating resources with labels in the arc element, instead of direct xlink:href addresses. This is a very useful quality with extended XLink, because it makes it possible to create classes of links, which is more or less exactly what we need in our example.4
For a programmer customizing an XML editor, it is fairly easy to implement something allowing such a link to be created, all at once instead of having to do the same link twice. The problem with such an implementation is simply a practical one: the application has no way of knowing which word or words in your document you intend to use as your hyperlink text. It can probably help you choose one, and insert the required markup, but it doesn’t know which to pick.
Obviously, you don’t have to implement XLink to use the basic single-source linking strategy outlined here, but it does help since a lot of other properties we need are already in place. Indeed, with the exception of the very neatness5 of the extended XLink solution with its multi-ended link, we could even do this in FrameMaker (+SGML).
The ID/IDREF approach has two things going for it: it guarantees that every ID attribute value used in every link target is a) unique, and b) the target exists. Leaving ID/IDREFs behind, we can guarantee neither. If we stick to links pointing to targets within the same physical file, we can fulfil a), but since b) is beyond us, a)’s usefulness is of limited value at best.
The very concept of single-source publishing will sooner or later lead to multiple files that together make up the information unit we want, however, so sooner or later, with enough file fragments and links pointing all over the place, ID values will clash and we’ll end up with a link that is no better than my “Toyko-Japan” example above. Robust methods of creating unique ID values are required.
Looking at ID attribute value creation from a practical point-of-view, we can safely say that the job shouldn’t be left to humans. As an example, my footnotes in this paper have the ID attribute values “ftnote-1”, “ftnote-2”, “ftnote-3”, and so on. What’s the probability that these ID attribute values are unique, even within this particular conference?
If you let a bunch of writers handle ID creation all by themselves, this is also about as unique as they get. In a big document management system, with ID uniqueness guaranteed by writers, we can forget all about linking, fragment reuse, and all that other stuff that was the document management system’s raison d’être to begin with.
Oh, and what about document names? Well, pretty much the same applies. A unique document needs a unique name. Once we start to break down XML documents in fragments, each of the fragments needs one. And if we keep at it for a while, there are going to be huge numbers of fragments, each of which absolutely requires a unique name so we can point to (or from) it, regardless if it’s for linking, or simply for locating and opening it in an editor.
So allow me to generalize what I’ve said a bit: Every participating resource [in a link] must be uniquely identified!
Does this guarantee that a link target exists? No, of course not. You can have a perfectly unique pointer to a non-existing target. Thus, in addition to unique resource names, we need to ensure that the resources exist, and continue doing it for as long as any link pointing at it exists. We don’t necessarily need to validate the link at once — at times, this may not even be possible6 — but there must be some way of ensuring that when used, the link is valid.
Say, this does imply the use of a document management system, doesn’t it?
What if we needed to profile our link, make it conditional? For example, what if we were writing vehicle service information, with the document applying to two different models, and we needed a link that pointed to one place if discussing the first, and to another if discussing the other model? I know of more than one writer that would gladly resort to the lazy solution:
For more information about the trimming options of the 1.8i engine, see Section 11.4 on Page 120. For more information about the trimming options of the 2.0T engine, see Section 11.5 on Page 128.
Which is fine, albeit wordy, if you’re discussing just two car models. but what if the document applied to a dozen of them, or twenty, or thirty? Single-source writing should take care of them all, without becoming needlessly wordy. Also, the very mentioning of specific models in text limits what could otherwise be constructed as applying to them all.
A couple of years ago, I and a colleague wrote extensive documentation for two DTDs we had created. One of them described a document collection, while the other focused on the individual documents in those collections. The DTDs shared a lot of elements and content models, obviously, but they were for very different purposes and, above all, for very different users, so they required their own documentation.
We saw the individual document DTD’s manuals as a subset of the collection DTD’s documentation so what we did was to mark up the differences with a role attribute, like this:
<section role="doc-coll"> <title>The doc-coll Element</title> ... </section> <section> <title>The doc Element</title> ... </section>
I’m sure you get the idea. A filter removed the document collection-specific sections to produce a document DTD-only manual. But we took the idea a bit further than that:
<p>This document is primarily intended for writers and editors using the &dtd_name; (Document Type Definition<wrap role="doc-coll">s</wrap>), but anyone wishing to acquire a greater knowledge of the DTD<wrap role="doc-coll">s</wrap> should find it useful. </p>
See how we used an entity instead of the DTD name? When producing a manual for the document collection DTD, the entity value was declared as doc and doc-coll DTDs, but when extracting the parts applying to the individual document DTD, it was instead declared as doc DTD. We had a number of other entities for this kind of thing, too; we quickly discovered that we needed to generalize the manual contents in some places.
Keeping in par with this approach, we also used an inline wrapper element (wrap) to rebuild sentences to suit the context. In the above example, it was used to go between the singular and plural forms of words and phrases, but using this method, we were able to do much more than that.
So, what does this have to do with profiling links? It’s very simple, really. What I wanted to show above is an approach for single-source publishing. It isn’t for the faint of heart, it requires a lot of discipline from the author, but it is a very simple way of generalizing, and at the same time helping to profile, content.
Going back to the problem of profiling links, let’s use our original single-source cross-referencing example and pretend that instead of just discussing rabbits, we want to include other rodents. In our first rewrite, the rodent we know we have to include is a rat, but we also know that subsequent editions might well want to discuss others. Giving it a first try, we might get something along the lines of7:
<p><hlink role="rabbit" xlink:href="id-rabbits">Rabbits</hlink> <hlink role="rat" xlink:href="id-rat">Rats</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref role="rabbit" xlink:href="id-rabbits/> <ref role="rat" xlink:href="id-rat"/>.</xref-wrapper></p>
Not particularly clever, is it? It’s clumsy, long, markup-heavy, and completely relies on the processing of the document where any unneeded information is removed. Also, if we were to talk about more than one kind of rodent at a time, the solution would fall apart.
An extended XLink solution is more attractive. After all, we are, in fact, discussing a kind of multi-ended links. Note that we’re using an entity to handle the hyperlink contents (that is, the applicable rodents) in question:
<p><hlink id="source-1">&rodents;</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref id="source-2"/>.</xref-wrapper></p>
The accompanying linkbase profiles the links:
<link xlink:type="extended"> <locator xlink:href="source-1" xlink:label="source" xlink:type="locator"/> <locator xlink:href="source-2" xlink:label="source" xlink:type="locator"/> <locator xlink:href="target-rat" xlink:label="target-rat" xlink:type="locator"/> <locator xlink:href="target-rabbit" xlink:label="target-rabbit" xlink:type="locator"/> <arc role="rat" xlink:from="source" xlink:to="target-rat" xlink:type="arc"/> <arc role="rabbit" xlink:from="source" xlink:to="target-rabbit" xlink:type="arc"/> </link>
Of course, we still need to process the links to set them in context, rats or rabbits,8 but this solution nevertheless represents a half-decent attempt at profiling. It’s still markup-heavy, though, and we are using extended out-of-line XLinks, requiring lots of processing outside the current document. What if we only had simple XLink, for one reason or another?
The XLink spec rather vaguely allows the definition of roles to link ends by using the xlink:role attribute. Originally, the role was designed as a descriptive property, a lot like the xlink:title attribute that is strictly intended for the human eye, but later revisions of the spec, as well as the final recommendation instead defined it as a URI. Unfortunately, the XLink recommendation specifically refrains from defining a processing model for XLinks (as opposed to, for example, XInclude), which means that one has to be defined for any practical application of it.9
So how can we use xlink:role? It is quite conceivable to give a link target a role and in that way profile it. For example, the role attribute could be used to identify a processing facility when publishing the link, that, depending on the publication format and media, and the currently applicable type of rodent, could process the link accordingly. The xlink:href attribute could in that case point out a general resource applicable to all possible rodents, while the xlink:role attribute would define the processing that is required in a particular context. Here’s a purely XLink variant of our example, using simple XLink emulating multi-ended links:
<p><hlink xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents">&rodents;</hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents"/>. </xref-wrapper></p>
Instead of relative URLs, as in the previous examples, I’ve chosen to identify the target (and its role) by using a URN. In other words, rather than using addresses, I’ve used names.
This, obviously, also requires some processing whenever the document is published, and can be a pain to present in the editing environment. It also requires a fairly complex XLink lookup mechanism. However, if the writing- and publishing-related rules are well established, and the writers are disciplined enough (and have the appropriate level of support from the authoring environment), it’s a clean and generic solution. It also has the advantage of leaving the specifics of link targeting in the proper context to the publishing process, allowing, for example, the publisher to increase the number of different rodents in subsequent editions of the document.
In this case, if more rodents were required in the document, simultaneously, the publishing process could, in addition to changing the general entity that handles the hyperlink contents, easily add the required paper-based references, plus any commas or other separators between them:
Rabbits and rats are outside the scope of this section. For detailed information about these lovable creatures, see Section 7, Rabbits, Page 91 and Section 8, Rats, Page 104.
Obviously, the kind of single-source authoring depicted here is rather easy to break, for example, by using the singular form in a sentence (“A rabbit is a lovable creature ...”), by referencing the rodents explicitly instead of using an entity (“Rabbits and rants” instead of &rodents;), and so on. In the end, however, this is what single-source publishing is all about — it’s about being able to customize content according to context without having to rewrite.
The use of general entities in “Purely XLink” is not necessarily a good idea. For one thing, entities require the presence of a DOCTYPE declaration, but they also limit the choice of tools because surprisingly many XSLT/DOM processors out there seem to have deficiences in entity support.
So let’s just leave them out, shall we?
<p><hlink xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents"></hlink> are outside the scope of this section.<xref-wrapper> For detailed information about these lovable creatures, see <ref xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents"/>. </xref-wrapper></p>
Here we assume that the same document management system that keeps track of the required rodents when publishing also inserts the required content in the hlink element. In practical terms, the xlink:role attribute is what does it since it’s already used for the purpose in the publishing process. The function’s fairly easy to implement, too, if you already have that other publishing functionality, but it’s a pain for the author since the hlink element is, in fact, empty. The least the system must be able to do is, therefore, to generate text in the XML editor to mark the spot and help the author visualize the context. The system can either provide the currently relevant list of rodents, or simply insert a generic standard text.
There are a variety of techniques to achieve something workable here. Processing intructions are one worth mentioning since it’s fairly easy to implement them as pseudo-elements where the user can enter content to make life easier during editing.
The processing instructions would, of course, be ignored by the publishing process.
There is an easier alternative, though. Since we’re risking more than just one or two types of rodents in a future version of the document, we should consider a switch mechanism in the DTD that allows us to turn off the hyperlink auto-generate feature and go back to writing the hyperlink text ourselves:
<p><hlink xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents" generate="no"> Most of the rodents mentioned here</hlink> are outside the scope of this section. <xref-wrapper>For detailed information about these lovable creatures, see <ref xlink:role="urn:x-rodents:r1:publishing#context" xlink:href="urn:x-rodents:r1:resources#rodents"/>. </xref-wrapper></p>
Note the generate attribute that leaves the hyperlink content to the writer. This will work, for as long as the hyperlink content isn’t rodent-specific, even though the example itself is almost unforgivably crude.
The lesson here is that this is essentially a style guide issue; you can’t rely on clever markup and implementation alone. By introducing automated hyperlink content generation, we could effectively be limiting, not expanding, the versatility of the system.
Remember the vehicle example I used to introduce this chapter with? That problem is from real life, from a well-known automobile manufacturer. Their solution is to use a document management system to handle XML fragments, links, images, and and so on. Everything is profiled according to context, and since there are surprisingly many variants of each vehicle model, the system must be able to handle a huge number of profiles. An author that modifies service information for a certain vehicle model and variant checks out the applicable resources (XML fragments, links, images, etc) in that context, but since at least some of these fragments apply to other profiles as well, the author must take great care to keep the writing generic enough, so nothing will break.
When documents (collections of fragments, actually) are checked out, the links describing the relations between resources are handled as inline extended XLinks, like this10:
<ref xlink:type="extended" xmlns:xlink="http://www.w3.org/1999/xlink" nevis:type="xref" xmlns:nevis="..." id="l1047052326016"> <locator xlink:type="locator" xlink:href="#l1047052326016" xlink:label="L0"/> <locator xlink:type="locator" xlink:href="urn:x-nevis:xref: 0800c8af80156736: 0900c8af80156738: 0000000000000000#include" xlink:label="L1"/> <arc xlink:type="arc" xlink:arcrole="urn:x-nevis:profile: 0b00c8af80206669" xlink:title="VCC-200181, Sittdynor" xlink:from="L0" xlink:to="L1" nevis:target-id="http://localhost:8080/ nevis/dialog/servlet/ XrefServlet?project_document= 0800c8af80156736+variant= 0900c8af80156738+resource= 0000000000000000+extSessionId= JOGR1049361968558+xml:lang=#include" nevis:qualification="qualified"/> </ref>
The inline links only exist when documents have been checked out; the links are handled very differently in the database, as link objects.11
Profiles, contained in the xlink:arcrole and nevis:... attribute values, are handled in groups. The URNs that handle profiles are created by the document management system, which is why the profiles aren’t human-readable. This example, however, demonstrates a real-life single-source document that utilizes the principles outlined in this whitepaper.
We’ve mainly discussed cross-references so far, but obviously, the authoring of good single-source documents requires other types of links. Fragment inclusions are perhaps the most important, but images may be just as important. Also, online versions of documents might want to embed features that simply cannot be made available in paper documents. For example, online versions of vehicle service documentation may require embedded software instruments such as Volt meters or oscilloscopes to make a diagnostics procedure truly interactive. Let’s have a look at some examples.
The inclusion of fragments may be achieved using a number of techniques. Entities is one; in SGML solutions, this was often used for the inclusion of everything from boilerplate texts to whole sections. Here’s a typical example of an external general entity, found in a DOCTYPE declaration:
<!ENTITY rabbits-bite SYSTEM "rabbits-bite.xml">
The entity would then be included in the document like this:
<p>It is important for your rabbit to regularly exercise.</p> &rabbits-bite; ...
The rabbits-bite.xml file might look like this:
<warning> <p>Rabbits are very cautious animals, easily threatened. When lifting up the animal from its cage, it might attempt to bite you if you move too quickly or lift it in the wrong way. </p> </warning>
The entity approach is a proven concept and works well in many circumstances. An advantage is that a validating XML processor must include the text and parse the resulting file.
If the rest of your DTD (or schema) uses XLink, the downside is that the entity mechanism is another linking mechanism, standardized as it may be. It is probably handled using different interfaces than the XLink cross-references, which almost invariably causes the application to be less user-friendly. Also, it can probably not share any software developed to handle various publishing requirements; it needs its own.
An XLink solution might look like this:
<p> ... </p> <ref xlink:type="simple" xlink:href="rabbits-bite.xml" zlink:show="embed"/> ...
The same mechanism is now used for both fragment inclusions and cross-references. A well-designed application can use the same code for any required customizations, of course, perhaps with some functionality added to handle inclusions. The downside is that there’s no built-in embedding or validation mechanism in XML for this. Therefore, a normalization process is required when publishing.
As for other advantages, using XLink offers great benefits when profiling fragment inclusions according to context (see “The problem revisited and expanded”); the same profiling mechanisms can be used for every type of link, provided that the same linking mechanism is used.
Images, like fragment inclusions, can be handled using entities, with a mechanism similar to fragment inclusions. Again, the downside is that this introduces an additional user interface, as well as additional customizing requirements (instead of simply using the XLink ones) if the cross-referencing system uses XLink.12
XLink, on the other hand, can handle images, too13:
<!ELEMENT graphic EMPTY> <!ATTLIST graphic xlink:type CDATA #FIXED "simple" xlink:href CDATA #IMPLIED xlink:show (embed|new|none) #IMPLIED xlink:actuate (onLoad|onRequest|none) #IMPLIED xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink" >
Several nice features are here offered by XLink. The xlink:show and xlink:actuate attribute values, for example, may be changed depending on publishing context. When writing user manuals for software, for example, a screenshot is hardly required for the online help version since you’ll probably have access to the real thing. And if XLink is used for images as well as fragment inclusions and cross-references, images can use the same profiling mechanisms (see “The problem revisited and expanded”) as the other links, resulting in both cheaper and more robust code.
Our rodents example is perhaps not the best for illustrating a profiling mechanism applicable to the types of links discussed, so let’s instead use the vehicle service information example from the beginning of “The problem revisited and expanded”. Let’s assume that vehicles need to be profiled according to model, model year, and engine variant, and that any of these properties may be used for any type of link. A declaration of the necessary attributes would in that case benefit from parameterization, as follows:
<!ENTITY % profiles.att model NMTOKENS #IMPLIED year NMTOKENS #IMPLIED variant NMTOKENS #IMPLIED >
This parameter would then be invoked by any linking element that needs it (leaving out the XLink attributes for clarity):
<!ATTLIST ref %xlink.ref.att; %profiles.att; > ... <!ATTLIST graphic %xlink.picture.att; %profiles.att; > ...
The ref element, in this case, would be used for both cross-references and fragment inclusions. The context decides what the element is used for — we don’t even need special attributes for this. If the ref element is found inline, within a wrapper (such as the xref-wrapper element), then it’s a cross-reference. If found on block level or above, it’s an inclusion.
Obviously, this is hardly a revolution in DTD development; parameterization remains a sound, and necessary, part of any reasonably complex DTD. The point is simply that a consistent DTD design with one linking system instead of many can lead to a simpler DTD, as well as less — and better — code.
It’s one thing to write DTDs that use the kind of linking strategies I’ve discussed above, but something else entirely to actually implement them. The vast majority of XML (and certainly SGML) environments I’ve seen lack any kind of support for creating links. As a writer, you’re expected to a) enter your own ID attribute values, and b) remember to put these values in corresponding referencing attributes elsewhere. It’s funny; had we been discussing Word or FrameMaker, no one would ever have accepted this, but in the wonderful world of markup, everybody seems to think it’s natural.
I don’t. It’s not that hard to build a linking tool that supports the user, and far from impossible to create an ID attribute value generation mechanism. “XLink dialog” is a screenshot of a dialog used for creating Simple XLink cross-references that we’ve implemented in a number of environments.
In its basic form, the XLink dialog and its underlying code isn’t that fancy. You pick the element you want to point to, and the dialog lists the caption or title of that element. The assumption is made that the element you want to point at is a container element of some kind (a section, a figure, a table, ...) and the caption or title of that container is either the first or the last child containing #PCDATA, making the lookup functionality much easier to implement. It’s something you want to keep in mind when writing DTDs, but the assumption holds true in a majority of cases anyway.
Vital here is that all environments we’ve built that use this XLink functionality also have an ID generation mechanism. The dialog will only display titles and captions of elements that have ID attribute values, so while there’s nothing to filter out the element types that aren’t allowed as link targets,14 if we only allow ID generation on elements that are allowed, we sort of get what we want anyway. This isn’t an ideal solution — there are a number of reasons to include ID attribute values in other places than just allowed link targets — but an element type filtering layer does introduce an extra level of complexity as well as add an unwanted DTD dependency or, at the very least, some additional configuration requirements.
It’s worth noting that this particular functionality was developed for smaller customers that cannot afford a heavy-duty document management system. They have everything they need stored on their file systems, and they rely very much on well-defined folder and subfolder structures to organize their documents and document fragments. A larger customer — for example, the one that paid for the extended XLink system I briefly outlined in “A real-life example” — with tens of thousands of reusable fragments and dozens of technical writers need more support from the user interface. The basic cross-referencing functionality, however, is almost identical to the one above; the difference is mainly that the extended XLink system also includes functionality (in the GUI, buttons that open subdialogs from the main XLink dialog) for profiling the links in the way I’ve described. And, of course, that most targets are handled as URNs and that there’s a document management system to keep track of the them all, allowing for far superior reuse facilities.
The strategies I’ve described in this paper force an often radically different approach to writing than would a conventional system. The writer needs to be constantly aware of the fact that her material will be published in a variety of contexts and on a variety of media. To succeed, it is vitally important to not limit the implementation to tools and user interfaces alone; a style guide for writers is essential and must be provided. A DTD without a style guide is of very limited value. Training is also important, as is an editorial function that ensures that all produced documents follow the applicable writing guidelines.
When we implemented the real-life system described in “A real-life example”, creating single-source cross-references as outlined here was one of the most difficult issues to grasp for writers and programmers alike. For example, during the project, I discovered that there were several mistakes in both implementation and in writing style that had to be corrected. Here’s a markup example I found (I’ve left out a lot of markup for clarity):
<p>For more information, <xref><ref/></xref>.</p>
Now what’s this? Well, apparently, no one could figure out why the xref wrapper was required in the first place, so writers simply contained the linking element (ref) directly in the wrapper. The stylesheet designers didn’t really get the point either; what one got when printing the above was this:
For more information, see Chapter 2 on Page 14.
In other words, not only did the wrapper remain essentially unused (the writers thought it a nuisance to have to use two elements to create a link), but the link itself generated both the target information (“Chapter 2 on Page 14”) and a verb, “see”. The verb is a no-no in generated text in almost any system; the idea falls apart as soon as you need to write your linking sentence in any other way than the above. But perhaps more importantly, it also breaks because the text flow of the translated documents cannot be expected to follow the same construct as seen here. In other words, the verb “see” cannot always be expected to be located next to the link or even follow the same grammatical rules. At best, this approach requires a lot more work to make a foreign-language stylesheets to work; at worst, it just cannot be done.
This example may seem trivial but resulted in several difficulties:
Luckily, we were able to correct these problems in time, before the erroneous markup was able to propagate through the system.
This example serves well to illustrate the practical difficulties in implementing a single-source linking strategy. The task is not trivial and must not be underestimated. So, what can be done to avoid this kind of pitfalls?
This single-source linking thing is really not that difficult. There are some basic rules that must be followed, however, if the results are to be worth the trouble.
Or downright disaster; take your pick.
Actually, any CDATA-based linking solves this problem.
It’s a one-fourth truth, actually, since you can have multiple targets and multiple sources, in addition to the having either multiple sources or multiple targets. And, of course, one source and one target is another alternative.
A “class”, in this case, is a link with two sources; both source locators are aliased using the same label, “source”. This label is then used to identify the “from” link end in the arc element that describes the link itself.
“Neatness” Is there such a word?
Getting back again to our basic single-source cross-reference example, it is quite conceivable that if the online and paper versions of the link point to different targets, they may not both be available for validation at the same time, in the same context; we’ll get back to this in the next section.
I’ve taken the liberty of removing some attributes required by the XLink spec. Hope you don’t mind.
Which is why there’s a role attribute in the arc elements.
This lack of a defined processing model is also often seen as an advantage since it does not unnecessarily limit any possible implementations. Consequently, the recommendation must, therefore, always be interpreted, and a processing model has to be defined. This means that while any conforming XLink implementation should be able to parse syntactically correct XLinks, they may not always behave in the way they were intended to.
I’ve simplified the markup somewhat, since the real thing always contains various processing attributes that really aren’t relevant for our example.
Link objects are the document management system’s way of expressing relations (in other words, links) between the various file resources in the database underneath. It’s an abstraction layer that enables the document management system to treat links in the same way as it treats file resources (XML files, images, etc). Consequently, the file resources are treated as file objects by the system. Both are objects to which identical sets of properties can be applied to.
And even if the cross-referencing system was your old trusty ID/IDREF, (at least) two sets of customizations would still be needed.
I’ve deliberately left out some of the XLink features, for example, xlink:show and xlink:actuate.
You can pick any element allowed by the DTD; that’s how this particular dialog works. At the time, it was the easiest way of implementing the functionality.
My deepest thanks must go to my dear friend and colleague, Henrik Mårtensson, who first suggested the use of an xref wrapper to identify the whole semantic link, instead of just the reference. He’s also a Desperate Perl Hacker, and the one I always turn to first to discuss my ideas.
Thanks also to my friends and colleagues at Sörman Information & Media AB.