Java tutorial
/* * Copyright (c) 2003- Shinji Kashihara. All rights reserved. * This program are made available under the terms of the Common Public License * v1.0 which accompanies this distribution, and is available at cpl-v10.html. */ package mergedoc.core; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; /** * Javadoc API ?? * @author Shinji Kashihara */ public class APIDocument { /** */ private static final Log log = LogFactory.getLog(APIDocument.class); /** ?????? */ private final Map<Signature, Comment> contextTable = new HashMap<Signature, Comment>(); /** * see link ??? * <pre> * group(1): URL * group(2): * </pre> */ private static final Pattern linkClassPattern = PatternCache.getPattern( "(?si)<A\\s+HREF=\"(?!ftp)(?!.*package-summary)(?!.*serialized-form)([^\"]+)\"[^>]*><CODE>(.+?)</CODE></A>"); /** * ?? * @param docDir API * @param className ?? * @param charsetName ?? * @throws IOException ???? */ public APIDocument(File docDir, String className, String charsetName) throws IOException { // API ? StringBuilder path = new StringBuilder(); path.append(docDir.getPath()); path.append(File.separator); path.append(className.replace('.', File.separatorChar)); path.append(".html"); // API ? File docFile = new CachedFile(path.toString()); load(docDir, docFile, charsetName); // API ? // prefix ???? PatternCache ???? String prefix = FastStringUtils.replaceFirst(docFile.getName(), "\\.html$", ""); Pattern innerClass = Pattern.compile(prefix + "\\..+\\.html$"); for (File f : docFile.listFiles()) { if (innerClass.matcher(f.getName()).matches()) { load(docDir, f, charsetName); } } } /** * ???<br> * ???? */ private static class CachedFile extends File { private static File cachedDir; private static File[] cachedFiles; private static final File[] EMPTY_FILES = new File[0]; public CachedFile(String path) { super(path); } @Override public File[] listFiles() { File dir = getParentFile(); if (!dir.equals(cachedDir)) { cachedDir = dir; cachedFiles = dir.listFiles(); if (cachedFiles == null) { cachedFiles = EMPTY_FILES; } } return cachedFiles; } } /** * API HTML ???? * @param docDir API * @param docFile API * @param charsetName ?? * @throws IOException ???? */ private void load(File docDir, File docFile, String charsetName) throws IOException { // ???????? if (!docFile.exists()) { return; } // API ?? InputStream is = new FileInputStream(docFile); byte[] buf = new byte[is.available()]; is.read(buf); is.close(); String docHtml = new String(buf, charsetName); docHtml = FastStringUtils.optimizeLineSeparator(docHtml); docHtml = docHtml.replace('\t', ' '); // WAVE DASH ?? char wavaDash = (char) Integer.decode("0x301c").intValue(); docHtml = docHtml.replace(wavaDash, ''); // API ???? String className = FastStringUtils.replaceFirst(docFile.getPath(), "\\.html$", ""); className = className.replace(docDir.getPath() + File.separator, ""); //Pattern??? className = className.replace(File.separatorChar, '.'); // StringBuffer?StringBuilder ???? if (className.equals("java.lang.StringBuffer") || className.equals("java.lang.StringBuilder")) { docHtml = docHtml.replace("%20", ""); } // API ?? Document doc = Jsoup.parse(docHtml); doc.outputSettings().prettyPrint(false); parseClassComment(className, doc); parseMethodComment(className, doc); } /** * ????? * @return ???? true */ public boolean isEmpty() { return contextTable.isEmpty(); } /** * ?????? Javadoc ???? * @param signature ?? * @return Javadoc */ public Comment getComment(Signature signature) { Comment comment = contextTable.get(signature); return comment; } /** * ? Javadoc ???? * author, version ? Javadoc ???????????<br> * @param className ?? * @param docHtml API */ private void parseClassComment(String className, Document doc) { Elements elements = doc.select("body > div.contentContainer > div.description > ul > li"); for (Element element : elements) { String sigStr = element.select("pre").first().html(); Signature sig = createSignature(className, sigStr); Comment comment = new Comment(sig); // deprecated String depre = ""; Elements divs = element.select("div"); if (divs.size() == 2) { depre = divs.get(0).html(); } parseDeprecatedTag(className, depre, comment); // if (divs.size() > 0) { String body = divs.last().html(); body = formatLinkTag(className, body); comment.setDocumentBody(body); } // parseCommonTag(className, element, comment); log.debug(sig); contextTable.put(sig, comment); } } /** * ? Javadoc ???? * @param className ?? * @param docHtml API */ private void parseMethodComment(String className, Document doc) { Elements elements = doc.select("body > div.contentContainer > div.details > ul > li > ul > li > ul > li"); for (Element element : elements) { Element sigElm = element.select("pre").first(); if (sigElm == null) { continue; } String sigStr = sigElm.html(); Signature sig = createSignature(className, sigStr); Comment comment = new Comment(sig); // deprecated String depre = ""; Elements divs = element.select("div"); if (divs.size() == 2) { depre = divs.get(0).html(); } if (divs.size() > 0) { String body = divs.last().html(); body = formatLinkTag(className, body); comment.setDocumentBody(body); } Elements dtTags = element.select("dl dt"); for (Element dtTag : dtTags) { String dtText = dtTag.text(); if (dtText.contains(":")) { Element dd = dtTag; while (true) { dd = dd.nextElementSibling(); if (dd == null || dd.tagName().equalsIgnoreCase("dd") == false) { break; } String name = dd.select("code").first().text(); if (dtText.contains(":")) { name = "<" + name + ">"; } String items = dd.html(); Pattern p = PatternCache .getPattern("(?si)<CODE>(.+?)</CODE>\\s*-\\s*(.*?)(<DD>|</DD>|</DL>|<DT>|$)"); Matcher m = p.matcher(items); if (m.find()) { String desc = formatLinkTag(className, m.group(2)); comment.addParam(name, desc); } } continue; } if (dtText.contains(":")) { Element dd = dtTag.nextElementSibling(); String str = dd.html(); str = formatLinkTag(className, str); comment.addReturn(str); continue; } if (dtText.contains(":")) { Element dd = dtTag; while (true) { dd = dd.nextElementSibling(); if (dd == null || dd.tagName().equalsIgnoreCase("dd") == false) { break; } String name = dd.select("code").first().text(); String items = dd.html(); Pattern p = PatternCache .getPattern("(?si)<CODE>(.+?)</CODE>\\s*-\\s*(.*?)(<DD>|</DD>|</DL>|<DT>|$)"); Matcher m = p.matcher(items); if (m.find()) { String desc = formatLinkTag(className, m.group(2)); String param = name + " " + desc; comment.addThrows(param); } } continue; } } // deprecated parseDeprecatedTag(className, depre, comment); // parseCommonTag(className, element, comment); contextTable.put(sig, comment); } } /** * ?????? * @param className ?? * @param sig ?? */ private Signature createSignature(String className, String sig) { sig = FastStringUtils.replaceAll(sig, "(?s)<.+?>", " "); // sig = FastStringUtils.replaceAll(sig, "\\ ", " "); // ? sig = FastStringUtils.replaceAll(sig, "\\<", "<"); // sig = FastStringUtils.replaceAll(sig, "\\>", ">"); // sig = FastStringUtils.replaceFirst(sig, "(?s)\\sthrows\\s.*", ""); Signature signature = new Signature(className, sig); return signature; } /** * Javadoc ? ?????? * @param className ?? * @param context * @param comment */ private void parseCommonTag(String className, Element element, Comment comment) { Elements dts = element.select("dl dt"); for (Element dt : dts) { String dtText = dt.text(); if (dtText.contains("")) { Elements aTags = dt.nextElementSibling().select("a:has(code)"); for (Element a : aTags) { String url = a.attr("href"); String ref; if (a.childNodeSize() != 1) { ref = aTags.outerHtml(); } else { ref = formatClassName(className, url); ref = FastStringUtils.replace(ref, "%28", "("); ref = FastStringUtils.replace(ref, "%29", ")"); Pattern methodRefPat = PatternCache.getPattern("-(.*)-$"); Matcher methodRefMat = methodRefPat.matcher(ref); if (methodRefMat.find()) { ref = FastStringUtils.replaceAll(ref, "-(.*)-$", "($1)"); // for Java8 ref = FastStringUtils.replace(ref, "-", ","); // for Java8 ref = FastStringUtils.replace(ref, ":A", "[]"); // for Java8 } } comment.addSee(ref); } } else if (dtText.contains("???:")) { comment.addSince(dt.nextElementSibling().text()); } } } /** * Javadoc ? deprecated ?????? * @param context * @param comment */ private void parseDeprecatedTag(String className, String context, Comment comment) { if (context.contains("?")) { Pattern pat = PatternCache.getPattern("(?si)<span class=\"strong\">?.+?<I>(.+?)</I>"); Matcher mat = pat.matcher(context); if (mat.find()) { String str = mat.group(1); str = formatLinkTag(className, str); comment.addDeprecated(str); } } } /** * HTML ? A Javadoc ? link ???? * <p> * ?? Javadoc ?? Comment ????????? * ?????Javadoc ? see ????????? * ?????? * * @param className ?? * @param html HTML ? A ? * @return Javadoc link */ private String formatLinkTag(String className, String html) { StringBuffer sb = new StringBuffer(); Matcher linkMatcher = linkClassPattern.matcher(html); while (linkMatcher.find()) { String url = linkMatcher.group(1).trim(); String label = linkMatcher.group(2).trim(); String ref = formatClassName(className, url); Pattern methodRefPat = PatternCache.getPattern("-(.*)-$"); Matcher methodRefMat = methodRefPat.matcher(ref); if (methodRefMat.find()) { ref = FastStringUtils.replaceAll(ref, "-(.*)-$", "($1)"); // for Java8 ref = FastStringUtils.replace(ref, "-", ","); // for Java8 ref = FastStringUtils.replace(ref, ":A", "[]"); // for Java8 } StringBuilder link = new StringBuilder(); link.append("{@link "); link.append(ref); if (label.length() > 0) { ref = ref.replace('#', '.'); if (!ref.endsWith(label)) { link.append(" "); link.append(label); } } link.append("}"); linkMatcher.appendReplacement(sb, link.toString()); } linkMatcher.appendTail(sb); html = sb.toString(); return html; } /** * see link ? URL package.class#member ????? * ???? package ???????? class ???? * @param className ?? * @param path * @return package.class#member ?? */ private String formatClassName(String className, String path) { String lastClassName = FastStringUtils.replaceFirst(className, ".+\\.", ""); String packageName = className.replace("." + lastClassName, ""); // Pattern??? String lastClassPrefix = "\\.([A-Z])"; path = FastStringUtils.replace(path, ".html", ""); path = path.replace('/', '.'); path = FastStringUtils.replaceFirst(path, "^\\.*", ""); path = FastStringUtils.replaceAll(path, "java.lang" + lastClassPrefix, "$1"); path = path.replaceAll(packageName + lastClassPrefix, "$1"); // Pattern??? path = path.replaceAll("^" + lastClassName + "#", "#"); // Pattern??? path = path.replaceAll(".package-summary$", ""); return path; } }