This sample program:
loads a XHTML file;
traverses the loaded document searching for h1
, h2
, h3
headings;
adds an empty <a name="tocentry
to each of these headings;NNN
"/>
for each of the traversed headings, adds an indented line containing <a href="#tocentry
to the NNN
">text of the heading
</a>div
that will be used as a Table of Contents (TOC);
inserts the div
used as a TOC as first child of body
;
saves modified document to disk.
Excerpts from AddTOC.java
:
public class AddTOC { private static final Name BODY = Name.get(Namespace.XHTML, "body"); private static final Name DIV = Name.get(Namespace.XHTML, "div"); private static final Name H1 = Name.get(Namespace.XHTML, "h1"); private static final Name H2 = Name.get(Namespace.XHTML, "h2"); private static final Name H3 = Name.get(Namespace.XHTML, "h3"); private static final Name A = Name.get(Namespace.XHTML, "a"); private static final Name BR = Name.get(Namespace.XHTML, "br"); private static final Name CLASS = Name.get("class"); private static final Name NAME = Name.get("name"); private static final Name HREF = Name.get("href"); private static final class Info { public int headingCount; public Element toc; public Element body; } public static void processDocument(Document doc) { final Info info = new Info(); Element b = new Element(Name.get(Namespace.XHTML, "b")); b.putAttribute(CLASS, "toctitle"); b.appendChild(new Text("Contents")); info.toc = new Element(DIV); info.toc.putAttribute(CLASS, "toc"); info.toc.appendChild(b); Traversal.traverse(doc.getRootElement(), new Traversal.HandlerBase() { public Object enterElement(Element element) { Name name = element.getName(); if (name == H1 || name == H2 || name == H3) { processHeading(element, info); return Traversal.LEAVE_ELEMENT; } else { if (name == BODY) { info.body = element; } return null; } } }); if (info.body != null) { info.toc.appendChild(new Element(BR)); info.toc.appendChild(new Element(Name.get(Namespace.XHTML, "hr"))); add(info.body, info.toc); } } private static void processHeading(Element heading, Info info) { String id = "tocentry" + info.headingCount++; Element target = new Element(A); target.putAttribute(CLASS, "tocentry"); target.putAttribute(NAME, id); add(heading, target); Traversal.TextGrabber grabber = new Traversal.TextGrabber(); Traversal.traverse(heading, grabber); String headingText = XMLText.collapseWhiteSpace(grabber.grabbed.toString()); Element link = new Element(A); link.putAttribute(HREF, "#" + id); link.appendChild(new Text(headingText)); int indentation; Name headingName = heading.getName(); if (headingName == H1) { indentation = 4; } else if (headingName == H2) { indentation = 8; } else { indentation = 12; } StringBuilder indent = new StringBuilder(); while (indentation > 0) { indent.append('\u00A0'); // --indentation; } info.toc.appendChild(new Element(BR)); info.toc.appendChild(new Text(indent.toString())); info.toc.appendChild(link); } private static void add(Element parent, Element added) { Name addedName = added.getName(); String addedClass = added.getAttribute(CLASS); boolean replaced = false; loop: for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { switch (child.getType()) { case TEXT: case COMMENT: case PROCESSING_INSTRUCTION: break; case ELEMENT: { Element element = (Element) child; if (element.getName() == addedName && addedClass.equals(element.getAttribute(CLASS))) { parent.replaceChild(element, added); replaced = true; break loop; } } break; } } if (!replaced) { parent.insertChild(parent.getFirstChild(), added); } }
Element and attribute names are not plain strings, they are
Names and namespaces are managed as symbols in a symbol table. For example, it is not possible to invoke Because of this, names and namespaces can be compared for equality using |
A document is composed of Attributes are not Function The
|
During the traversal,
Document traversal can be controlled by returning a value from notification methods. Return In the | |
You do not always need to define your own | |
|
public static void main(String[] args) throws IOException { if (args.length != 2) { System.err.println( "usage: java AddTOC in_xhtml_file out_xhtml_file"); System.exit(1); } String inFileName = args[0]; String outFileName = args[1]; Document doc = LoadDocument.load(new File(inFileName)); AddTOC.processDocument(doc); SaveDocument.save(doc, new File(outFileName)); }
The document is loaded using the high-level document loader Both document loaders automatically add some properties to the loaded document. Example: a Both loaders are XML catalog aware. Note that in However, there many advantages to using
| |
The document is saved using the hight-level document writer |
Any XML node can have application-level properties. These properties are generally added by document loaders at load time but nothing prevents a Java™ developer from adding its own properties at any time.
What follows is a comparison between element attributes and properties.
Attribute | Property |
---|---|
Part of document content. | Not part of document content. |
User can edit attributes. | User cannot edit properties. |
Can be loaded and saved to disk as XML. | Transient. |
XML nodes other than elements cannot have attributes. | Any XML node can have properties. |
An attribute name is a Name . An attribute value is string. | A property name is also a Name . A property value is an Object . |
Views are notified when attributes are changed by the means of an AttributeEvent . | Views are also notified when properties are changed by the means of a PropertyEvent . |
Properties used to implement XXE have their names defined as constants in com.xmlmind.xml.doc.Constants
and in com.xmlmind.xmledit.edit.Constants
.