Element type specialization hierarchy declaration (the @class attribute)

Each DITA element declares its specialization hierarchy as the value of the @class attribute. The @class attribute usually provides a mapping from the element's current name to its more general equivalents, but it can also provide a mapping from the current name to more general and more specialized equivalents. All specialization-aware processing can be defined in terms of @class attribute values without reference to a given element's tagname.

Specialization hierarchy declaration requirements

Values for the @class attribute must conform to the following syntax requirements:

  • An initial "-" or "+" character followed by one or more spaces, "-" for element types defined in structural vocabulary modules, "+" for element types defined in domain modules.
  • A sequence of one or more module/type pair tokens of the form "modulename/typename", with each pair of tokens separated by one or more spaces, where modulename is the short name of the vocabulary module and typename is the element type name. Tokens are ordered left to right from most general to most specialized.
  • At least one trailing space character (" "). The trailing space ensures that string matches on module/name pairs can always include a leading and trailing space in order to reliably match full tokens.

When the @class attribute is declared in a DTD or XSD, it must be declared with a default value. In order to support generalization round-tripping (generalizing specialized content into a generic form and then returning it to the specialized form) the default value must not be fixed. This allows a generalization process to overwrite the default values defined by a general document type with specialized values taken from the document being generalized.

When a vocabulary module declares new element types, it must provide a @class attribute for each element type that it declares. The @class attribute must include a mapping for every structural type or domain in the specialized type's ancestry, even those in which no element renaming occurred. The mapping must start with the value for the base type (for example topic or map), and finish with the current element type.

A vocabulary module must not change the @class attribute for elements that it does not specialize, but simply reuses by reference from more generic levels. For example, since task, bctask, and guitask use the <p> element without specializing it, they must not declare mappings for it.

The @class attribute should not be modified by authors.

Examples (non-normative)

The @class attribute for the task topic type's <step> element is:

<!ATTLIST step         class  CDATA "- topic/li task/step ">

This tells us that the <step> element is equivalent to the <li> element in a generic topic. It also tells us that <step> is equivalent to a <step> in a task topic, which we already knew, but it's worth noting this in the attribute because it enables round-trip migration between upper level and lower level types without loss of information.

While a given element's tagname is normally the same as the typename of the last token in the @class value, this is not required. Processors that perform generalization may transform elements from specialized types to less-specialized types, leaving the values of the @class attribute unchanged (thus preserving knowledge of the original most-specialized form). For example, if a user runs a generalizing transformation that maps all elements to their first @class value, but preserves their content and attribute values, then the user can follow it up with a "specialize" transformation that maps all elements to their last @class value (preserving content and attribute values), and provide a full round trip for all content between the two document types, using nothing but two generic transformations and the information in the @class attribute.

The @class attribute tells a processor what general classes of elements the current element belongs to. DITA scopes elements by module type (for example topic type, domain type, or map type) instead of document type, which lets document type developers combine multiple topic types in a single document without complicating transformation logic.

The sequence of values in the @class attribute is important because it tells processors which value is the most general and which is most specific. This is especially important for "specializing" transformations, where you can apply a general rule that says: if the element doesn't have a mapping to the target topic type, simply use the last value of the @class attribute (and assume that the specialized topic type is reusing some general element declarations, which only have mappings for the level at which they were declared).

Example of structural type element with @class attribute

<appstep class="- topic/li task/step bctask/appstep ">
  <cmd class="- topic/ph task/cmd ">A specialized step</cmd>
</appstep>

Example of domain element with @class attribute

<wintitle class="+ topic/keyword ui-d/wintitle ">A specialized keyword</wintitle>

While this example is trivial, more complicated hierarchies (say, five levels deep, with renaming occurring at levels two and four only) make explicit intermediate values essential.

The specialization hierarchy for a given element type must reflect any intermediate modules between the base type and the specialization type, as shown in this example:

Example of @class attribute with intermediate value

<windowname class="- topic/keyword task/keyword guitask/windowname ">

The intermediate values are necessary so that generalizing and specializing transformations can map values simply and accurately. For example, if task/keyword was missing as a value, and a user decided to generalize this guitask up to a task topic, then the transformation would have to guess whether to map to keyword (appropriate if task is more general than guitask, which it is) or leave it as windowname (appropriate if task were more specialized, which it isn't). By always providing mappings for more general values, processors can then apply the simple rule that missing mappings must by default be to more specialized values than the one we are generalizing to, which means the last value in the list is appropriate. For example, when generalizing <guitask> to <task>, if a <p> element has no target value for <task>, we can safely assume that <p> does not specialize from <task> and should not be generalized.

Was this helpful?