Package org.netlib.generate

Source Code of org.netlib.generate.JavaGenerator$Doublet

/*
* 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 <http://www.gnu.org/licenses/>.
*
* 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.
* <p>
* 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<A, B> {
    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<Type, String> classDefs =
      new HashMap<Type, String>();

  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<String> dontUseJNI = new HashSet<String>();

  /**
   * @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<Class<?>> 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<Method> methods = new ArrayList<Method>();
    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<Doublet<String, String>> 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<String, String> 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<String, String> 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<Doublet<String, String>> typeAndName) {
    List<String> names = new ArrayList<String>();
    List<String> types = new ArrayList<String>();
    for (Doublet<String, String> 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<Doublet<String, String>> 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<String, String> 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<Doublet<String, String>> 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<Doublet<String, String>> 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<String> notSupportedByJNI =
      new HashSet<String>(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",
        /* these are not defined on Ubuntu 12.10 */
       
        "slamc1", "slamc2", "slamc4", "slamc5",
        "dlamc1", "dlamc2", "dlamc4", "dlamc5"
       
        ));

  /** A list of methods where F2J has a different signature than in OS X Leopard */
  static final List<String> 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<String, String[]> fromDocs = getParameterNames(method);
    String javadocs = fromDocs.keySet().iterator().next();
    String[] names = fromDocs.values().iterator().next();
    Class<?>[] paramTypes = method.getParameterTypes();
    List<Doublet<String, String>> 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<Class<?>> getClasses(String packageName, IClassFilter filter) {
    String packagePath = packageName.replace('.', '/');
//    ArrayList<URL> classpath = new ArrayList<URL>();
//    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<Class<?>> result = new ArrayList<Class<?>>();
    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<JarEntry> 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<String, String[]> 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\tboolean succeeded=false;\n");   
    b.append("\t\ttry {\n");
    b.append("\t\t\tSystem.loadLibrary(libname);\n");
    b.append("\t\t\tsucceeded=true;\n");   
    b.append("\t\t} catch (UnsatisfiedLinkError e) {\n");
    b.append("\t\t} catch (SecurityException e) {\n");
    b.append("\t\t}\n");
    b.append("\t\tisLoaded = succeeded;\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("seymour@cs.utk.edu</a> with any questions.\n<p>");
    Matcher matcher = pattern.matcher(javadoc);
    boolean matched = matcher.find();
    assert matched;
    int start = matcher.end();
    int end = javadoc.indexOf("</pre>", start);
    javadoc =
        javadoc.substring(start, end).replaceAll("\n \\*", "\n").replaceAll(
          "\n c", "\n");
    return "<pre>" + javadoc + "</pre>\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() + "</A></B>(\\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)</CODE>\\E");
    matcher = pattern.matcher(javadoc);
    matched = matcher.find(begin);
    assert matched;
    int end = matcher.start();
    pattern = Pattern.compile("&nbsp;([^,]*)(,|$)");
    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 * <code>max(1, M)</code> 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 * <code>max(1, max(M, N))</code> 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<Doublet<String, String>> typeAndName(Class<?>[] paramTypes,
      String[] names) {
    assert names.length == paramTypes.length;
    List<Doublet<String, String>> l =
        new ArrayList<Doublet<String, String>>();
    Class<?> lastType = Void.TYPE;
    for (int i = 0; i < names.length; i++) {
      Class<?> type = paramTypes[i];
      if (type.equals(Integer.TYPE)
          && (lastType.equals(double[].class)
              || lastType.equals(int[].class)
              || lastType.equals(boolean[].class) || lastType.equals(float[].class))) {
        lastType = type;
        continue;
      }
      String t = classDefs.get(type);
      if (t == null)
        t = "Object";
      l.add(new Doublet<String, String>(t, names[i]));
      lastType = type;
    }
    return l;
  }
}
TOP

Related Classes of org.netlib.generate.JavaGenerator$Doublet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.