/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.build.doc; import java.io.File; import java.io.FileReader; import java.util.Stack; import org.h2.util.IOUtils; /** * This class checks that the HTML and XML part of the source code * is well-formed XML. */ public class XMLChecker { /** * This method is called when executing this application from the command * line. * * @param args the command line parameters */ public static void main(String... args) throws Exception { new XMLChecker().run(args); } private void run(String... args) throws Exception { String dir = "."; for (int i = 0; i < args.length; i++) { if ("-dir".equals(args[i])) { dir = args[++i]; } } process(dir + "/src"); process(dir + "/docs"); } private void process(String path) throws Exception { if (path.endsWith("/CVS") || path.endsWith("/.svn")) { return; } File file = new File(path); if (file.isDirectory()) { for (String name : file.list()) { process(path + "/" + name); } } else { processFile(path); } } private static void processFile(String fileName) throws Exception { int idx = fileName.lastIndexOf('.'); if (idx < 0) { return; } String suffix = fileName.substring(idx + 1); if (!suffix.equals("html") && !suffix.equals("xml") && !suffix.equals("jsp")) { return; } // System.out.println("Checking file:" + fileName); FileReader reader = new FileReader(fileName); String s = IOUtils.readStringAndClose(reader, -1); Exception last = null; try { checkXML(s, !suffix.equals("xml")); } catch (Exception e) { last = e; System.out.println("ERROR in file " + fileName + " " + e.toString()); } if (last != null) { last.printStackTrace(); } } private static void checkXML(String xml, boolean html) throws Exception { // String lastElement = null; // <li>: replace <li>([^\r]*[^<]*) with <li>$1</li> // use this for html file, for example if <li> is not closed String[] noClose = {}; XMLParser parser = new XMLParser(xml); Stack<Object[]> stack = new Stack<Object[]>(); boolean rootElement = false; while (true) { int event = parser.next(); if (event == XMLParser.END_DOCUMENT) { break; } else if (event == XMLParser.START_ELEMENT) { if (stack.size() == 0) { if (rootElement) { throw new Exception("Second root element at " + parser.getRemaining()); } rootElement = true; } String name = parser.getName(); if (html) { for (String n : noClose) { if (name.equals(n)) { name = null; break; } } } if (name != null) { stack.add(new Object[] { name, parser.getPos() }); } } else if (event == XMLParser.END_ELEMENT) { String name = parser.getName(); if (html) { for (String n : noClose) { if (name.equals(n)) { throw new Exception("Unnecessary closing element " + name + " at " + parser.getRemaining()); } } } while (true) { Object[] pop = stack.pop(); String p = (String) pop[0]; if (p.equals(name)) { break; } String remaining = xml.substring((Integer) pop[1]); if (remaining.length() > 100) { remaining = remaining.substring(0, 100); } throw new Exception("Unclosed element " + p + " at " + remaining); } } else if (event == XMLParser.CHARACTERS) { // lastElement = parser.getText(); } else if (event == XMLParser.DTD) { // ignore } else if (event == XMLParser.COMMENT) { // ignore } else { int eventType = parser.getEventType(); throw new Exception("Unexpected event " + eventType + " at " + parser.getRemaining()); } } if (stack.size() != 0) { throw new Exception("Unclosed root element"); } } }