com.litongjava.tio.boot.paranamer.JavadocParanamer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tio-boot Show documentation
Show all versions of tio-boot Show documentation
Java High Performance Web Development Framework
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
* strong>(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(?:B|strong)>\\(", 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 extends ZipEntry> 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);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy