org.netlib.generate.JavaGenerator Maven / Gradle / Ivy
Show all versions of netlib-java Show documentation
/*
* Copyright ThinkTank Maths Limited 2006, 2007
*
* This file is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this file.
* If not, see .
*
* For the avoidance of doubt, source code generated by this program is not considered
* a derivative work.
*/
package org.netlib.generate;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.netlib.util.StringW;
import org.netlib.util.booleanW;
import org.netlib.util.doubleW;
import org.netlib.util.floatW;
import org.netlib.util.intW;
/**
* Due to the depressing number of LAPACK routines, it is much more efficient to
* auto-generate the Java code for the wrapper and corresponding Java and JNI
* implementations.
*
* Warning: this code is very monolithic and horrible. It was written in a hurry for a
* one-off run. It is probably very hard to understand and not at all elegant. Efforts
* have been best spent on making sure the output code is elegant as it is the stuff that
* actually matters.
*
* @author Samuel Halliday
*/
class JavaGenerator {
class Doublet {
public A a;
public B b;
public Doublet(A a, B b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return a + " " + b;
}
}
interface IClassFilter {
/**
* @param className
* @return true if the class with the given name is acceptable.
*/
public boolean isValid(String className);
}
private static final Map classDefs =
new HashMap();
static {
/*
* The complete(ish) list of parameter types for F2J methods. Typically an array
* is followed by an int specifying the offset.
*/
classDefs.put(String.class, "String");
classDefs.put(Integer.TYPE, "int");
classDefs.put(Double.TYPE, "double");
classDefs.put(Boolean.TYPE, "boolean");
classDefs.put(Float.TYPE, "float");
classDefs.put(intW.class, "intW");
classDefs.put(doubleW.class, "doubleW");
classDefs.put(booleanW.class, "booleanW");
classDefs.put(floatW.class, "floatW");
classDefs.put(StringW.class, "StringW");
classDefs.put(int[].class, "int[]");
classDefs.put(double[].class, "double[]");
classDefs.put(boolean[].class, "boolean[]");
classDefs.put(float[].class, "float[]");
}
public static void main(String[] args) throws Exception {
// create the BLAS wrapper
JavaGenerator blas =
new JavaGenerator("org.netlib.blas", "BLAS",
"lib/f2j/jlapack-0.8-javadoc.zip");
writeToFile(blas.getAbstractWrapper(), "src/org/netlib/blas/BLAS.java");
writeToFile(blas.getJavaWrapper(), "src/org/netlib/blas/JBLAS.java");
writeToFile(blas.getJNIWrapper(), "src/org/netlib/blas/NativeBLAS.java");
writeToFile(blas.getJNIC(), "jni/org_netlib_blas_NativeBLAS.c");
// create the LAPACK wrapper
JavaGenerator lapack =
new JavaGenerator("org.netlib.lapack", "LAPACK",
"lib/f2j/jlapack-0.8-javadoc.zip");
writeToFile(lapack.getAbstractWrapper(),
"src/org/netlib/lapack/LAPACK.java");
writeToFile(lapack.getJavaWrapper(),
"src/org/netlib/lapack/JLAPACK.java");
writeToFile(lapack.getJNIWrapper(),
"src/org/netlib/lapack/NativeLAPACK.java");
writeToFile(lapack.getJNIC(), "jni/org_netlib_lapack_NativeLAPACK.c");
// create the ARPACK wrapper
// TODO: add the ARPACK javadocs here
JavaGenerator arpack =
new JavaGenerator("org.netlib.arpack", "ARPACK", "");
writeToFile(arpack.getAbstractWrapper(),
"src/org/netlib/arpack/ARPACK.java");
writeToFile(arpack.getJavaWrapper(),
"src/org/netlib/arpack/JARPACK.java");
writeToFile(arpack.getJNIWrapper(),
"src/org/netlib/arpack/NativeARPACK.java");
writeToFile(arpack.getJNIC(), "jni/org_netlib_arpack_NativeARPACK.c");
}
static void writeToFile(String string, String filename) throws IOException {
FileOutputStream out = new FileOutputStream(filename);
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
writer.write(string);
writer.close();
}
/**
* The generated files are licenced under the BSD licence, the same as the netlib
* sources.
*/
private final String COPYRIGHT =
"/*\n * Copyright 2003-2007 Keith Seymour.\n"
+ " * Copyright 1992-2007 The University of Tennessee. All rights reserved.\n"
+ " * \n"
+ " * Redistribution and use in source and binary forms, with or without\n"
+ " * modification, are permitted provided that the following conditions are\n"
+ " * met:\n"
+ " * \n"
+ " * - Redistributions of source code must retain the above copyright\n"
+ " * notice, this list of conditions and the following disclaimer.\n"
+ " * \n"
+ " * - Redistributions in binary form must reproduce the above copyright\n"
+ " * notice, this list of conditions and the following disclaimer listed\n"
+ " * in this license in the documentation and/or other materials\n"
+ " * provided with the distribution.\n"
+ " * \n"
+ " * - Neither the name of the copyright holders nor the names of its\n"
+ " * contributors may be used to endorse or promote products derived from\n"
+ " * this software without specific prior written permission.\n"
+ " * \n"
+ " * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+ " * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+ " * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+ " * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+ " * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+ " * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+ " * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+ " * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+ " * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+ " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+ " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+ " * \n" + " * This file was auto-generated by the "
+ JavaGenerator.class.getCanonicalName()
+ "\n * program, a part of netlib-java.\n * \n"
+ " * @see http://code.google.com/p/netlib-java/\n" + " */\n";
private final String javadocs;
private final String javaWrapper;
private final String jniC;
private final String jniWrapper;
private final String pkg;
private String post;
private String pre;
private final String topWrapper;
private final String wrapperName;
/**
* Some F2J methods take parameters that I don't know how to deal with in the JNI, so
* we'll simply make sure the wrapper always sends them to the F2J implementation.
*/
private final Set dontUseJNI = new HashSet();
/**
* @param packageName
* @param wrapperName
* @param javadocs
* @throws IOException
*/
JavaGenerator(String packageName, String wrapperName, String javadocs)
throws IOException {
pkg = packageName;
this.javadocs = javadocs;
this.wrapperName = wrapperName;
// hack for fortran/C names. BLAS is different to the other netlib libs
if (wrapperName.equals("BLAS")) {
pre = "cblas_";
post = "";
} else {
pre = "";
post = "_";
}
List> classes = getClasses(pkg, new IClassFilter() {
public boolean isValid(String className) {
assert className != null;
assert className.startsWith(pkg);
String shortName = className.substring(pkg.length() + 1);
if (shortName.toUpperCase().equals(shortName))
// all caps mean the "convenience" F2J libs
// or the wrapper class we are trying to create!
return false;
if (shortName.startsWith("Native"))
// these are the JNI classes
return false;
if (shortName.contains("$"))
// inner classes
return false;
if (shortName.endsWith("Test"))
// test cases
return false;
return true;
}
});
List methods = new ArrayList();
for (Class> clazz : classes) {
Method[] ms = clazz.getDeclaredMethods();
for (Method m : ms) {
// F2J methods have the same name as their containing class
String name = m.getName();
String className = clazz.getSimpleName();
if (!name.equals(className.toLowerCase()))
continue;
methods.add(m);
break;
}
}
StringBuilder topWrapper = new StringBuilder();
StringBuilder javaWrapper = new StringBuilder();
StringBuilder jniWrapper = new StringBuilder();
StringBuilder jniC = new StringBuilder();
topWrapper.append(COPYRIGHT);
javaWrapper.append(COPYRIGHT);
jniWrapper.append(COPYRIGHT);
jniC.append(COPYRIGHT);
topWrapper.append("package " + pkg + ";\n\n");
topWrapper.append("import java.util.logging.Logger;\n");
topWrapper.append("import org.netlib.util.StringW;\n");
topWrapper.append("import org.netlib.util.booleanW;\n");
topWrapper.append("import org.netlib.util.doubleW;\n");
topWrapper.append("import org.netlib.util.floatW;\n");
topWrapper.append("import org.netlib.util.intW;\n\n");
topWrapper.append("/**\n");
topWrapper.append(" * "
+ wrapperName
+ " provider which will attempt to access a native implementation\n");
topWrapper.append(" * and falling back to use F2J if none is available.\n *\n");
topWrapper.append(" * @see http://sourceforge.net/projects/f2j\n");
topWrapper.append(" * @see http://www.netlib.org/"
+ wrapperName.toLowerCase() + "/\n");
topWrapper.append(" * @author Samuel Halliday\n");
topWrapper.append(" */\n");
topWrapper.append("public abstract class " + wrapperName + " {\n\n");
topWrapper.append(topWrapperLoader(wrapperName));
javaWrapper.append("package " + pkg + ";\n\n");
javaWrapper.append("import java.util.logging.Logger;\n");
javaWrapper.append("import org.netlib.util.StringW;\n");
javaWrapper.append("import org.netlib.util.booleanW;\n");
javaWrapper.append("import org.netlib.util.doubleW;\n");
javaWrapper.append("import org.netlib.util.floatW;\n");
javaWrapper.append("import org.netlib.util.intW;\n\n");
javaWrapper.append("/**\n");
javaWrapper.append(" * " + wrapperName
+ " provider implementation which uses F2J.\n *\n");
javaWrapper.append(" * @see http://sourceforge.net/projects/f2j\n");
javaWrapper.append(" * @author Samuel Halliday\n");
javaWrapper.append(" */\n");
javaWrapper.append("final class J" + wrapperName + " extends "
+ wrapperName + " {\n\n");
javaWrapper.append("\tstatic final " + wrapperName
+ " INSTANCE = new J" + wrapperName + "();\n\n");
javaWrapper.append("\tprivate J" + wrapperName + "() {\n");
javaWrapper.append("\t}\n\n");
jniWrapper.append("package " + pkg + ";\n\n");
jniWrapper.append("import java.util.logging.Logger;\n");
jniWrapper.append("import org.netlib.util.StringW;\n");
jniWrapper.append("import org.netlib.util.booleanW;\n");
jniWrapper.append("import org.netlib.util.doubleW;\n");
jniWrapper.append("import org.netlib.util.floatW;\n");
jniWrapper.append("import org.netlib.util.intW;\n");
jniWrapper.append("import org.netlib.utils.JNIMethods;\n\n");
jniWrapper.append("/**\n");
jniWrapper.append(" * "
+ wrapperName
+ " provider implementation which uses the Java Native Interface to access\n");
jniWrapper.append(" * system netlib libraries.\n *\n");
jniWrapper.append(" * @see http://www.netlib.org/\n");
jniWrapper.append(" * @author Samuel Halliday\n");
jniWrapper.append(" */\n");
jniWrapper.append("final class Native" + wrapperName + " extends "
+ wrapperName + " {\n\n");
jniWrapper.append(jniWrapperLoader(wrapperName));
jniC.append("\n#include \"f2j_jni.h\"\n");
jniC.append("#include \"" + pkg.toLowerCase().replace(".", "_")
+ "_Native" + wrapperName + ".h\"\n\n");
for (Method method : methods) {
System.out.println("Generating " + method.getName());
Type[] params = method.getGenericParameterTypes();
for (Type param : params) {
if (classDefs.containsKey(param))
continue;
System.err.println(method.getName() + " has a " + param
+ " parameter, so we'll not generate a JNI");
dontUseJNI.add(method.getName());
}
String[] wrapper = createWrapper(method);
topWrapper.append(wrapper[0]);
javaWrapper.append(wrapper[1]);
jniWrapper.append(wrapper[2]);
jniC.append(wrapper[3]);
}
topWrapper.append("}\n");
javaWrapper.append("}\n");
jniWrapper.append("}\n");
// System.out.print(topWrapper);
this.topWrapper = topWrapper.toString();
this.javaWrapper = javaWrapper.toString();
this.jniWrapper = jniWrapper.toString();
this.jniC = jniC.toString();
}
/**
* @return the Java source code for the top level abstract class for the package
*/
public String getAbstractWrapper() {
return topWrapper;
}
/**
* @return the Java source code for the F2J delegate class
*/
public String getJavaWrapper() {
return javaWrapper;
}
/**
* @return the C source code for the JNI code
*/
public String getJNIC() {
return jniC;
}
/**
* @return the Java source code for the JNI delegate class
*/
public String getJNIWrapper() {
return jniWrapper;
}
/**
* @param s
* @return a capitalised version of the input
*/
private String capitalize(String s) {
if (s.length() == 0)
return s;
return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
}
/**
* @param method
* @param typeAndName
* @return
*/
private String createJavaWrapper(Method method,
List> typeAndName) {
// no need for javadocs
StringBuilder builder = new StringBuilder();
builder.append("\t@Override\n");
builder.append("\tpublic "
+ method.getReturnType().getName().toLowerCase() + " "
+ method.getName() + "(");
for (int i = 0; i < typeAndName.size(); i++) {
Doublet tm = typeAndName.get(i);
String type = tm.a;
String name = tm.b;
builder.append(type + " " + name);
if (i != typeAndName.size() - 1)
builder.append(", ");
}
builder.append(") {\n\t\t");
if (!method.getReturnType().equals(Void.TYPE))
builder.append("return ");
String n = method.getName();
builder.append(pkg + "." + capitalize(n) + "." + n + "(");
for (int i = 0; i < typeAndName.size(); i++) {
Doublet tm = typeAndName.get(i);
String type = tm.a;
String name = tm.b;
builder.append(name);
if (type.contains("[]")) {
// offset is always zero
builder.append(", 0");
}
if (i != typeAndName.size() - 1)
builder.append(", ");
}
builder.append(");\n\t}\n\n");
return builder.toString();
}
/**
* @param method
* @param typeAndName
* @return
*/
private String createJNICode(Method method,
List> typeAndName) {
List names = new ArrayList();
List types = new ArrayList();
for (Doublet tn : typeAndName) {
types.add(tn.a);
names.add(tn.b);
}
String name = pkg + ".Native" + wrapperName + "." + method.getName();
String rtn = method.getReturnType().getName().toLowerCase();
// cblas hack
String pre = this.pre;
String post = this.post;
if (wrapperName.equals("BLAS") && method.getName().equals("lsame")) {
pre = "";
post = "_";
}
JNIGenerator jni =
new JNIGenerator(pre, post, name, types, names, rtn,
wrapperName.equals("BLAS") ? true : false);
return jni.getTemplate() + "\n";
}
/**
* @param method
* @param typeAndName
* @return
*/
private String createJNIWrapper(Method method,
List> typeAndName) {
// no need for javadocs
StringBuilder builder = new StringBuilder();
builder.append("\t@Override\n");
builder.append("\tpublic native "
+ method.getReturnType().getName().toLowerCase() + " "
+ method.getName() + "(");
for (int i = 0; i < typeAndName.size(); i++) {
Doublet tm = typeAndName.get(i);
String type = tm.a;
String name = tm.b;
builder.append(type + " " + name);
if (i != typeAndName.size() - 1)
builder.append(", ");
}
builder.append(");\n\n");
return builder.toString();
}
/**
* @param javadocs
* @param typeAndName
* @return
*/
private String createTopJavaDocs(String javadocs,
List> typeAndName) {
StringBuilder builder = new StringBuilder();
builder.append("\t/**\n");
builder.append(javadocs);
for (int i = 0; i < typeAndName.size(); i++) {
builder.append("\t * @param " + typeAndName.get(i).b + "\n");
}
builder.append("\t */\n");
return builder.toString();
}
/**
* @param method
* @param typeAndName
* @param javadocs
* @return
*/
private String createTopWrapper(Method method,
List> typeAndName, String javadocs) {
StringBuilder builder = new StringBuilder();
builder.append(createTopJavaDocs(javadocs, typeAndName));
builder.append("\tpublic abstract "
+ method.getReturnType().toString().toLowerCase() + " "
+ method.getName() + "(");
for (int i = 0; i < typeAndName.size(); i++) {
builder.append(typeAndName.get(i).a + " " + typeAndName.get(i).b);
if (i != typeAndName.size() - 1)
builder.append(", ");
}
builder.append(");\n\n");
return builder.toString();
}
/**
* A list of methods not defined in headers on OS X Leopard or Ubuntu Gutsy. These are
* most likely subroutines that should not be a part of the API, but we leave them in
* anyway.
*/
static final Set notSupportedByJNI =
new HashSet(Arrays.asList(
/* These are BLAS/LAPACK not defined on OS X Leopard */
"disnan", "dlacn2", "dlag2s", "dlahr2", "dlaisnan", "dlaneg",
"dlaqr0", "dlaqr1", "dlaqr2", "dlaqr3", "dlaqr4", "dlaqr5",
"dlarra", "dlarrc", "dlarrd", "dlarrj", "dlarrk", "dlarrr",
"dlazq3", "dlazq4", "dsgesv", "dstemr", "ilaver", "iparmq",
"sisnan", "slag2d", "slahr2", "slaisnan", "slaneg", "slaqr0",
"slaqr1", "slaqr2", "slaqr3", "slaqr4", "slaqr5", "slarra",
"slarrc", "slarrj", "slarrr", "sstemr", "slacn2", "slarrd",
"slarrk", "slazq3", "slazq4",
/* These are BLAS not defined on Ubuntu Gutsy (LAPACK 3.0) */
"lsame",
/*
* These are LAPACK and not defined in LAPACK 3.0
* http://www.netlib.org/clapack/clapack.h
*/
"dlangb", "dlange", "dlangt", "dlanhs", "dlansb", "dlansp",
"dlanst", "dlansy", "dlantb", "dlantp", "dlantr", "dlapy2",
"dlapy3", "lsamen", "slangb", "slange", "slangt", "slanhs",
"slansb", "slansp", "slanst", "slansy", "slantb", "slantp",
"slantr", "slapy2", "slapy3", "dlamc3", "dsecnd", "second",
"slamch", "slamc3",
/*
* The following is not defined in clapack.h bet really should be!
* So we're leaving it in and we'll suffer the compile time warning.
*/
// "dlamch",
/* these are not defined in the ARPACK headers */
"dmout", "dvout", "icnteq", "icopy", "iset", "iswap", "ivout",
"smout", "svout", "dgetv0", "dlaqrb", "dnaitr", "dnapps",
"dnaup2", "dnconv", "dneigh", "dngets", "dsaitr", "dsapps",
"dsaup2", "dsconv", "dseigt", "dsesrt", "dsgets", "dsortc",
"dsortr", "dstatn", "dstats", "dstqrb", "sgetv0", "slaqrb",
"snaitr", "snapps", "snaup2", "snconv", "sneigh", "sngets",
"ssaitr", "ssapps", "ssaup2", "ssconv", "sseigt", "ssesrt",
"ssgets", "ssortc", "ssortr", "sstatn", "sstats", "sstqrb"));
/** A list of methods where F2J has a different signature than in OS X Leopard */
static final List incompatibleWithJni =
Arrays.asList("dlar1v", "dlarrb", "dlarre", "dlarrf", "dlarrv",
"slar1v", "slarrb", "slarre", "slarrf", "slarrv");
/**
* @param method
* @return the 4D array of wrappers. The first is the abstract portion, the second is
* the F2J wrapper, the third is the JNI wrapper and the forth is the C JNI
* code.
* @throws IOException
*/
private String[] createWrapper(Method method) throws IOException {
String name = method.getName();
String[] parts = new String[4];
Map fromDocs = getParameterNames(method);
String javadocs = fromDocs.keySet().iterator().next();
String[] names = fromDocs.values().iterator().next();
Class>[] paramTypes = method.getParameterTypes();
List> typeAndName =
typeAndName(paramTypes, names);
// some exceptional methods
if (notSupportedByJNI.contains(name)
|| incompatibleWithJni.contains(name)
|| dontUseJNI.contains(name)) {
String javaWrapper = createJavaWrapper(method, typeAndName);
String javaDocs = createTopJavaDocs(javadocs, typeAndName);
parts[0] = javaWrapper.replace("@Override\n", javaDocs);
System.err.println("Forcing F2J for " + name);
parts[1] = "";
parts[2] = "";
parts[3] = "";
return parts;
}
parts[0] = createTopWrapper(method, typeAndName, javadocs);
parts[1] = createJavaWrapper(method, typeAndName);
parts[2] = createJNIWrapper(method, typeAndName);
parts[3] = createJNICode(method, typeAndName);
return parts;
}
/**
* This convenience method will examine the classpath and find any classes which are
* in the requested package. A filter can be specified to exclude results.
*
* @param packageName
* @param filter
* @return all classes in a given package
* @see http://forum.java.sun.com/thread.jspa?threadID=757391&messageID=4326850
*/
private List> getClasses(String packageName, IClassFilter filter) {
String packagePath = packageName.replace('.', '/');
// ArrayList classpath = new ArrayList();
// String[] classpathString = System.getProperty("java.class.path").split(":");
// for (int i = 0 ; i < classpathString.length ; i++){
// if (classpathString[i] == null)
// continue;
// try {
// URL url = new URL("file:" + classpathString[i]);
// classpath.add(url);
// } catch (MalformedURLException ex) {
// Logger.getLogger(JavaGenerator.class.getName()).
// log(Level.SEVERE, classpathString[i] + " " + ex.getMessage());
// }
// }
URL [] classpath = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
List> result = new ArrayList>();
System.out.println(Arrays.toString(classpath));
for (URL url : classpath) {
File file;
try {
file = new File(url.toURI());
} catch (URISyntaxException e1) {
continue;
}
if (file.getPath().endsWith(".jar")) {
// class path is a jar file
JarFile jarFile;
try {
jarFile = new JarFile(file);
} catch (IOException e) {
continue;
}
for (Enumeration entries = jarFile.entries(); entries.hasMoreElements();) {
String entryName = (entries.nextElement()).getName();
if (entryName.matches(packagePath + "/\\w*\\.class")) {
// get only class files in package dir
ClassLoader classLoader =
new URLClassLoader(new URL[] { url });
String className =
entryName.replace('/', '.').substring(0,
entryName.lastIndexOf('.'));
if (!filter.isValid(className))
continue;
Class> clazz;
try {
clazz = classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
continue;
}
result.add(clazz);
}
}
} else {
// class path is a directory
File packageDirectory =
new File(file.getPath() + "/" + packagePath);
for (File f : packageDirectory.listFiles()) {
if (f.getPath().endsWith(".class")) {
String className =
packageName
+ "."
+ f.getName().substring(0,
f.getName().lastIndexOf('.'));
if (!filter.isValid(className))
continue;
ClassLoader classLoader =
new URLClassLoader(new URL[] { url });
Class> clazz;
try {
clazz = classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
continue;
}
result.add(clazz);
}
}
}
}
return result;
}
/**
* Note that we cannot use a library like Paranamer here because the information is
* not in the bytecode. We look in the Javadocs!
*
* @param method
* @return a singleton map from the javadoc description to the parameter names of the
* method, in order. (Yes, I know this is a hack)
* @throws IOException
*/
private Map getParameterNames(Method method)
throws IOException {
ZipInputStream in;
try {
in = new ZipInputStream(new FileInputStream(javadocs));
} catch (FileNotFoundException e) {
// no javadocs available, just return arg1 ... argN
int size = method.getGenericParameterTypes().length;
String[] names = new String[size];
for (int i = 0; i < size; i++) {
names[i] = "arg" + (i + 1);
}
String docs =
"\t * No documentation was available when generating this method.\n\t * \n";
return Collections.singletonMap(docs, names);
}
ZipEntry entry;
// deal with the capitalisation of LAPACK classes... clashes there
String m = method.getName();
String p =
pkg.replace(".", "/") + "/" + m.substring(0, 1).toUpperCase()
+ m.substring(1) + ".html";
try {
while ((entry = in.getNextEntry()) != null) {
String name = entry.getName();
if (name.endsWith(p)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Transfer bytes from the ZIP file to the output stream
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
String javadoc = new String(out.toByteArray());
out.close();
String[] names = parseParameterNames(method, javadoc);
String documentation =
parseJavadocDescription(method, javadoc);
return Collections.singletonMap(documentation, names);
}
}
throw new RuntimeException("getParameterNames failed");
} finally {
in.close();
}
}
private String jniWrapperLoader(String wrapperName) {
StringBuilder b = new StringBuilder();
b.append("\t// singleton\n");
b.append("\tprotected static final Native" + wrapperName
+ " INSTANCE = new Native" + wrapperName + "();\n\n");
b.append("\t// indicates if the JNI loaded OK. If this is false, calls to the native\n");
b.append("\t// methods will fail with UnsatisfiedLinkError\n");
b.append("\tprotected final boolean isLoaded;\n\n");
b.append("\tprivate Native" + wrapperName + "() {\n");
b.append("\t\tString libname = JNIMethods.getPortableLibraryName(\"jni"
+ wrapperName.toLowerCase() + "\");\n");
b.append("\t\ttry {\n");
b.append("\t\t\tSystem.loadLibrary(libname);\n");
b.append("\t\t} catch (UnsatisfiedLinkError e) {\n");
b.append("\t\t\tisLoaded = false;\n");
b.append("\t\t\treturn;\n");
b.append("\t\t}\n");
b.append("\t\tisLoaded = true;\n");
b.append("\t}\n\n");
return b.toString();
}
/**
* @param method
* @param javadoc
* @return
*/
private String parseJavadocDescription(Method method, String javadoc) {
Pattern pattern =
Pattern.compile("[email protected] with any questions.\n
");
Matcher matcher = pattern.matcher(javadoc);
boolean matched = matcher.find();
assert matched;
int start = matcher.end();
int end = javadoc.indexOf("
", start);
javadoc =
javadoc.substring(start, end).replaceAll("\n \\*", "\n").replaceAll(
"\n c", "\n");
return "" + javadoc + "\n"; } /** * @param method * @param javadoc * @return */ private String[] parseParameterNames(Method method, String javadoc) { int n = method.getParameterTypes().length; String[] names = new String[n]; // this is the worst regex code I've ever written... I'm being lazy int begin = javadoc.indexOf("METHOD SUMMARY"); Pattern pattern = Pattern.compile("\\Q>" + method.getName() + "(\\E"); Matcher matcher = pattern.matcher(javadoc); boolean matched = matcher.find(begin); assert matched; // this begin is the real beginning of our search begin = matcher.end(); // we need to cap the region to look at pattern = Pattern.compile("\\Q)\\E"); matcher = pattern.matcher(javadoc); matched = matcher.find(begin); assert matched; int end = matcher.start(); pattern = Pattern.compile(" ([^,]*)(,|$)"); matcher = pattern.matcher(javadoc); matcher.region(begin, end); int cnt = 0; while (matcher.find()) { String name = matcher.group(1); names[cnt] = name; cnt++; } assert cnt == n; return names; // int n = method.getParameterTypes().length; // String[] names = new String[n]; // for (int i = 0; i < n; i++) { // names[i] = "arg" + (i + 1); // } // return names; } private String topWrapperLoader(String wrapperName) { // use static initialisation StringBuilder builder = new StringBuilder(); builder.append("\tstatic private final " + wrapperName + " current;\n"); builder.append("\tstatic {\n"); builder.append("\t\tLogger logger = Logger.getLogger(\"org.netlib." + wrapperName.toLowerCase() + "\");\n"); builder.append("\t\tif (Native" + wrapperName + ".INSTANCE.isLoaded) {\n"); builder.append("\t\t\tcurrent = Native" + wrapperName + ".INSTANCE;\n"); builder.append("\t\t\tlogger.config(\"Using JNI for " + wrapperName + "\");\n"); builder.append("\t\t} else {\n"); builder.append("\t\t\tcurrent = J" + wrapperName + ".INSTANCE;\n"); builder.append("\t\t\tlogger.config(\"Using F2J as JNI failed for " + wrapperName + "\");\n"); builder.append("\t\t}\n"); if ("LAPACK".equals(wrapperName)) { // workaround bug 5 builder.append("\t\tcurrent.slamch(\"E\");\n"); builder.append("\t\tcurrent.dlamch(\"E\");\n"); } builder.append("\t}\n\n"); builder.append("\tpublic static " + wrapperName + " getInstance() {\n"); builder.append("\t\treturn current;\n"); builder.append("\t}\n\n"); // // leading dimension helper methods // builder.append("\t/**\n"); // builder.append("\t *
max(1, M)
provided as a convenience for 'leading
// dimension' calculations\n\t * \n");
// builder.append("\t * @param n\n");
// builder.append("\t */\n");
// builder.append("\tstatic public int ld(int n) {\n");
// builder.append("\t\treturn Math.max(1, n);\n");
// builder.append("\t}\n");
// builder.append("\t/**\n");
// builder.append("\t * max(1, max(M, N))
provided as a convenience for
// 'leading dimension' calculations\n\t * \n");
// builder.append("\t * @param m\n");
// builder.append("\t * @param n\n");
// builder.append("\t */\n");
// builder.append("\tstatic public int ld(int m, int n) {\n");
// builder.append("\t\treturn Math.max(1, Math.max(m, n));\n");
// builder.append("\t}\n\n");
return builder.toString();
// the following is an alternative using lazy initialisation
// StringBuilder builder = new StringBuilder();
// builder.append("\tstatic private volatile " + wrapperName
// + " current = null;\n\n");
// builder.append("\tprivate static final Object currentLock = new Object();\n\n");
// builder.append("\tpublic static final " + wrapperName
// + " getInstance() {\n");
// builder.append("\t\tsynchronized (currentLock) {\n");
// builder.append("\t\t\t// synchronised lazy initialisation\n");
// builder.append("\t\t\tif (current == null) {\n");
// builder.append("\t\t\t\tLogger logger = Logger.getLogger(\"org.netlib\");\n");
// builder.append("\t\t\t\t// test the JNI implementation\n");
// builder.append("\t\t\t\tif (Native" + wrapperName
// + ".INSTANCE.isLoaded) {\n");
// builder.append("\t\t\t\t\tcurrent = Native" + wrapperName + ".INSTANCE;\n");
// builder.append("\t\t\t\t\tlogger.info(\"Using JNI for " + wrapperName + "\");\n");
// builder.append("\t\t\t\t} else {\n");
// builder.append("\t\t\t\t\t// otherwise use F2J\n");
// builder.append("\t\t\t\t\tcurrent = J" + wrapperName + ".INSTANCE;\n");
// builder.append("\t\t\t\t\tlogger.info(\"Using F2J as JNI failed for "
// + wrapperName + "\");\n");
// builder.append("\t\t\t\t}\n");
// builder.append("\t\t\t}\n");
// builder.append("\t\t\treturn current;\n");
// builder.append("\t\t}\n");
// builder.append("\t}\n");
// return builder.toString();
}
/**
* @param paramTypes
* @param names
* @return
*/
private List