As the next step, we can allow particular documents to enable or disable particular parts of the DTD. This is accomplished with INCLUDE and IGNORE sections. The basic syntax for these directives appears below.
<![INCLUDE[ <!-- Declarations the parser reads --> ]]> <![IGNORE[ <!-- Declarations the parser ignores --> ]]>
Note that the syntax is the same except for the keyword. By defining the keyword as a parameter entity reference, you can provide a switch customizers can use to turn sections of the DTD on or off. These can be individual declarations or groups of related declarations.
For example, some bank subsidiaries with different systems might wish to leave out an explicit closing balance since it can be calculated from the opening balance and the individual transactions. To enable this, you'd first define a parameter entity such as this one:
<!ENTITY % IncludeClosingBalance "INCLUDE">
Next, you'd place the declaration of the ClosingBalance element inside a conditional section that can be either included or ignored depending on the value of the IncludeClosingBalance entity.
<![%IncludeClosingBalance;[ <!ELEMENT %ClosingBalance.qname; (#PCDATA)> ]]>
What about the content models that use ClosingBalance ? How can they be parameterized? This is quite tricky, but it can be done. You'll need two declarations, one for the case that includes the closing balance and one for the case that doesn't. You'll need to define two parameter entities that identify the two cases. Do this both inside and after the conditional block that decides whether or not to use closing balances .
<![%IncludeClosingBalance;[ <!ELEMENT %ClosingBalance.qname; (#PCDATA)> <!ENTITY AddClosingBalance "INCLUDE"> <!ENTITY DontAddClosingBalance "IGNORE"> ]]> <!ENTITY AddClosingBalance "IGNORE"> <!ENTITY DontAddClosingBalance "INCLUDE">
Inside the conditional block the entities have the value to be used if the closing balance is included. After the conditional block the entities have the value to be used if the closing balance is not included. Order matters here. We're relying on the fact that the first declaration the parser encounters takes precedence when picking between the two possibilities.
Next, wrap the two different declarations of the Statement element in their own conditional sections based on the AddClosingBalance and DontAddClosingBalance entities.
<![%AddClosingBalance;[ <!ELEMENT %Statement.qname; ( %Bank.qname;, %Account.qname;, %Date.qname;, %OpeningBalance.qname;, (%Transaction.qname;)*, %ClosingBalance.qname;) > ]]> <![%DontAddClosingBalance;[ <!ELEMENT %Statement.qname; ( %Bank.qname;, %Account.qname;, %Date.qname;, %OpeningBalance.qname;, (%Transaction.qname;)*) > ]]>
I don't recommend going this far for all declarations in all DTDs, but when you need this much flexibility, this approach is very convenient .
Parameterization recognizes the reality that one size does not fit all. Different systems need to supply and support different information. Making DTDs parameterizable enables them to be customized and reused in ways never envisioned by the original developers. At a minimum you should allow for parameterization that does not remove information from the document: changing namespace prefixes and adding attributes and content to elements. Allowing the complete redefinition of element content models is perhaps a little more dangerous. It has the potential to break processes that depend on the information guaranteed to be provided by a document valid against the unparameterized DTD. However, even this can be useful as long as application software takes proper care not to assume too much and the customized DTD uses a different public ID so it's easily distinguished from the uncustomized variant. Parameterization is not always necessary for tightly coupled systems inside one organization, but its enhanced flexibility is very important for the loosely coupled systems of the Internet, where widely dispersed organizations routinely use applications for tasks the original developers never imagined.