Skip to content

Commit

Permalink
Add new Map Cleanup plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Robert D Anderson <[email protected]>
  • Loading branch information
Robert D Anderson committed Sep 5, 2018
1 parent 74c1fa3 commit cc47b9b
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 0 deletions.
71 changes: 71 additions & 0 deletions org.metadita.cleanupmap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Extend preprocessing with a "map cleanup" step

This plugin is an extension of a [DITA-OT Slack channel](https://dita-ot.slack.com/archives/C03SENFUQ/p1536160123000100) thread
about an issue I found in publishing PDF.

As defined in the DITA spec, when you refer to another map using `format="ditamap"` from something like `<chapter>`,
you're essentially forcing the role of "chapter" onto top level entries in that map. In our implementation, this is
done by forcing the chapter's `@class` attribute on to the pulled top-level entries. This process ensures that
whatever you pull in is treated like a chapter, preserving the overall intent and structure of the bookmap.
The process generally works in most cases, even if it makes implementations a bit ugly. There are also
exceptions for things like `<mapref>` where it would be counter-productive to push the `mapref` role onto
referenced topics. Background and examples using `<chapter>` are here: http://docs.oasis-open.org/dita/dita/v1.3/errata01/os/complete/part1-base/archSpec/base/cascading-of-roles-in-specialized-maps.html

The issue I've encountered is that when you move that map reference from `<chapter>` up to `<part>`, you
suddenly lose all of your chapters. In our implementation, parts are really only allowed to nest chapters
(though the base bookmap model is technically more flexible). So if you have a map branch like this, you'll
end up with "Part I" that nests "Chapter 1" and "Chapter 2":
```
<part href="majorTopic.dita">
<chapter href="majorTopicFeatureA.dita"><!--...--></chapter>
<chapter href="majorTopicFeatureB.dita"><!--...--></chapter>
</part>
```

Now, what happens if I need to reuse that same sequence outside of the map? I move that same structure
into a generic map that's used to publish in non-book contexts, and then pull it into my bookmap:
```
In the bookmap:
<part href="reusableMajorTopic.ditamap" format="ditamap"/>
In reusableMajorTopic.ditamap:
<map>
<topicref href="majorTopic.dita">
<topicref href="majorTopicFeatureA.dita"><!--...--></topicref>
<topicref href="majorTopicFeatureB.dita"><!--...--></topicref>
</topicref>
</map>
```

Now, based on the cascading of roles as defined in the specification, the reference from `<part>`
forces the top-level entry `majorTopic.dita` to be treated as a part. But, critically, it loses
any knowledge that the sub-topics are chapters. Today, in DITA-OT 3.1 (and earlier), I end up with
a PDF that has a "Part I" as before -- but the sections within that are formatted as
generic topics instead of as chapters. On its own, this is unfortunate; if my map contains other
parts that are *not* coded as references, I end up with some parts where nested topics
show up as chapters, and other parts where nested topics are *not* chapters.

## The plugin

Because anyone can specialize or constrain markup to require a specific structure -- and generic
DITA-OT steps that resolve maps cannot know about or enforce those structures -- it seemed like
a good idea to make this a generic "Fix map structure markup" process.

This plugin makes use of the general `preprocess.post` extension point - one I've found useful
for a lot of general-purpose extensions. It adds in a simple XSLT step that runs over maps in the temp
directory. All it does is look for:

1. Part elements (match class value ` bookmap/part `), where
1. The child element is a `topicref` but is *not* a `chapter`, and
1. The format is DITA (to make sure we don't turn a `<ditavalref>` into a chapter), and
1. It actually references a topic (or has a title)

In those cases, it forces the role of "chapter" onto the child topicref.

The plugin also defines an extension point that allows other plugins to make similar changes.
This means if you have a map structure that requires specific children -- but your markup also allows
that structure to be pulled in from another map -- you can plug in to this process and
ensure that the map meets your requirements.

**Note:** This plugin requires DITA-OT 3.0 or higher, as it makes use of the `ant.import` extension
added in that version of DITA-OT.
18 changes: 18 additions & 0 deletions org.metadita.cleanupmap/cleanuptarget.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Ant targets used by many or all IBM transform types -->
<project xmlns:dita="http://dita-ot.sourceforge.net" name="cleanuptarget"
xmlns:if="ant:if"
xmlns:unless="ant:unless">

<target name="cleanup.map.structures" unless="skip.cleanup.map.structures">
<pipeline message="Update class attributes to match content models" taskname="cleanup-map-structures">
<xslt basedir="${dita.temp.dir}"
reloadstylesheet="${dita.preprocess.reloadstylesheet}"
style="${dita.plugin.org.metadita.cleanupmap.dir}/xsl/cleanupmap.xsl" filenameparameter="file-being-processed">
<ditafileset format="ditamap"/>
<xmlcatalog refid="dita.catalog"/>
</xslt>
</pipeline>
</target>

</project>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin id="org.metadita.cleanupmap">
<!-- extension points -->
<extension-point id="metadita.xsl.cleanup.map.structures" name="XSLT import to fix map structures"/>
<feature extension="ant.import" file="cleanuptarget.xml"/>
<feature extension="depend.preprocess.post" value="cleanup.map.structures"/>
<template file="xsl/cleanupmap_template.xsl"/>
</plugin>
11 changes: 11 additions & 0 deletions org.metadita.cleanupmap/samples/BOOK.ditamap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookmap PUBLIC "-//OASIS//DTD DITA BookMap//EN" "bookmap.dtd">
<bookmap>
<booktitle>
<mainbooktitle>Main book title</mainbooktitle>
</booktitle>
<part href="startpart.dita">
<chapter href="startchap.dita"/>
</part>
<part href="pulledin.ditamap" format="ditamap"/>
</bookmap>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/makechap.dita
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="makechap">
<title>Make me a chapter!</title>
<body>
<p>Cause I'm pulled in as child of thing pulled in as part!</p>
</body>
</topic>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/makechap2.dita
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="makechap">
<title>Make me another chapter!</title>
<body>
<p>Cause I'm pulled in as child of thing pulled in as part!</p>
</body>
</topic>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/makepart.dita
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="makepart">
<title>Make me into a part!</title>
<body>
<p>Cause I'm pulled in to part!</p>
</body>
</topic>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/pulledin.ditamap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "ibm-map.dtd">
<map xml:lang="en-us"><title>I'm gonna be pulled in!</title>
<topicref href="makepart.dita">
<topicref href="makechap.dita"/>
<topicref href="makechap2.dita"/>
</topicref>
</map>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/startchap.dita
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="startchap">
<title>I'm really a chapter</title>
<body>
<p>referenced from chapter</p>
</body>
</topic>
8 changes: 8 additions & 0 deletions org.metadita.cleanupmap/samples/startpart.dita
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="startpart">
<title>I'm really a part</title>
<body>
<p>referenced from part</p>
</body>
</topic>
14 changes: 14 additions & 0 deletions org.metadita.cleanupmap/xsl/cleanup-bookmap.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">

<xsl:template match="*[contains(@class,' bookmap/part ')]/
*[contains(@class,' map/topicref ') and not(contains(@class,' bookmap/chapter '))][empty(@format) or @format='dita'][@href or @navtitle or */*[contains(@class,' topic/navtitle ')]]/
@class">
<xsl:attribute name="class">- map/topicref bookmap/chapter </xsl:attribute>
<xsl:attribute name="originalclass"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>

</xsl:stylesheet>
14 changes: 14 additions & 0 deletions org.metadita.cleanupmap/xsl/cleanupmap.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

<xsl:import href="plugin:org.dita.base:xsl/common/dita-utilities.xsl"/>
<xsl:import href="plugin:org.dita.base:xsl/common/output-message.xsl"/>

<xsl:import href="cleanupmapImpl.xsl"/>
<xsl:import href="cleanup-bookmap.xsl"/>


<xsl:variable xmlns:dita="http://dita-ot.sourceforge.net" name="msgprefix">DOTX</xsl:variable>

<xsl:output method="xml" encoding="utf-8" indent="no"/>

</xsl:stylesheet>
13 changes: 13 additions & 0 deletions org.metadita.cleanupmap/xsl/cleanupmapImpl.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">

<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>
18 changes: 18 additions & 0 deletions org.metadita.cleanupmap/xsl/cleanupmap_template.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">

<xsl:import href="plugin:org.dita.base:xsl/common/dita-utilities.xsl"/>
<xsl:import href="plugin:org.dita.base:xsl/common/output-message.xsl"/>

<xsl:import href="cleanupmapImpl.xsl"/>
<xsl:import href="cleanup-bookmap.xsl"/>
<dita:extension id="metadita.xsl.cleanup.map.structures" behavior="org.dita.dost.platform.ImportXSLAction" xmlns:dita="http://dita-ot.sourceforge.net"/>

<xsl:variable name="msgprefix">DOTX</xsl:variable>

<xsl:output method="xml" encoding="utf-8" indent="no" />

</xsl:stylesheet>

0 comments on commit cc47b9b

Please sign in to comment.