mergedoc.core.APIDocument.java Source code

Java tutorial

Introduction

Here is the source code for mergedoc.core.APIDocument.java

Source

/*
 * 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, "\\&nbsp;", " "); // ?
        sig = FastStringUtils.replaceAll(sig, "\\&lt;", "<"); // 
        sig = FastStringUtils.replaceAll(sig, "\\&gt;", ">"); // 
        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;
    }
}