Variables Domain

The variables domain provides markup for declaring and referencing variables that are scoped by the map and topic hierarchy.

Variables are declared in map metadata, topicref metadata, topic prologs, and topic content. Variable definitions are specializations of <data> and may occur wherever <data> is allowed.

References may occur anywhere that <keyword> or <text> is allowed.

Variable resolution is done at output processing time, meaning the last step in a normal DITA process. This means that all content references and all filtering should have been applied by the time variable references and definitions are processed. This means you can use normal content reference and filtering facilities to determine the effective values of variable definitions and variable references.

Note that as of D4P version 0.9.19 the Variables mechanism does not attempt to cover all use cases. It is intentionally simple so as to make implementation easy and foster use and experimentation in order to gather futher requirements. A complete variables facility, such as would be appropriate for inclusion in the DITA specification, could be quite a bit more complicated.

Variable Definitions: <d4p-variable-definition> and <d4p-variable-definitions>

Variable definitions are defined using the <d4p-variable-definition> element and always contained by the <d4p-variable-definitions> element:
      <d4p-variable-definition name="prodname"

The @name attribute is the name of the variable, by which it is referenced using <d4p-variableref-keyword> or <d4p-variableref-text>. The name may be any string. Because it is defined in an XML attribute, any whitespace in the value will be normalized to single space characters. This allows you to use normal content as a variable name if appropriate, e.g. <d4p-variable-definition name="default value for variable">... where that variable name could be appropriate as the default or fallback value for the variable, as the variable reference will simply be rendered as-is when there is no declaration for variable in the referencing scope.

The variable value may be specified either using the @value attribute or in the content of the <d4p-variable-definition> element. If @value is specified then the content of the element is ignored. This allows you to use the content of the variable definition as documentation for the variable if you choose.

If you put the variable value in the content then you may use any markup allowed where the variable reference is allowed (or, more accurately, where the content can be usefully and correctly processed, which is not quite the same thing).

Note that variable references are not content references and so the same strict content model consistency constraints do not necessarily apply. However, you should be careful to only put markup in variables that will be correctly and appropriately processed in the context where the variables are referenced. In practice, this means limiting your content to normal inline markup and avoiding things like footnotes, cross references, block elements, and so on. If you want to impose more constraints on what is allowed in variable definitions, you can use constraint modules or further specialization to do so. The base markup as defined by the D4P project errs on the side of generality.

The <d4p-variable-definitions> element serves to group and label sets of variable definitions. The element may have an initial <title> element, which serves as a descriptive label for the group, and <d4p-variable-definitions> may be nested:
      <title>Top-level variable definitions</title>
        <title>Nested variable definitions element</title>
          name="varFallbackDefinedInRootTopic">This is the fallback value defined
          in the root topic</d4p-variable-definition-fallback>

When resolving variables, the first definition within a given scope (containing element) in document order wins, so in the case of nested <d4p-variable-definitions> elements, the nesting is not significant, only the order of the definitions in normal depth-first tree order. This means that a definition contained by the top-level variable definitions group but occurring after all nested variable definition groups could be overridden by a definition within any of the nested groups. You can take advantage of this behavior to have "default" values that may be overridden by nested groups used by reference.

Fallback values: <d4p-variable-definition-fallback>

For topics that may used in many contexts, you often want to have fallback values for the variables referenced from those topics so that when those variables are not declared at all, an appropriate value is used, such as "variable XXX not defined" or an appropriate default value, or whatever. Fallback values are used only when there is no other in-scope declaration of the variable. That is, you don't want the fallback value to override attempts to set the variable.

Fallback values are declared using the <d4p-variable-definition-fallback> element, normally within topic prologs, although you can declare fallback values within any scope, just as for other variables.

Fallback values are used only when there is no other in-scope definition of the variable. This includes definitions in the same scope as the fallback, meaning that if you have both a fallback and normal definition of the same variable name in the same scope, the fallback will never be used. You might have this case if you have a set of fallback values that are reused by conref and you happen, in one use context, to also define a value for the variable. In that case, the existence of the fallback will not interfere with the other definition of the variable.

Variable References: <d4p-variableref_keyword> and <d4p-variableref_text>

Variables may be referenced in any context where <keyword> or <text> are allowed, using the corresponding specialization, <d4p-variableref_keyword> or <d4p-variableref_text>.

The content of the reference element is the variable name:
<p>The <d4p-variableref_keyword>prodname</d4p-variableref_keyword> 

If the variable cannot be resolved (or you don't have processing support for D4P variables installed), then the content of the reference element is used, just as for normal <keyword> or <text> processing. If the variable can be resolved, then the nearest variable definition is used to determine the effective value.

The effective binding for a variable is determined as follows:
  1. In all cases, only the direct element and map tree ancestry of the reference is considered, with the exception of topic prologs, which always appy to the entire topic (including the topic title). Definitions in sibling topics, maps, or topicrefs are never considered for the purpose of determining the effective definition for a given reference.
  2. The nearest definition within ancestor elements determines the effective binding. For elements within topics, this includes any ancestry within the topic body as well as definitions within the topic's prolog. Note that the placement of the definition within the ancestor is not important—in particular, the variable definition could come after the subelement that contains the reference.
  3. For topics referenced from maps, if there is no definition within the topic, then the nearest definition in the map tree ancestry determines the effective binding. That is, definitions are scoped by topicrefs such that the definition in the nearest topicref to the topic that references the topic provides the binding.
  4. If the variable is not defined within the topic or within any topicref in the map tree ancestry, then any definition within the map's metadata is used. This means that root maps may define default values for variables.
  5. If a variable is not defined anywhere, processors may choose to provide default values or otherwise allow for run-time definition of variables.

Stated more informally, the intent of these rules is that elements define variable scopes within topics and topicrefs define scopes within maps.

Note: Within topics, only references defined within topic documents are considered—topics related into a hierarchy via maps do not affect the variable definitions within each other, meaning that variable resolution processors do not need to look in map-defined ancestor topics for variable definitions, only literal ancestor topics within the same XML document. However, if topics are merged because of normal DITA @chunk processing or via some type of topic merging, such as is done for the Toolkit's PDF processing, then previously-separate XML documents may become single XML documents and thus affect variable resolution. However, this would normally only affect fallback values such that a variable reference for which there was no fallback (because none was defined in the topic's direct XML ancestry) would now have a fallback or a defined value, because one was defined in an ancestor in the merged document. Because the nearest definition to a reference is the always used, it is not possible for chunking and merging to affect the values of variables defined in the original XML ancestry.