package javacodebook.xml.processing.dom.parse; import java.util.*; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXParseException; import org.xml.sax.SAXException; import org.w3c.dom.*; /** * Die ErrorCollector-Klasse implementiert das org.xml.sax.ErrorHandler * Interface und lässt sich somit sowohl bei SAX-Parsern als auch bei * DOM-Parsern als ErrorHandler registrieren. Im Wesentlichen sammeln * Objekte dieser Klasse Fehlermeldungen, die beim Parsen aufgetreten sind * und stellen diese in aufbereiterter Form zur Verfügung. */ public class ErrorCollector implements ErrorHandler { private Vector warnings = new Vector(); private Vector errors = new Vector(); private Vector fatalErrors = new Vector(); // dieses Objekt wird ausschließlich als Factory benutzt, um // DOM-Knoten zu instanziieren und Fehlermeldungen in XML // zur Verfügung stellen zu können. private Document domFactory = new org.apache.xerces.dom.DocumentImpl(); // Eine Methode, die von dem org.xml.sax.ErrorHandler Interface // gefordert wird. Falls ein ErrorCollector-Objekt bei einem Parser // registriert ist, wird sie jedesmal aufgerufen, fall eine Warnung // auftritt. public void warning(SAXParseException exception) throws SAXException { // die Meldungen der Warnung werden in dem Vector namens 'warnings' // aufbewahrt. warnings.add(exception.getMessage()); } // analog zur warning-Methode public void error(SAXParseException exception) throws SAXException { // die Meldungen der Fehler werden in dem Vector namens 'errors' // aufbewahrt. errors.add(exception.getMessage()); } // analog zur warning-Methode public void fatalError(SAXParseException exception) throws SAXException { // die Meldungen der fatalen Fehler werden in dem Vector namens 'fatalErrors' // aufbewahrt. fatalErrors.add(exception.getMessage()); } /** * Die Methode dient zum Zurücksetzen aller * Fehlermeldungen und Warnings. */ public void reset() { warnings.removeAllElements(); errors.removeAllElements(); fatalErrors.removeAllElements(); } /** * Die Methode liefert ein Element welches die Fehler- * meldungen wiederum in seinen Unterelementen * kapselt. Auf diese Weise können Fehlermeldungen * sehr flexibel weiterverarbeitet werden. * * @return org.w3c.dom.Element */ public Element getErrorMessagesAsXML() { return formatAsXML(errors, "errors"); } // siehe getErrorMessagesAsXML() public Element getWarningsMessagesAsXML() { return formatAsXML(warnings, "warnings"); } // siehe getErrorMessagesAsXML() public Element getFatalErrorMessagesAsXML() { return formatAsXML(fatalErrors, "fatalErrors"); } /** * Diese Methode liefert die Fehlermeldungen als einfachen leicht * formatierten Text. * * @return java.lang.String */ public String getErrorMessagesAsText() { return formatAsText(errors, "errors"); } // siehe getErrorMessagesAsText() public String getWarningsMessagesAsText() { return formatAsText(warnings, "warnings"); } // siehe getErrorMessagesAsText() public String getFatalErrorMessagesAsText() { return formatAsText(fatalErrors, "fatalErrors"); } /** * Diese Methode liefert die Fehlermeldungen in Form eines * Vectors von Strings. * * @return java.lang.String */ public Vector getErrorMessages() { return errors; } // siehe Methode getErrorMessages() public Vector getWarningsMessages() { return warnings; } // siehe Methode getErrorMessages() public Vector getFatalErrorMessages() { return fatalErrors; } /** * Diese Methode verpackt Fehlermeldungen eines Typs in * eine w3c-DOM-Element. Auf diese Weise können die Fehler- * meldungen sehr flexibel weiterverarbeitet werden. * * @param vectorWithStringElements * @param type * @return org.w3c.dom.Element */ private Element formatAsXML(Vector vectorWithStringElements, String type) { Element messages = domFactory.createElement("messages"); messages.setAttribute("type", type); Enumeration enum = vectorWithStringElements.elements(); while (enum.hasMoreElements()) { Element message = domFactory.createElement("message"); Text text = domFactory.createTextNode( (String) enum.nextElement()); message.appendChild(text); messages.appendChild(message); } return messages; } /** * Diese Methode formatiert Fehlermeldungen eines Typs als einfachen String * * @param vectorWithStringElements * @param type * @return java.lang.String */ private String formatAsText(Vector vectorWithStringElements, String type) { if(vectorWithStringElements.size()==0)return "no "+type+" occured"; String returnString = "The following " + type + " have occured"; Enumeration enum = vectorWithStringElements.elements(); while (enum.hasMoreElements()) { String message = (String) enum.nextElement(); returnString = returnString + "\n\t* " + message + "\n"; } return returnString; } // Einige Methoden, um den Verlauf des Parsing-Prozesses beurteilen // zu können, ohne die Fehlermeldungen zu erfragen. public boolean hasWarnings() { if(warnings.size()>0) return true; else return false; } public boolean hasErrors() { if(errors.size()>0) return true; else return false; } public boolean hasFatalErrors() { if(fatalErrors.size()>0) return true; else return false; } public boolean anyProblems() { if (hasWarnings() || hasErrors() || hasFatalErrors()) return true; else return false; } } --- Neue Klasse --- package javacodebook.xml.processing.dom.parse; import java.util.*; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXParseException; import org.xml.sax.SAXException; import org.w3c.dom.*; /** * Die OurErrorHandler-Klasse implementiert das org.xml.sax.ErrorHandler * Interface und lässt sich somit sowohl bei SAX-Parsern als auch bei * DOM-Parsern als ErrorHandler registrieren. Im Wesentlichen sammeln * Objekte dieser Klasse Fehlermeldungen, die beim Parsen aufgetreten sind * und stellen diese in aufbereiterter Form zur Verfügung. */ public class OurErrorHandler implements ErrorHandler { private Vector warnings = new Vector(); private Vector errors = new Vector(); private Vector fatalErrors = new Vector(); // dieses Objekt wird ausschließlich als Factory benutzt, um // DOM-Knoten zu instanziieren und Fehlermeldungen in XML // zur Verfügung stellen zu können. private Document domFactory = new org.apache.xerces.dom.DocumentImpl(); // Eine Methode, die von dem org.xml.sax.ErrorHandler Interface // gefordert wird. Falls ein OurErrorHandler-Objekt bei einem Parser // registriert ist, wird sie jedesmal aufgerufen, fall eine Warnung // auftritt. public void warning(SAXParseException exception) throws SAXException { // die Meldungen der Warnung werden in dem Vector namens 'warnings' // aufbewahrt. warnings.add(exception.getMessage()); } // analog zur warning-Methode public void error(SAXParseException exception) throws SAXException { // die Meldungen der Fehler werden in dem Vector namens 'errors' // aufbewahrt. errors.add(exception.getMessage()); } // analog zur warning-Methode public void fatalError(SAXParseException exception) throws SAXException { // die Meldungen der fatalen Fehler werden in dem Vector namens 'fatalErrors' // aufbewahrt. fatalErrors.add(exception.getMessage()); } /** * Die Methode dien zum Zurücksetzen aller * Fehlermeldungen und Warnings. */ public void reset() { warnings.removeAllElements(); errors.removeAllElements(); fatalErrors.removeAllElements(); } /** * Die Methode liefert ein Element welches die Fehler- * meldungen wiederum in seinen Unterelementen * kapselt. Auf diese Weise können Fehlermeldungen * sehr flexibel weiterverarbeitet werden. * * @return org.w3c.dom.Element */ public Element getErrorMessagesAsXML() { return formatAsXML(errors, "errors"); } // siehe getErrorMessagesAsXML() public Element getWarningsMessagesAsXML() { return formatAsXML(warnings, "warnings"); } // siehe getErrorMessagesAsXML() public Element getFatalErrorMessagesAsXML() { return formatAsXML(fatalErrors, "fatalErrors"); } /** * Diese Methode liefert die Fehlermeldungen als einfachen leicht * formatierten Text. * * @return java.lang.String */ public String getErrorMessagesAsText() { return formatAsText(errors, "errors"); } // siehe getErrorMessagesAsText() public String getWarningsMessagesAsText() { return formatAsText(warnings, "warnings"); } // siehe getErrorMessagesAsText() public String getFatalErrorMessagesAsText() { return formatAsText(fatalErrors, "fatalErrors"); } /** * Diese Methode liefert die Fehlermeldungen in Form eines * Vectors von Strings. * * @return java.lang.String */ public Vector getErrorMessages() { return errors; } // siehe Methode getErrorMessages() public Vector getWarningsMessages() { return warnings; } // siehe Methode getErrorMessages() public Vector getFatalErrorMessages() { return fatalErrors; } /** * Diese Methode verpackt Fehlermeldungen eines Typs in * eine w3c-DOM-Element. Auf diese Weise können die Fehler- * meldungen sehr flexibel weiterverarbeitet werden. * * @param vectorWithStringElements * @param type * @return org.w3c.dom.Element */ private Element formatAsXML(Vector vectorWithStringElements, String type) { Element messages = domFactory.createElement("messages"); messages.setAttribute("type", type); Enumeration enum = vectorWithStringElements.elements(); while (enum.hasMoreElements()) { Element message = domFactory.createElement("message"); Text text = domFactory.createTextNode( (String) enum.nextElement()); message.appendChild(text); messages.appendChild(message); } return messages; } /** * Diese Methode formatiert Fehlermeldungen eines Typs als einfachen String * * @param vectorWithStringElements * @param type * @return java.lang.String */ private String formatAsText(Vector vectorWithStringElements, String type) { if(vectorWithStringElements.size()==0)return "no "+type+" occured"; String returnString = "The following " + type + " have occured"; Enumeration enum = vectorWithStringElements.elements(); while (enum.hasMoreElements()) { String message = (String) enum.nextElement(); returnString = returnString + "\n\t* " + message + "\n"; } return returnString; } // Einige Methoden, um den Verlauf des Parsing-Prozesses beurteilen // zu können, ohne die Fehlermeldungen zu erfragen. public boolean hasWarnings() { if(warnings.size()>0) return true; else return false; } public boolean hasErrors() { if(errors.size()>0) return true; else return false; } public boolean hasFatalErrors() { if(fatalErrors.size()>0) return true; else return false; } public boolean anyProblems() { if (hasWarnings() || hasErrors() || hasFatalErrors()) return true; else return false; } } --- Neue Klasse --- package javacodebook.xml.processing.sax.parse; import java.util.*; import org.xml.sax.*; /** * Der StatefullContentHandler implementiert das ContentHandler-Interface * und hat 2 wesentliche generische Funktionen. * 1. Er kann bestimmte Elemente zählen * 2. Er kann alle Werte eines gewünschten Elements in Form eines * Vectors zur Verfügung stellen. */ public class StatefullContentHandler implements ContentHandler { private Vector contextVector = new Vector(); private Hashtable elementCounter = new Hashtable(); private Hashtable elementDataContainer = new Hashtable(); // Es folgen Methoden die vom ContentHandler-Interface gefordert werden. // // Für jedes Ereignis, dass beim Parsen des XML-Dokuments auftreten kann, // gibt es hier die entsprechenende Methode, die zur Verarbeitung des // Ereignisses implementiert werden kann. Die meisten der folgenden // Implementierungen sind Dummy-Implementierungen. Im Normalfall würde man // in so einem Fall von der org.xml.sax.helpers.DefaultHandler-Klasse erben // und nur die Methoden überschreiben, die man wirklich braucht. Die // DefaultHandler-Klasse liefert leere Implementierungen für alle vom // ContentHandler-Interface geforderten Methoden. public void setDocumentLocator(Locator locator) { System.out.println("setDocumentLocator" + locator); } public void startDocument() throws SAXException { // Das Dokument wird als oberster Kontext angemeldet. this.startContext("DocumentElement"); System.out.println("startDocument"); } public void endDocument() throws SAXException { // Abmeldung des obersten Kontextes this.endContext(); System.out.println("endDocument"); } public void startPrefixMapping(String prefix, String uri) throws SAXException { System.out.println("startPrefixMapping " + prefix + " " + uri); } public void endPrefixMapping(String prefix) throws SAXException { System.out.println("startPrefixMapping " + prefix); } public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { System.out.println("startElement " + localName); // für jedes neue Element wird eine neuer Kontext angemeldet. this.startContext(localName); // falls das Element für eine Zählung registriert ist, zähle // den entsprechenden Zähler in der Hashtable eins hoch. if (this.elementCounter.containsKey(localName)) { ((Counter)this.elementCounter.get(localName)).increase(); } } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { System.out.println("endElement " + localName); // Abmeldung des Kontextes this.endContext(); } public void characters(char[] ch, int start, int length) throws SAXException { System.out.println("characters " + new String(ch,start,length)); // Falls der Kontext über die extractValuesOfElements-Methode registriert wurde, // wird der Inhalt des Elements in einem Vector abgelegt, der in einer // Hashtable diesem Elemententyp zugeordnet ist. if (this.elementDataContainer.containsKey(this.getContext())) { ((Vector)this.elementDataContainer.get(this.getContext())).add(new String(ch,start,length)); } } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { System.out.println("ignorableWhitespace " + ch); } public void processingInstruction(String target, String data) throws SAXException { System.out.println("processingInstruction " + target + " " + data); } public void skippedEntity(String name) throws SAXException { System.out.println("skippedEntity " + name); } // Ende der Methoden die vom ContentHandler-Interface gefordert werden. /** * Diese Methode dient dazu, ein Element für eine Zählung zu registrieren. * Sofern ein Element hier registriert wurde, kann über die getCountOfElement- * Methode erfragt werden, wie oft es in dem Dokument erschienen ist. * * @param elementName */ public void countElements(String elementName) { this.elementCounter.put(elementName, new Counter()); } /** * Falls ein Elementtyp über diese Methode registriert wird, ist nach * dem Parsing-Prozess der Inhalt aller Elemente die im Dokument * aufgetreten sind über die getValuesOfElement-Methode verfügbar. * * @param elementName */ public void extractValuesOfElements(String elementName) { this.elementDataContainer.put(elementName, new Vector()); } /** * Falls ein Elementtyp zuvor über die extractValuesOfElements- * Methode registriert wurde, liefert die Methode einen Vector mit * Strings von Inhalten aller zugehörigen Elemente des gesamten * Dokuments. * * @param name * @return java.util.Vector */ public Vector getValuesOfElement(String name) { return (Vector)this.elementDataContainer.get(name); } /** * Liefert die im Dokument aufgetretene Anzahl von Elementen * des als Parameter übergebenen Typs oder -1, * falls der Elementtyp nicht zur Zählung registriert war. * * @param type * @return int */ public int getCountOfElement(String type) { if (this.elementCounter.containsKey(type)) { return ((Counter)this.elementCounter.get(type)).getInt(); } else { // -1 um zu zeigen, dass der Wert keine Aussage macht, da // das Element unter Umständen gar nicht registriert war. return -1; } } /** * Die Methode dient zur Anmeldung einen neuen Kontextes * * @param name */ private void startContext(String name) { contextVector.add(name); } /** * Die Methode dient zur Abmeldung eines Kontextes */ private void endContext() { contextVector.removeElement(contextVector.lastElement()); } /** * Die Methode liefert den aktuellen Kontext zurück. * Jeweils das zuletzt eingefügte Element im contextVector ist der * aktuelle Kontext. * * @return java.lang.String */ private String getContext() { return (String) contextVector.lastElement(); } } // Hilfsklasse zum Zählen class Counter { private int counter=0; protected void increase() { counter++; } protected int getInt() { return counter; } } --- Neue Klasse --- package javacodebook.xml.processing.sax.parse; import org.w3c.dom.*; import org.xml.sax.*; import javacodebook.xml.processing.dom.parse.*; /** * Die ValidatingSAXParseUtil benutzt einen SAX-Parser, die ErrorHandler- * Implementierung OurErrorHandler und die ContentHandler-Implementierung * StatefullContentHandler um ein XML-Dokument zu validieren, bestimmte * Elemente zu zählen und einige Elemente zu extrahieren. */ public class ValidatingSAXParseUtil { private static final String USAGE = "\nBenutzerhinweis: javacodebook.xml.processing.sax.parse.ValidatingSAXParseUtil " + "\n\nwobei\n\n\n" + "die URI ist, unter der ein XML-Dokument zu finden ist das geparst und validiert werden soll.\n"; /** * In der main-Methode wird der Kommandozeilen-Parameter ausgelesen, der beschreibt, welches * Dokument geparst werden soll. Dann wird ein SAX-Parser instanziiert, bei dem * ein ErrorHandler und ein ContentHandler registriert werden. * Es wird das Dokument geparst und anschließend die Fehler vom * ErrorHandler und einige Daten vom ContentHandler abgefragt. * * @param args */ public static void main(String[] args) { if (args.length != 1) { System.out.println(getUsage()); System.exit(1); } String documentLocation = args[0]; org.apache.xerces.parsers.SAXParser parser = new org.apache.xerces.parsers.SAXParser(); // Es wird ein Objekt unserer ErrorHandler-Implementierung // aus dem Beispiel 'processing.dom.parse' instanziiert. javacodebook.xml.processing.dom.parse.ErrorCollector errorCollector = new javacodebook.xml.processing.dom.parse.ErrorCollector(); // Zur Extraktion von Daten wird eine Instanz unseres StatefulContentHandlers // erzeugt. StatefullContentHandler statefullContentHandler = new StatefullContentHandler(); // Das StatefullContentHandler-Objekt wird so konfiguriert, dass es die // Anzahl der Person-Elemente zählt. statefullContentHandler.countElements("person"); // Außerdem sollen alle E-Mail-Adressen extrahiert werden. statefullContentHandler.extractValuesOfElements("email"); try { // über die die setFeature-Methode des Parser-Objektes kann // die Validierung eingeschaltet werden. parser.setFeature("http://xml.org/sax/features/validation", true); // Das ErrorHandler-Objekt wird beim Parser als // ErrorHandler registriert. parser.setErrorHandler(errorCollector); // Das ContentHandler-Objekt wird beim Parser registriert. parser.setContentHandler(statefullContentHandler); // Das Dokument, dass als Kommandozeilenparameter übergeben // wurde wird geparst. parser.parse(documentLocation); // Falls Probleme aufgetreten sind, werden die entsprechenden // Meldungen in die Standardausgabe geschrieben. if (errorCollector.anyProblems()) { System.out.println(errorCollector.getWarningsMessagesAsText()); System.out.println(errorCollector.getErrorMessagesAsText()); System.out.println(errorCollector.getFatalErrorMessagesAsText()); } else { System.out.println("Das Dokument entspricht Schema bzw. DTD"); } // Nun können die Ergebnisse von unserem StatefullContentHandler erfragt und // in die Standardausgabe geschrieben werden. System.out.println("Anzahl der person Elemente : "+statefullContentHandler.getCountOfElement("person")); System.out.println("emails: "+statefullContentHandler.getValuesOfElement("email")); } catch (Exception e) { System.out.println("Dokument konnte nicht verarbeitet werden: " + e + "\n" + errorCollector.getFatalErrorMessagesAsText()); } } public static String getUsage() { return USAGE; } }