/* * 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.ArrayList; import java.util.Collections; import java.util.HashMap; import org.h2.tools.Server; import org.h2.util.IOUtils; import org.h2.util.StringUtils; /** * The link checker makes sure that each link in the documentation * points to an existing target. */ public class LinkChecker { private static final boolean OPEN_EXTERNAL_LINKS = false; private static final String[] IGNORE_MISSING_LINKS_TO = { "SysProperties", "ErrorCode" }; private HashMap<String, String> targets = new HashMap<String, String>(); private HashMap<String, String> links = new HashMap<String, String>(); /** * 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 LinkChecker().run(args); } private void run(String... args) throws Exception { String dir = "docs"; for (int i = 0; i < args.length; i++) { if ("-dir".equals(args[i])) { dir = args[++i]; } } process(dir); listExternalLinks(); listBadLinks(); } private void listExternalLinks() { for (String link : links.keySet()) { if (link.startsWith("http")) { if (link.indexOf("//localhost") > 0) { continue; } if (OPEN_EXTERNAL_LINKS) { System.out.println(link); try { Server.openBrowser(link); Thread.sleep(100); } catch (Exception e) { // ignore } } } } } private void listBadLinks() throws Exception { ArrayList<String> errors = new ArrayList<String>(); for (String link : links.keySet()) { if (!link.startsWith("http") && !link.endsWith("h2.pdf") && link.indexOf("_ja.") < 0) { if (targets.get(link) == null) { errors.add(links.get(link) + ": Link missing " + link); } } } for (String link : links.keySet()) { if (!link.startsWith("http")) { targets.remove(link); } } for (String name : targets.keySet()) { if (targets.get(name).equals("id")) { boolean ignore = false; for (String to : IGNORE_MISSING_LINKS_TO) { if (name.indexOf(to) >= 0) { ignore = true; break; } } if (!ignore) { errors.add("No link to " + name); } } } Collections.sort(errors); for (String error : errors) { System.out.println(error); } if (errors.size() > 0) { throw new Exception("Problems where found by the Link Checker"); } } 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 n : file.list()) { process(path + "/" + n); } } else { processFile(path); } } private void processFile(String path) throws Exception { targets.put(path, "file"); String lower = StringUtils.toLowerEnglish(path); if (!lower.endsWith(".html") && !lower.endsWith(".htm")) { return; } String fileName = new File(path).getName(); String parent = path.substring(0, path.lastIndexOf('/')); String html = IOUtils.readStringAndClose(new FileReader(path), -1); int idx = -1; while (true) { idx = html.indexOf(" id=\"", idx + 1); if (idx < 0) { break; } int start = idx + " id=\"".length(); int end = html.indexOf("\"", start); if (end < 0) { error(fileName, "Expected \" after id= " + html.substring(idx, idx + 100)); } String ref = html.substring(start, end); if (!ref.startsWith("_")) { targets.put(path + "#" + ref, "id"); } } idx = -1; while (true) { idx = html.indexOf(" href=\"", idx + 1); if (idx < 0) { break; } int start = html.indexOf("\"", idx); if (start < 0) { error(fileName, "Expected \" after href= at " + html.substring(idx, idx + 100)); } int end = html.indexOf("\"", start + 1); if (end < 0) { error(fileName, "Expected \" after href= at " + html.substring(idx, idx + 100)); } String ref = html.substring(start + 1, end); if (ref.startsWith("http:") || ref.startsWith("https:")) { // ok } else if (ref.startsWith("javascript:")) { ref = null; // ok } else if (ref.length() == 0) { ref = null; // ok } else if (ref.startsWith("#")) { ref = path + ref; } else { String p = parent; while (ref.startsWith(".")) { if (ref.startsWith("./")) { ref = ref.substring(2); } else if (ref.startsWith("../")) { ref = ref.substring(3); p = p.substring(0, p.lastIndexOf('/')); } } ref = p + "/" + ref; } if (ref != null) { links.put(ref, path); } } idx = -1; while (true) { idx = html.indexOf("<a ", idx + 1); if (idx < 0) { break; } int equals = html.indexOf("=", idx); if (equals < 0) { error(fileName, "Expected = after <a at " + html.substring(idx, idx + 100)); } String type = html.substring(idx + 2, equals).trim(); int start = html.indexOf("\"", idx); if (start < 0) { error(fileName, "Expected \" after <a at " + html.substring(idx, idx + 100)); } int end = html.indexOf("\"", start + 1); if (end < 0) { error(fileName, "Expected \" after <a at " + html.substring(idx, idx + 100)); } String ref = html.substring(start + 1, end); if (type.equals("href")) { // already checked } else if (type.equals("id")) { targets.put(path + "#" + ref, "id"); } else { error(fileName, "Unsupported <a ?: " + html.substring(idx, idx + 100)); } } } private static void error(String fileName, String string) { System.out.println("ERROR with " + fileName + ": " + string); } }