All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.litongjava.tio.boot.paranamer.JavadocParanamer Maven / Gradle / Ivy

There is a newer version: 1.8.6
Show newest version
package com.litongjava.tio.boot.paranamer;

import static java.lang.String.format;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * Accesses Javadocs to extract parameter names. Supports:-
 * 
    *
  • Javadoc in zip file
  • *
  • Javadoc in directory
  • *
  • Javadoc at remote URL
  • *
* * @author Samuel Halliday */ public class JavadocParanamer implements Paranamer { protected interface JavadocProvider { InputStream getRawJavadoc(String canonicalClassName) throws IOException; } private final JavadocProvider provider; /** * @param archiveOrDirectory either a zip archive or base directory of Javadocs. * @throws FileNotFoundException if the parameter or package-list * cannot be found. */ public JavadocParanamer(File archiveOrDirectory) throws IOException { if (!archiveOrDirectory.exists()) throw new FileNotFoundException(archiveOrDirectory.getAbsolutePath()); if (archiveOrDirectory.isDirectory()) provider = new DirJavadocProvider(archiveOrDirectory); else if (archiveOrDirectory.isFile()) provider = new ZipJavadocProvider(archiveOrDirectory); else throw new IllegalArgumentException("neither file nor directory: " + archiveOrDirectory); } /** * @param url base URL of the JavaDocs * @throws FileNotFoundException if the url does not have a * /package-list */ public JavadocParanamer(URL url) throws IOException { this.provider = new UrlJavadocProvider(url); } public String[] lookupParameterNames(AccessibleObject accessible) { return lookupParameterNames(accessible, true); } public String[] lookupParameterNames(AccessibleObject accessible, boolean throwExceptionIfMissing) { if (!(accessible instanceof Member)) throw new IllegalArgumentException(accessible.getClass().getCanonicalName()); try { String javadocFilename = getJavadocFilename((Member) accessible); InputStream stream = provider.getRawJavadoc(javadocFilename); String raw = streamToString(stream); if (accessible instanceof Method) return getMethodParameterNames((Method) accessible, raw); else if (accessible instanceof Constructor) return getConstructorParameterNames((Constructor) accessible, raw); else throw new IllegalArgumentException(accessible.getClass().getCanonicalName()); } catch (IOException e) { if (throwExceptionIfMissing) throw new ParameterNamesNotFoundException(accessible.toString(), e); else return Paranamer.EMPTY_NAMES; } catch (ParameterNamesNotFoundException e) { if (throwExceptionIfMissing) throw e; else return Paranamer.EMPTY_NAMES; } } private String[] getConstructorParameterNames(Constructor cons, String raw) { return getParameterNames(cons, cons.getDeclaringClass().getSimpleName(), cons.getParameterTypes(), raw); } private String[] getMethodParameterNames(Method method, String raw) { return getParameterNames(method, method.getName(), method.getParameterTypes(), raw); } /* * Some example patterns * * File#listFiles(FileFilter filter) File(File parent, String child) * Collections#containsAll(Collection c) ================================= * Java 4, 5 & 6 ------------- listFiles(FileFilter filter) File< * /B>(File parent, String child) * * Java 4 ------ * containsAll(Collection c) * * Java 5 & 6 ---------- * containsAll(Collection<?> c) * * Java 7 ------ listFiles(FileFilter filter) * File< * /strong>(File parent, String child) * containsAll(Collection<?> c) */ @SuppressWarnings("rawtypes") private String[] getParameterNames(AccessibleObject a, String name, Class[] types, String raw) { if (types.length == 0) return new String[0]; StringBuilder regex = new StringBuilder(); regex.append(format(">\\Q%s\\E\\(", name)); for (Class klass : types) { regex.append(format(",?\\s*(?:]+>)?[\\w.]*\\Q%s\\E(?:)?(?:<[^&]+>)? ([^),\\s]+)", klass.getSimpleName())); } regex.append(format("\\)
")); Pattern pattern = Pattern.compile(regex.toString(), Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(raw); if (!matcher.find()) throw new ParameterNamesNotFoundException(a + ", " + regex); String[] names = new String[types.length]; for (int i = 0; i < names.length; i++) names[i] = matcher.group(1 + i).trim(); return names; } //////////// CONVENIENCE METHODS //////////// // to keep dependencies light, we don't have Guava or http-client protected static String getJavadocFilename(Member member) { return getCanonicalName(member.getDeclaringClass()).replace('.', '/') + ".html"; } protected static String getCanonicalName(Class klass) { // doesn't support names of nested classes if (klass.isArray()) return getCanonicalName(klass.getComponentType()) + "[]"; return klass.getName(); } protected static String streamToString(InputStream input) throws IOException { InputStreamReader reader = new InputStreamReader(input, "UTF-8"); BufferedReader buffered = new BufferedReader(reader); try { String line; StringBuilder builder = new StringBuilder(); while ((line = buffered.readLine()) != null) { builder.append(line); builder.append("\n"); } return builder.toString(); } finally { buffered.close(); } } protected static InputStream urlToStream(URL url) throws IOException { URLConnection conn = url.openConnection(); conn.connect(); return conn.getInputStream(); } //////////// Provider Implementations //////////// protected static class ZipJavadocProvider implements JavadocProvider { private final ZipFile zip; public ZipJavadocProvider(File file) throws IOException { zip = new ZipFile(file); find("package-list"); } private ZipEntry find(String postfix) throws FileNotFoundException { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(postfix)) return entry; } throw new FileNotFoundException(postfix); } public InputStream getRawJavadoc(String fqn) throws IOException { ZipEntry entry = find(fqn); return zip.getInputStream(entry); } } protected static class UrlJavadocProvider implements JavadocProvider { private final URL base; public UrlJavadocProvider(URL base) throws IOException { this.base = base; streamToString(urlToStream(new URL(base + "/package-list"))); } public InputStream getRawJavadoc(String fqn) throws IOException { return urlToStream(new URL(base + "/" + fqn)); } } protected static class DirJavadocProvider implements JavadocProvider { private final File dir; public DirJavadocProvider(File dir) throws IOException { this.dir = dir; if (!new File(dir, "package-list").exists()) throw new FileNotFoundException("package-list"); } public InputStream getRawJavadoc(String fqn) throws IOException { File file = new File(dir, fqn); return new FileInputStream(file); } } }