Table of Contents
A documentHook is some code written in Java™ notified by XXE each time a document (having a given document type) is created, opened, checked for validity, saved to disk and closed.
This is a very general mechanism which has been created to perform semantic validation beyond what can be done using a DTD or XML-Schema alone but which can also be used to perform many other tasks.
A documentHook which:
checks that the src attribute of the <img> element is not an absolute file path;
checks that the value of the name attribute of a <a> element is not already in use;
checks that for each href attribute of a <a> element starting with # (local reference), there is an element with such name or id;
has been written to be used as an example in this tutorial.
Compile this documentHook by executing ant (see build.xml) in samples/checklinks/. The build creates checklinks.jar. Then test the documentHook by:
Copying checklinks.incl and checklinks.jar to .XXE_install_directory/addon/config/xhtml/
Including checklinks.incl in the XXE configuration file for XHTML which is .XXE_install_directory/addon/config/xhtml/xhtml_strict.xxe
Restarting XXE.
Loading tests/in/sample2.html into XXE and examining all the problems found by the documentHook for this file (click on the tool tab to display the semantic warnings).
How to deploy a documentHook is detailed in Section 7, “documentHook” in XMLmind XML Editor - Configuration and Deployment.
The DocumentHook interface is very easy to understand:
| Method | Description |
|---|---|
| documentCreated | Invoked after a document has been created. |
| documentOpened | Invoked after a document has been opened. |
| checkingDocument | Invoked before a document conforming to a DTD or schema is validated. |
| documentChecked | Invoked after a document conforming to a DTD or schema has been validated. |
| savingDocument | Invoked before a document is saved to disk. |
| documentSaved | Invoked after a document has been saved to disk. |
| savingDocumentAs | Invoked before a document is saved to a different location. |
| documentSavedAs | Invoked after a document has been saved to a different location. |
| savingDocumentCopy | Invoked before a copy of document being edited is saved to disk. |
| documentCopySaved | Invoked after a copy of document being edited has been saved to disk. |
| copyingDocument | Invoked before a document is copied to a temporary file by a process command. |
| documentCopied | Invoked after a document has been copied to a temporary file by a process command. |
| closingDocument | Invoked before the document being edited is closed. |
| documentClosed | Invoked after a document being edited is closed. |
The documentHook used as an example in this tutorial just needs to implement the documentChecked method, therefore it extends adapter class DocumentHookBase rather than implement the above interface.
public class CheckLinks extends DocumentHookBase {
private static final Name SRC = Name.get("src");
private static final Name NAME = Name.get("name");
private static final Name ID = Name.get("id");
private static final Name HREF = Name.get("href");
public Diagnostic[] documentChecked(Document doc, int status,
Diagnostic[] diagnostics) {
if (status != STATUS_SUCCESS)
return diagnostics;
final ArrayList warnings = new ArrayList();
final ArrayList links = new ArrayList();
final HashMap anchors = new HashMap();
Traversal.traverse(doc.getRootElement(), new Traversal.HandlerBase() {
public Object enterElement(Element element) {
String localName = element.getLocalName();
String anchorName = null;
if ("img".equals(localName)) {
String src = element.getAttribute(SRC);
if (src != null) {
if (src.startsWith("file:/") ||
src.startsWith("/") ||
src.startsWith("\\\\") ||
(src.length() >= 3 &&
Character.isLetter(src.charAt(0)) &&
src.regionMatches(1, ":\\", 0, 2))) {
warnings.add(new DiagnosticImpl(
element,
"src attribute looks like an absolute file path",
Diagnostic.SEVERITY_SEMANTIC_WARNING));
}
}
} else if ("a".equals(localName)) {
String href = element.getAttribute(HREF);
if (href != null) {
if (href.startsWith("#"))
links.add(element);
} else {
anchorName = element.getAttribute(NAME);
if (anchorName != null) {
ArrayList elements =
(ArrayList) anchors.get(anchorName);
if (elements == null) {
elements = new ArrayList();
anchors.put(anchorName, elements);
}
elements.add(element);
}
}
}
String id = element.getAttribute(ID);
if (id != null && !id.equals(anchorName)) {
ArrayList elements = (ArrayList) anchors.get(id);
if (elements == null) {
elements = new ArrayList();
anchors.put(id, elements);
}
elements.add(element);
}
return null;
}
});
int count = links.size();
for (int i = 0; i < count; ++i) {
Element element = (Element) links.get(i);
String id = element.getAttribute(HREF).substring(1);
if (!anchors.containsKey(id))
warnings.add(new DiagnosticImpl(
element,
"reference to non-existent name or id '" + id + "'",
Diagnostic.SEVERITY_SEMANTIC_WARNING));
}
Iterator iter = anchors.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String id = (String) entry.getKey();
ArrayList elements = (ArrayList) entry.getValue();
count = elements.size();
for (int i = 1; i < count; ++i) {
warnings.add(new DiagnosticImpl(
(Element) elements.get(i),
"name or id '" + id + "' already defined",
Diagnostic.SEVERITY_SEMANTIC_WARNING));
}
}
int warningCount = warnings.size();
if (warningCount > 0) {
Diagnostic[] diagnostics2 =
new Diagnostic[diagnostics.length + warningCount];
System.arraycopy(diagnostics, 0, diagnostics2, 0,
diagnostics.length);
for (int i = 0; i < warningCount; ++i)
diagnostics2[diagnostics.length+i] =
(Diagnostic) warnings.get(i);
diagnostics = diagnostics2;
}
return diagnostics;
}
}If the This is the case for all the methods of a Example 1: saving the document fails because of an I/O error. Example 2: closing the document fails because an | |
Document is traversed using the Traversal utility. The anonymous Traversal.Handler
| |
If the value of the | |
Elements | |
Elements | |
Elements having an Note that a | |
Elements contained in ArrayList | |
Elements contained in HashMap | |
If semantic warnings have been found for the document, they are added to the list of Diagnostic passed as an argument and the augmented list is returned as the result of the |