DITA 1.3 proposed feature #13004

Proposal to provide a mechanism for different effective key definitions at different locations within a map structure.

Date and version information

Include the following information:
  • The latest revision date for this proposal is 2012 January 18
  • This proposal was introduced by Jeff Ogden before being passed to Chris Nitchie.
  • This proposal was initially accepted for inclusion into DITA 1.3 on 2011 June 21: Minutes from 2011 June 21.
  • Additional discussions of this proposal as a partial mechanism for variable text in topics here.

Original requirement or use case

Future post DITA 1.2 work from Issue #12007: Item 2: Consider adding a scoping mechanism for use with key definitions and possibly other aspects of DITA based on the map hierarchy or the combined element hierarchy within a set of maps. Related topic: Consider allowing subsequent key definitions within the same map or within peer maps to override previous key definitions, probably as part of some hierarchal scope, and probably in a fashion that is the same as or similar to override mechanisms to be developed for other features such as ditaval.

Use cases

The DITA 1.2 specification dictates that there be exactly one effective definition for a given key for all key references within a map structure (see “Effective key definitions” here). This is a severe limitation that prohibits some common use-cases, such as:
  • Omnibus publications that combine multiple standalone maps, each with their own set of key definitions.

    • In some cases, it will be necessary for one 'sub-publication' in an omnibus publication to refer to a location in another 'sub-publication.' Thus, there must be some way to reference a key that is defined in another location's key space.
  • Cases where a topic is reused at multiple locations in a map structure, but the behavior of links and/or text replacement for each use context must be different.

Proposed solution

In DITA 1.2, a given root map in a map structure defines a single key space, with exactly one effective definition for every key name. This poposal would allow a map author to place a virtual "fence" around a section of the map, and say, essentially, "keys defined in this section of the map structure, stay in (can only be referenced from) this section of the map structure." Thus for a given key, there could be different effective definitions at different locations - inside different "fences" - within the map structure. These "fences" are called key scopes, and they are defined by putting a @keyscope attribute on a map or topicref element. Key definitions within a scope are called scoped key definitions.

There are cases where a topic or map in one scope needs to reference a piece of content from a different scope. For example, an omnibus piece of technical documentation where each section applies to a single component of the product, and is mostly self-contained, but occasionally needs to link to something in another section. For this reason, the @keyscope attribute specifies a name that can be prepended to key references to address keys within that scope. For example, you could reference a key "k" in a scope "s" from outside that scope via the key reference, "s.k". This is called a scope-qualified key name. If a given definition is within multiple layers of scopes - a fence within a fence - you would specify the full "scope path" to the key, e.g. keyref="outerScope.innerScope.keyName".

The detailed requirements for key reference processing in a map containing key scopes are as follows.

The content considered to be part of a key scope is as follows:

The root element of the root map in a map structure always defines an implicit root key scope. There are no other implicit key scopes. For example, no key scope is introduced by referencing a child DITA map; the content brought in by such a reference is part of the same scope as the map reference.

The value of the @keyscope attribute is used when generating implicit scope-qualified key names for the parent scope. As such, it must also be a legal key name. Scope names should be unique within a given parent scope. If there are duplicates, conflicting qualified key names may - and likely will - result. However, unqualified references within those scopes will function as expected, resolving to the definition in the local scope. For purposes of implicit qualified key names, the first key scope within a given parent scope with a given name takes precedence.

Benefits

Technical requirements

DTD and Schema modifications
Topic or map specialization
None.
Domain
None.
Element
No new elements.
Attributes
  • keyscope

    • New NMTOKEN attribute for topicref-atts group and its variants.

    • Processors will be required to process key definitions and key references within elements marked with this attribute according to the behavior described above.

    • Does not contain translatable text.

Processing impact
See above.
Usability impact
Key scopes are simple to specify. However, when a map utilizes this feature, the effective definition for a given key reference will now depend not only on the map that contains the reference, but the location within that map where the reference occurs. So identifying the applicable definition for a given key reference may be considerably more difficult in some cases, both for users and for tools.

Costs

Examples

Combination of Standalone Maps

This map combines three standalone course maps, each with its own set of keys. Each of the mapref elements is marked with a keyscope attribute, meaning that the key definitions within the referenced map sub-structures will be confined to those sub-structures.

Since the summary key is defined in this map as part of the implicit root scope, and keys defined in parent scopes override those in child scopes, its definition overrides any conflicting definition of summary in any of the referenced maps.

<map xml:lang="en">
  <title>Training Courses</title>
  <mapref href="course-1.ditamap" keyscope="course-1"/>
  <mapref href="course-2.ditamap" keyscope="course-2"/>
  <mapref href="course-3.ditamap" keyscope="course-3"/>
  <topicref keys="summary" href="omnibus-summary.dita"/>
</map>

Without key scopes, the key names that have definitions for all three maps, like introduction or prerequisites, would take their effective definition from course-1.ditamap, resulting in incorrect behavior in the output.

Reuse of the Same Topic with Different Key Behavior

In this example, the “oil change” procedures for Tractor X and Tractor Y are similar enough to reuse the same topic for both, but the name of the product is included in the title via keyref. In the map below, the topic common/oilchange.dita is referenced twice. Each reference is wrapped within a topicgroup that defines a key scope, and each scope has its own definition of the ProductName key.

<map xml:lang="en">
  <title>Equipment Oil Change Procedures</title>

  <topicgroup keyscope="Tractor-X">
    <keydef keys="ProductName">
      <topicmeta>
        <linktext>Tractor X</linktext>
      </topicmeta>
    </keydef>
    <topicref href="common/oilchange.dita"/>
  </topicgroup>

  <topicgroup keyscope="Tractor-Y">
    <keydef keys="ProductName">
      <topicmeta>
        <linktext>Tractor Y</linktext>
      </topicmeta>
    </keydef>
    <topicref href="common/oilchange.dita"/>
  </topicgroup>

</map>
<task id="oilchange" xml:lang="en">
  <title>Changing the Oil on the <ph keyref="ProductName"/></title>
  <!-- etc. -->
</task>

An alternative way to code the above map would be to specify @keyscope on each of the topicref elements, and next the keydef elements within them. This is a somewhat less intuitive but equally valid formulation.

<map xml:lang="en">
  <title>Equipment Oil Change Procedures</title>

  <topicref href="common/oilchange.dita" keyscope="Tractor-X">
    <keydef keys="ProductName">
      <topicmeta>
        <linktext>Tractor X</linktext>
      </topicmeta>
    </keydef>
  </topicref>

  <topicref href="common/oilchange.dita" keyscope="Tractor-Y">
    <keydef keys="ProductName">
      <topicmeta>
        <linktext>Tractor Y</linktext>
      </topicmeta>
    </keydef>
  </topicref>

</map>

Without key scopes, the effective text for both references would be “Tractor X,” making this an impossible scenario in DITA 1.2.

Key Definition Containment

This example illustrates how key definitions within a scope do not apply outside that scope. Here, key-1 is defined within the key scope scope-1. Since the reference to key-1 is outside that scope, the reference is treated as an undefined key.

<map xml:lang="en">
  <title>Key Definition Containment Example</title>

  <topicgroup keyscope="scope-1">
    <keydef keys="key-1" href="someTopic.dita" />
  </topicgroup>
  
  <!-- Processors should issue a warning for an undefined key
       since the reference occurs in the root scope, and the
       root scope does not contain a definition for key-1. -->
  <topicref keyref="key-1" />

  <!-- However, this reference is OK. -->
  <topicref keyref="scope-1.key-1" />
</map>

Cross-Scope Linking

Take the following map:

<map>
  <topicgroup keyscope="scope-1">
    <topicref keys="scope-1-key-1" href="Topic1.dita"/>
  </topicgroup>
  <topicgroup keyscope="scope-2">
    <topicref keys="scope-2-key-1" href="Topic2.dita"/>
  </topicgroup>
</map>

This map contains two scopes, each with one topic. If you wanted to author a link from Topic 1 to Topic 2, you could do so by creating a reference to the qualified key for Topic 2.

<p>Link to <xref keyref="scope-2.scope-2-key-1">Topic 2</xref>.</p>

Precedence Within a Scope and Qualified Key Names

In the following map, the mapref specifies a keyscope, and then contains several key definitions. Since key precedence in a scope follows DITA 1.2 rules, the key definitions in this map take precedence over key definitions in the sub-map, and earlier key definitions within the scope take precedence over later ones.

<map id="map-1" xml:lang="en">
  <title>Outer Map</title>
  <topicref format="ditamap" href="submap.ditamap" keyscope="scope-1">
    <keydef id="keydef-1" keys="key-1"/>
    <keydef id="keydef-2" keys="key-2"/>
  </topicref>
  <keydef id="keydef-3" keys="key-1"/>
</map>
<map id="submap" xml:lang="en">
  <title>Sub-Map</title>
  <keydef id="keydef-4" keys="key-1"/>
  <keydef id="keydef-5" keys="key-2"/>
</map>

Here are the effective key definitions for the scopes in this map structure.

  • Implicit root scope

    key-1
    keydef-3, since the other definitions are confined within scope-1.
    key-2
    Undefined, since all definitions for this key occur within the boundaries of scope-1
    scope-1.key-1
    keydef-1. Since key definition precedence within a scope occurs according to the same rules used in DITA 1.2, and keydef-1 happens shallower in the map structure of scope-1 than keydef-4, keydef-1 takes precedence.
    scope-1.key-2
    keydef-2, which overrides keydef-5 for the same reason.
  • scope-1

    key-1
    keydef-3, since this key definition is in the root scope, and thus takes precedence over any definitions in child scopes.
    key-2
    keydef-2.
    scope-1.key-1
    keydef-1, inherited from the parent scope.
    scope-1.key-2
    keydef-2, inherited from the parent scope.

Explicitly Defining Qualified Names

If a parent scope explicitly defines a qualified key name that would otherwise be implictly defined by virtue of a child scope, the explicit definition takes precedence.

<map>
  <title>Explicit Key Definition Example</title>

  <keydef keys="child.keyName" id="keydef1" />

  <topicgroup keyscope="child">
    <keydef id="keydef2" keys="keyName" />

    <topicref id="topicref1" keyref="keyName" />
    <topicref id="topicref2" keyref="child.keyName" />
  </topcgroup>

  <topicref id="topicref3" keyref="keyName" />
  <topicref id="topicref4" keyref="child.keyName" />
</map>
 

The effective definition for the different topicrefs are:

topicref1
keydef2, because it is an unqualified reference to the locally-scoped key.
topicref2
keydef1, because that definition is inherited from the parent scope, and the parent scope's explicit definition takes precedence over the implicit definition derived from the scope.
topicref3
Undefined, because the unqualified definition for keyName is contained within the scope.
topicref4
keydef1, since the explicit definition overrides the implicit definition derived from the scope.

Multiple Key Scope Names

Take child map, child.ditamap:

<map keyscope="mapScopeName1 mapScopeName2">
  <keydef keys="key-1" />
</map>

And this parent.ditamap:

<map>
  <title>Publication Title</title>
  <mapref keyscope="maprefScopeName1 maprefScopeName2"
          href="child.ditamap" />
  <topicref keyref="mapScopeName1.key-1" />
  <topicref keyref="mapScopeName2.key-1" />
  <topicref keyref="maprefScopeName1.key-1" />
  <topicref keyref="maprefScopeName2.key-1" />
</map>

All references will resolve because the child scope has four names.