OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

dita message

[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]


Subject: [dita] Scoped Keys Questions from Tom Magliery


As I mentioned during this morning's TC call, Tom Magliery reached out to
me last week with some questions from his team on some of the nuances of
scoped keys. I've restructured things so that the questions and the
answers are together, and put it in the attached text file (when I paste
it into Outlook, I lose the indenting).

Chris

Scoped Keys
-----------

Some notes on the format I'Ve used to describe key scope structures
below:

* An entry with a leading dash denotes a key scope boundary.
* An entry in parentheses is an implicit qualified key derived from a
child scope.
* An unadorned line is an explicit key definition.
* Overridden key definitions are omitted.
* Comments are wrapped in square brackets.

-----------------------------------------------------------
<!--
Sample - 1
	- What are the effective keys in ks_0 in map-0.ditamap?
-->
<!-- map-0.ditamap -->
<map>
  <topicgroup keyscope="ks_0">
    <keydef keys="key_a" href="a.dita" />
    <keydef keys="key_b" href="b.dita" />
  </topicgroup>
  <mapref href="map-1.ditamap" />
  <topicgroup keyscope="ks_0">
    <keydef keys="key_a" href="a.dita" />
    <keydef keys="key_c" href="c.dita" />
  </topicgroup>
</map>

<!-- map-1.ditamap -->
<map>
  <topicgroup keyscope="ks_0">
    <keydef keys="key_b" href="b.dita" />
    <keydef keys="key_c" href="c.dita" />
  </topicgroup>
</map>


There is no such thing as a non-contiguous key scope. The names only
come
into play when determining the qualified key names the parent scope can
use to address the keys in that scope. Here, we have three completely
distinct key scopes which just happen to share the name ks_0. I'Ll refer
to them as ks_0a (the first one in map-0), ks_0b (the second one in
map-0), and ks_0c (the one in map-1). The root map also introduces an
implicit scope, so there are four scopes in all.

The keys key_a, key_b, and key_c are very straightforward to determine
for
all three non-root scopes.

- ks_0a
    key_a=a.dita
    key_b=b.dita
- ks_0b
    key_a=a.dita
    key_c=c.dita
- ks_0c
    key_b=b.dita
    key_c=c.dita

The unqualified key names are scoped - fenced off - in their respective
scopes.

Determining the qualified key bindings requires a little more thought,
but
not much. Because these three scopes share the same name, a level of
ambiguity is introduced. Here's how to solve the ambiguation.

A key space is determined via a breadth-first traversal of the map tree
within a scope (and the root map introduces the 'root scope', so we're
really talking about four scopes in all, here). A breadth-first
traversal
of the root map structure would encounter ks_0a first, then ks_0b, then
ks_0c. For purposes of precedence, the qualified key definitions
provided
by a scope boundary are considered to occur at that scope boundary. So:

ks_0.key_a=a.dita (from ks_0a)
ks_0.key_b=b.dita (from ks_0a)
ks_0.key_c=c.dita (from ks_0b)

ks_0a doesn't contain key_c, so no ks_0.key_c is contributed to the root
scope for ks_0a. The definition for ks_0.key_a provided by ks_0b takes
lower precedence than the one provided by ks_0a, because ks_0a occurs
first. However, this is the first definition for key_c, so ks_0.key_c is
provided by ks_0b. Next, the breadth-first traversal encounters ks_0c.
Since all of its qualified key definitions have been provided by the
other two, shallower scopes, none of its qualified definitions take
precedence. However, if ks_0c introduced key_d, then that would be
contributed to the root scope as ks_0.key_d.

Finally, since a child scope inherits all of the key definitions from
its parent scope, including qualified key names provided by itself
and its sibling scopes, all thre ks_0 instances inherit those three
qualified definitions.

So the final tally is:

- Root scope
    (ks_0.key_a=a.dita) (from ks_0a)
    (ks_0.key_b=b.dita) (from ks_0a)
    (ks_0.key_c=c.dita) (from ks_0b)
    - ks_0a
        [all keys from root]
        key_a=a.dita (local definition)
        key_b=b.dita (local definition)
    - ks_0b
        [all keys from root]
        key_a=a.dita (local definition)
        key_c=c.dita (local definition)
    - ks_0c
        [all keys from root]
        key_b=b.dita (local definition)
        key_c=c.dita (local definition)

-----------------------------------------------------------

<!--
Sample - 2
	- Is the key scope hierarchy valid?
	- What are the <xref> targets in t_000.dita?
	- What are the <xref> targets in t_00.dita?
	- What are the <xref> targets in t_0.dita?
-->
<map>
  <topicgroup keyscope="ks_0">
	<keydef keys="key_a" href="a.dita" />

	<topicgroup keyscope="ks_0">
		<keydef keys="key_a" href="aa.dita" />
		<topicref href="t_00.dita"/>

		<topicgroup keyscope="ks_0">
			<keydef keys="key_a" href="aaa.dita" />
			<topicref href="t_000.dita"/>
		</topicgroup>
	</topicgroup>
  </topicgroup>
  <topicref href="t_0.dita"/>
</map>

<!-- t_000.dita -->
<task>
  <xref keyref="key_a"/>
  <xref keyref="ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.ks_0.key_a"/>
</task>

<!-- t_00.dita -->
<task>
  <xref keyref="key_a"/>
  <xref keyref="ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.ks_0.key_a"/>
</task>

<!-- t_0.dita -->
<task>
  <xref keyref="key_a"/>
  <xref keyref="ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.key_a"/>
  <xref keyref="ks_0.ks_0.ks_0.key_a"/>
</task>


Again, a scope inherits all key definitions from its parent scope. And
again, we have different scopes with the same name, but instead of being
siblings, they're nested inside one another. The key space structure is:

- Root scope
    (ks_0.key_a=a.dita)
    (ks_0.ks_0.key_a=aa.dita)
    (ks_0.ks_0.ks_0.key_a=aaa.dita)
    - ks_0
        [all keys from the root scope; ks_0.key_a and ks_0.ks_0.key_a overridden]
        key_a=a.dita
        - ks_0
            [all keys from parent; key_a and ks_0.key_a overridden]
            - ks_0
                [all keys from parent; key_a overridden]

Thus, t_00.dita and t_000.dita, occurring in ks_0.ks_0 and
ks_0.ks_0.ks_0 respectively, have the same effective key
definitions:

<task>
  <xref keyref="key_a" href="a.dita"/>
  <xref keyref="ks_0.key_a" href="a.dita"/>
  <xref keyref="ks_0.ks_0.key_a" href="aa.dita"/>
  <xref keyref="ks_0.ks_0.ks_0.key_a" href="aaa.dita"/>
</task>

However, t_0.dita is at the root scope, which has no definition for an
unqualified key_a, so:

<task>
<xref keyref="key_a"/> <!-- no definition -->
<xref keyref="ks_0.key_a" href="a.dita"/>
<xref keyref="ks_0.ks_0.key_a" href="aa.dita"/>
<xref keyref="ks_0.ks_0.ks_0.key_a" href="aaa.dita"/>
</task>

(Side note: one potential algorithm is to implement a key scope such
that it delegates all resolution requests to its parent, and only
consults its local definitions if the parent's resolution attempt
fails or if there is no parent, i.e. the root scope. This is the
algorithm I've been using when I test out these concepts.)

-----------------------------------------------------------

<!--
Sample - 3
	- What is the <xref> target in t_000.dita?
-->
<map>
  <topicgroup keyscope="ks_0">
    <keydef keys="key_a" href="aa.dita" />
	  <topicgroup keyscope="ks_1">
		<keydef keys="key_a" href="aaa.dita" />
		<topicref href="t_000.dita"/>
	  </topicgroup>
  </topicgroup>
  <topicref href="t_0.dita"/>
  <keydef keys="key_a" href="a.dita" />
</map>

<!-- t_000.dita -->
<task>
  <xref keyref="ks_0.key_a"/>
</task>


The root scope gets its definition for ks_0.key_a via the key_a
definition in scope ks_0. This definition is inherited by all
child, grandchild, etc. scopes. t_000.dita occurs in scope
ks_0.ks_1, which descends from the root scope, so the effective
definition for ks_0.key_a is aa.dita. For completeness, here's
the scope structure:

- Root scope
    key_a=a.dita
    (ks_0.key_a=aa.dita)
    (ks_0.ks_1.key_a=aaa.dita)
    - ks_0
        [all from root; key_a overridden]
        (ks_1.key_a=aaa.dita)
        - ks_1
            [all from ks_0; key_a overridden]

-----------------------------------------------------------

<!--
Sample - 4
	- What does the key scope hierarchy look like in map-0.ditamap?
	- How to reference key_a and key_b explicitly in t_0.dita?
(i.e., use dot notation).
-->
<!-- map-0.ditamap -->
<map>
  <mapref href="map-1.ditamap" keyscope="ks_0"/>
  <topicref href="t_0.dita"/>
</map>

<!-- map-1.ditamap -->
<map>
  <topicgroup keyscope="ks_1">
	  <keydef keys="key_a" href="a.dita" />
	  <topicgroup keyscope="ks_0">
		<keydef keys="key_b" href="b.dita" />
	  </topicgroup>
  </topicgroup>
</map>


- Root scope
  ks_0.ks_1.key_a=a.dita
  ks_0.ks_1.ks_0.key_b=b.dita
  - ks_0
      [all from parent]
      ks_1.key_a=a.dita
      ks_1.ks_0.key_b=b.dita
      - ks_1
          [all from parent]
          key_a=a.dita
          ks_0.key_b=b.dita
          - ks_0
              [all from parent]
              key_b=b.dita


t0.dita is referenced from the root scope, so would use
keyref="ks_0.ks_1.key_a" and keyref="ks_0.ks_1.ks_0.key_b",
respectively.

(VARIATION)

If you'd put the keyscope attribute on the <map> element of
map-1.ditamap
instead of in a nested <topicgroup>, I.e.

<!-- map-1.ditamap -->
<map keyscope="ks_1">
  <keydef keys="key_a" href="a.dita" />
  <topicgroup keyscope="ks_0">
    <keydef keys="key_b" href="b.dita" />
  </topicgroup>
</map>

Then instead of two nested scopes, you have one scope with two names,
e.g. the equivalent of keyscope="ks_0 ks_1". A key in a child scope
can be referenced from a parent scope using any of that scope's names
as a prefix, so now the scope hierarchy looks like this:

- Root scope
    (ks_1.key_a=a.dita)
    (ks_1.ks_0.key_b=b.dita)
    (ks_0.key_a=a.dita)
    (ks_0.ks_0.key_b=b.dita)
    - ks_0 ks_1
        [all from parent]
        key_a=a.dita
        (ks_0.key_b=b.dita)
        - ks_0
            [all from parent]
            key_b=b.dita

And now t0.dita has options. For key_a it can use either
keyref="ks_0.key_a" or "ks_1.key_a", and for key_b it can use either
"ks_0.ks_0.key_b" or "ks_1.ks_0.key_b".


(NOTE: This is the one place where it's worth mentioning
cross-publication linking when talking about key scopes. If
map-1.dita was referenced via scope="peer" instead of
scope="local", then you couldn't reliably use the scope name
from the map to reference its keys, because that map might
not always be available.)

-----------------------------------------------------------

<!--
Sample - 5
	- What does the key scope hierarchy look like in map-0.ditamap?
	- How to reference key_a and key_b explicitly in t_0.dita?
(i.e., use dot notation).
-->
<!-- map-0.ditamap -->
<map>
  <mapref href="map-1.ditamap" keyscope="ks_0"/>
  <mapref href="map-1.ditamap" keyscope="ks_1"/>
  <topicref href="t_0.dita"/>
</map>

<!-- map-1.ditamap -->
<map>
  <topicgroup keyscope="ks_1">
	  <keydef keys="key_a" href="a.dita" />
	  <topicgroup keyscope="ks_0">
		<keydef keys="key_b" href="b.dita" />
	  </topicgroup>
  </topicgroup>
</map>

- Root scope
    (ks_0.ks_1.key_a=a.dita)
    (ks_0.ks_1.ks_0.key_b=b.dita)
    (ks_1.ks_1.key_a=a.dita)
    (ks_1.ks_1.ks_0.key_b=b.dita)
    - ks_0
        [all from parent]
        (ks_1.key_a=a.dita)
        (ks_1.ks_0.key_b=b.dita)
        - ks_1
            [all from parent]
            key_a=a.dita
            (ks_0.key_b=b.dita)
            - ks_0
                [all from parent]
                key_b=b.dita
    - ks_1
        [identical to root->ks_0]


Once again, t0 occurs at the root scope, so it can reference key_a via
"ks0.ks_1.key_a" or "ks_1.ks_1.key_a", and key_b via either
"ks_0.ks_1.ks_0.key_b" or "ks_1.ks_1.ks_0.key_b". However, note that in
this case which one you use implies which copy of the topic should be
referenced in the output, and processors should probably honor that,
though it's not required by the spec. And again, if the keyscope were on
the map instead of on an inner topicgroup, that first level of scoping
would collapse, though then ks_1 would always refer to the first child
scope, due to precedence rules.

-----------------------------------------------------------

(THOUGHTS ON AUTHORING TOOLS)

Authoring tools that wish to be keyref-aware needs some way of
specifying the context from which key definitions are to be
determined. In DITA 1.2, that's as simple as specifying the
root map. In DITA 1.3, in addition to a root scope, they
additionally need to specify a scope path. For instance,
I would expect the user experience when opening a topic from a map
will be unchanged, since the system can determine where in the map,
and thus the key scope structure, the selected topicref resides,
and so the appropriate key space can be determined automatically.
For programs that provide a UI allowing the user to specify the key
resolution context to use, the process will be more involved for
the system, but usually not for the user:

1. User selects root map containing the key context.
2. System determines the key scope path(s) that apply for the given
topic in the specified map. When there is exactly one - the most
likely scenario in my experience - the system automatically uses that
scope, and the user is done.
3. If the specified topic is referenced by the selected map from
multiple key scopes, the system needs to prompt the user for which of
those scopes to apply. This is also the case if the topic is not
referenced from the map, though that might also be considered cause
for a warning message to the user, and the system will need to present
all possible scope paths.

When providing the user with a UI for selecting a key reference to
insert, they should be provided from the currently-selected key
context, determined as described above.

An authoring tool might want to present the list of keys available for a
map. Whereas in DITA 1.2, this would be a flat list of effective
definitions, it will now need to be hierarchical. It might also want to
provide indicators when a given key name is implicitly defined in a
child scope vs. explicitly defined within that scope, as I've attempted
to do in the above examples using parentheses. There might also be options
for showing/hiding implicit qualified keys and overridden keys.


[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]