001// DtdDemo.java: demonstration application showing DTD queries.
002// NO WARRANTY! See README, and copyright below.
003// $Id$
004// Modified 11/8/98 to add package statement.
005package com.microstar.xml.demo;
006
007import java.util.Enumeration;
008
009import com.microstar.xml.XmlParser;
010
011/**
012 * Demonstration application showing DTD queries.
013 * <p>Usage: <code>java DtdDemo &lt;url&gt;</code>
014 * <p>Or, use it as an applet, supplying the URL as the <code>url</code>
015 * parameter.
016 * <p>Note: This does not preserve any processing instructions
017 * or parameter entities in the DTD; otherwise, produces a fully expanded
018 * and normalised version.
019 * @author Copyright (c) 1997, 1998 by Microstar Software Ltd.;
020 * @author written by David Megginson &lt;dmeggins@microstar.com&gt;
021 * @version 1.1
022 * @since Ptolemy II 0.2
023 * @see com.microstar.xml.XmlParser
024 * @see com.microstar.xml.XmlHandler
025 * @see XmlApp
026 */
027public class DtdDemo extends XmlApp {
028    /**
029     * Entry point for an application (applets enter through XmlApp.init()).
030     * @param args The arguments.  The first argument should be the uri
031     * @exception Exception If the parse fails
032     * @see XmlApp
033     */
034    public static void main(String[] args) throws Exception {
035        DtdDemo demo = new DtdDemo();
036
037        if (args.length != 1) {
038            System.err.println("Usage: java DtdDemo <uri>");
039            System.exit(1);
040        } else {
041            demo.doParse(args[0]);
042        }
043    }
044
045    /**
046     * Print a comment showing where the DTD (if any) begins.
047     */
048    @Override
049    public void startDocument() {
050        displayText("<-- Start of DTD -->\n");
051    }
052
053    /**
054     * Print a comment showing where the DTD (if any) ends.
055     * @param errorCount Ignored in this method.
056     */
057    public void endDocument(int errorCount) {
058        displayText("<-- End of DTD -->");
059    }
060
061    /**
062     * Dump the DTD.
063     * <p>Once this event is received, we know that the DTD is
064     * completely parsed, and can use AElfred's query routines
065     * to reconstruct a normalised version of it.
066     * @see #dumpNotations
067     * @see #dumpEntities
068     * @see #dumpElements
069     */
070    @Override
071    public void doctypeDecl(String name, String pubid, String sysid) {
072        dumpNotations();
073        dumpEntities();
074        dumpElements();
075    }
076
077    /**
078     * Produce normalised declarations for all notations.
079     * @see #makeExternalIdentifiers
080     */
081    public void dumpNotations() {
082        Enumeration notationNames = parser.declaredNotations();
083        String nname;
084        String extId;
085
086        // Mark the beginning of a new section.
087        displayText("<-- Notation Declarations -->\n");
088
089        // Loop through all declared notations.
090        while (notationNames.hasMoreElements()) {
091            nname = (String) notationNames.nextElement();
092            extId = makeExternalIdentifiers(parser.getNotationPublicId(nname),
093                    parser.getNotationSystemId(nname));
094            displayText("<!NOTATION " + nname + +' ' + extId + ">\n");
095        }
096    }
097
098    /**
099     * Produce normalised declarations for all general entities.
100     * @see #makeLiteral
101     * @see #makeExternalIdentifiers
102     */
103    public void dumpEntities() {
104        Enumeration entityNames = parser.declaredEntities();
105        String ename;
106        String value;
107
108        // Mark the beginning of a new section.
109        displayText("<-- Entity Declarations -->\n");
110
111        // Loop through all the declared
112        // entities.
113        while (entityNames.hasMoreElements()) {
114            ename = (String) entityNames.nextElement();
115
116            // Skip parameter entities.
117            if (ename.startsWith("%")) {
118                continue;
119            }
120
121            // Construct a value based on the
122            // class of entity.
123            value = null;
124
125            switch (parser.getEntityType(ename)) {
126            // Internal text entity
127            case XmlParser.ENTITY_INTERNAL:
128                value = makeLiteral(parser.getEntityValue(ename));
129                break;
130
131            // External binary entity
132            case XmlParser.ENTITY_NDATA:
133                value = makeExternalIdentifiers(parser.getEntityPublicId(ename),
134                        parser.getEntitySystemId(ename)) + "NDATA "
135                        + parser.getEntityNotationName(ename);
136                break;
137
138            // External text entity
139            case XmlParser.ENTITY_TEXT:
140                value = makeExternalIdentifiers(parser.getEntityPublicId(ename),
141                        parser.getEntitySystemId(ename));
142                break;
143            }
144
145            // Print a normalised declaration.
146            displayText("<!ENTITY " + ename + ' ' + value + ">\n");
147        }
148    }
149
150    /**
151     * Produce normalised declarations for all elements.
152     * @see #dumpAttributes
153     */
154    public void dumpElements() {
155        Enumeration elementNames = parser.declaredElements();
156        String elname;
157
158        // Mark the beginning of a new section.
159        displayText("<-- Element Type Declarations -->\n");
160
161        // Loop through all of the declared
162        // elements.
163        while (elementNames.hasMoreElements()) {
164            String contentSpec = "ANY";
165
166            elname = (String) elementNames.nextElement();
167
168            // Construct a content spec based
169            // on the element's content type.
170            switch (parser.getElementContentType(elname)) {
171            case XmlParser.CONTENT_EMPTY:
172                contentSpec = "EMPTY";
173                break;
174
175            case XmlParser.CONTENT_ANY:
176                contentSpec = "ANY";
177                break;
178
179            case XmlParser.CONTENT_ELEMENTS:
180            case XmlParser.CONTENT_MIXED:
181                contentSpec = parser.getElementContentModel(elname);
182                break;
183            }
184
185            // Print a normalised element type
186            // declaration.
187            displayText("<!ELEMENT " + elname + ' ' + contentSpec + ">");
188
189            // Print the ATTLIST declaration,
190            // if any.
191            dumpAttributes(elname);
192
193            // Blank line.
194            displayText("");
195        }
196    }
197
198    /**
199     * Dump attributes for an element.
200     * @param elname The element name.
201     * @see #makeAttributeType
202     * @see #makeAttributeValue
203     */
204    public void dumpAttributes(String elname) {
205        Enumeration attributeNames = parser.declaredAttributes(elname);
206        String aname;
207        String type;
208        String value;
209
210        // Skip if there are no declared
211        // attributes for this element
212        // type.
213        if (attributeNames == null) {
214            return;
215        }
216
217        // Print the start of the ATTLIST
218        // declaration.
219        displayText("<!ATTLIST " + elname);
220
221        // Loop through all of the declared
222        // attributes.
223        while (attributeNames.hasMoreElements()) {
224            aname = (String) attributeNames.nextElement();
225            type = makeAttributeType(elname, aname);
226            value = makeAttributeValue(elname, aname);
227
228            // Print the declaration for a
229            // single attribute.
230            displayText("  " + aname + ' ' + type + ' ' + value);
231        }
232
233        // Finish the ATTLIST declaration.
234        displayText(">");
235    }
236
237    /**
238     * Generate the attribute type as a normalised string.
239     * @param elname The element name.
240     * @param aname The attribute name.
241     * @return The attribute type as a normalised string.
242     */
243    public String makeAttributeType(String elname, String aname) {
244        // Construct a string equivalent
245        // of the attribute type.
246        switch (parser.getAttributeType(elname, aname)) {
247        case XmlParser.ATTRIBUTE_CDATA:
248            return "CDATA";
249
250        case XmlParser.ATTRIBUTE_ID:
251            return "ID";
252
253        case XmlParser.ATTRIBUTE_IDREF:
254            return "IDREF";
255
256        case XmlParser.ATTRIBUTE_IDREFS:
257            return "IDREFS";
258
259        case XmlParser.ATTRIBUTE_ENTITY:
260            return "ENTITY";
261
262        case XmlParser.ATTRIBUTE_ENTITIES:
263            return "ENTITIES";
264
265        case XmlParser.ATTRIBUTE_NMTOKEN:
266            return "NMTOKEN";
267
268        case XmlParser.ATTRIBUTE_NMTOKENS:
269            return "NMTOKENS";
270
271        case XmlParser.ATTRIBUTE_ENUMERATED:
272
273            // An enumeration.
274            return parser.getAttributeEnumeration(elname, aname);
275
276        case XmlParser.ATTRIBUTE_NOTATION:
277
278            // An enumeration of notations.
279            return "NOTATION " + parser.getAttributeEnumeration(elname, aname);
280        }
281
282        return null;
283    }
284
285    /**
286     * Generate a full attribute default value.
287     * @param elname The element name.
288     * @param aname The attribute name.
289     * @return The full attribute default value
290     * @see #makeLiteral
291     */
292    public String makeAttributeValue(String elname, String aname) {
293        // Generate a default value based
294        // on the type.
295        switch (parser.getAttributeDefaultValueType(elname, aname)) {
296        case XmlParser.ATTRIBUTE_DEFAULT_IMPLIED:
297            return "#IMPLIED";
298
299        case XmlParser.ATTRIBUTE_DEFAULT_SPECIFIED:
300            return makeLiteral(parser.getAttributeDefaultValue(elname, aname));
301
302        case XmlParser.ATTRIBUTE_DEFAULT_REQUIRED:
303            return "#REQUIRED";
304
305        case XmlParser.ATTRIBUTE_DEFAULT_FIXED:
306            return "#FIXED " + makeLiteral(
307                    parser.getAttributeDefaultValue(elname, aname));
308        }
309
310        return null;
311    }
312
313    /**
314     * Construct a string equivalent of external identifiers.
315     * @param pubid The public identifier
316     * @param sysid The string id
317     * @return The external identifiers
318     * @see #makeLiteral
319     */
320    public String makeExternalIdentifiers(String pubid, String sysid) {
321        String extId = "";
322
323        if (pubid != null) {
324            extId = "PUBLIC " + makeLiteral(pubid);
325
326            if (sysid != null) {
327                extId = extId + ' ' + makeLiteral(sysid);
328            }
329        } else {
330            extId = "SYSTEM " + makeLiteral(sysid);
331        }
332
333        return extId;
334    }
335
336    /**
337     * Quote a literal, and escape any '"' or non-ASCII characters within it.
338     * @param data The data
339     * @return the data as a literal
340     */
341    public String makeLiteral(String data) {
342        char[] ch = data.toCharArray();
343        StringBuffer buf = new StringBuffer();
344
345        buf.append('"');
346
347        for (char element : ch) {
348            if (element == '"') {
349                buf.append("&#22;");
350            } else if (element > 0x7f) {
351                buf.append("&#" + (int) element + ";");
352            } else {
353                buf.append(element);
354            }
355        }
356
357        buf.append('"');
358
359        return buf.toString();
360    }
361}
362
363// End of DtdDemo.java