001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (license2)
004: * Initial Developer: H2 Group
005: */
006: package org.h2.tools.doc;
007:
008: import java.io.File;
009: import java.io.FileReader;
010: import java.io.IOException;
011: import java.io.Reader;
012: import java.io.StringWriter;
013: import java.util.Stack;
014:
015: /**
016: * This class checks that the HTML and XML part of the source code
017: * is well-formed XML.
018: */
019: public class XMLChecker {
020:
021: public static void main(String[] args) throws Exception {
022: new XMLChecker().run(args);
023: }
024:
025: private void run(String[] args) throws Exception {
026: String dir = ".";
027: for (int i = 0; i < args.length; i++) {
028: if ("-dir".equals(args[i])) {
029: dir = args[++i];
030: }
031: }
032: process(dir + "/src");
033: process(dir + "/docs");
034: }
035:
036: void process(String path) throws Exception {
037: if (path.endsWith("/CVS") || path.endsWith("/.svn")) {
038: return;
039: }
040: File file = new File(path);
041: if (file.isDirectory()) {
042: String[] list = file.list();
043: for (int i = 0; i < list.length; i++) {
044: process(path + "/" + list[i]);
045: }
046: } else {
047: processFile(path);
048: }
049: }
050:
051: void processFile(String fileName) throws Exception {
052: int idx = fileName.lastIndexOf('.');
053: if (idx < 0) {
054: return;
055: }
056: String suffix = fileName.substring(idx + 1);
057: if (!suffix.equals("html") && !suffix.equals("xml")
058: && !suffix.equals("jsp")) {
059: return;
060: }
061: // System.out.println("Checking file:" + fileName);
062: FileReader reader = new FileReader(fileName);
063: String s = readStringAndClose(reader, -1);
064: Exception last = null;
065: try {
066: checkXML(s, !suffix.equals("xml"));
067: } catch (Exception e) {
068: last = e;
069: System.out.println("ERROR in file " + fileName + " "
070: + e.toString());
071: }
072: if (last != null) {
073: last.printStackTrace();
074: }
075: }
076:
077: public static String readStringAndClose(Reader in, int length)
078: throws IOException {
079: if (length <= 0) {
080: length = Integer.MAX_VALUE;
081: }
082: int block = Math.min(4096, length);
083: StringWriter out = new StringWriter(
084: length == Integer.MAX_VALUE ? block : length);
085: char[] buff = new char[block];
086: while (length > 0) {
087: int len = Math.min(block, length);
088: len = in.read(buff, 0, len);
089: if (len < 0) {
090: break;
091: }
092: out.write(buff, 0, len);
093: length -= len;
094: }
095: in.close();
096: return out.toString();
097: }
098:
099: private static void checkXML(String xml, boolean html)
100: throws Exception {
101: // String lastElement = null;
102: // <li>: replace <li>([^\r]*[^<]*) with <li>$1</li>
103: // use this for html file, for example if <li> is not closed
104: String[] noClose = new String[] {};
105: XMLParser parser = new XMLParser(xml);
106: Stack stack = new Stack();
107: boolean rootElement = false;
108: while (true) {
109: int event = parser.next();
110: if (event == XMLParser.END_DOCUMENT) {
111: break;
112: } else if (event == XMLParser.START_ELEMENT) {
113: if (stack.size() == 0) {
114: if (rootElement) {
115: throw new Exception("Second root element at "
116: + parser.getRemaining());
117: }
118: rootElement = true;
119: }
120: String name = parser.getName();
121: for (int i = 0; html && i < noClose.length; i++) {
122: if (name.equals(noClose[i])) {
123: name = null;
124: break;
125: }
126: }
127: if (name != null) {
128: stack.add(new Object[] { name,
129: new Integer(parser.getPos()) });
130: }
131: } else if (event == XMLParser.END_ELEMENT) {
132: String name = parser.getName();
133: for (int i = 0; html && i < noClose.length; i++) {
134: if (name.equals(noClose[i])) {
135: throw new Exception(
136: "Unnecessary closing element " + name
137: + " at "
138: + parser.getRemaining());
139: }
140: }
141: while (true) {
142: Object[] pop = (Object[]) stack.pop();
143: String p = (String) pop[0];
144: if (p.equals(name)) {
145: break;
146: }
147: String remaining = xml.substring(((Integer) pop[1])
148: .intValue());
149: if (remaining.length() > 100) {
150: remaining = remaining.substring(0, 100);
151: }
152: throw new Exception("Unclosed element " + p
153: + " at " + remaining);
154: }
155: } else if (event == XMLParser.CHARACTERS) {
156: // lastElement = parser.getText();
157: } else if (event == XMLParser.DTD) {
158: } else if (event == XMLParser.COMMENT) {
159: } else {
160: int eventType = parser.getEventType();
161: throw new Exception("Unexpected event " + eventType
162: + " at " + parser.getRemaining());
163: }
164: }
165: if (stack.size() != 0) {
166: throw new Exception("Unclosed root element");
167: }
168: }
169:
170: }
|