com.github.fommil.netlib.generator.NativeImplJniGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.github.fommil.netlib.generator.NativeImplJniGenerator.java

Source

/*
 * Copyright (C) 2013 Samuel Halliday
 *
 * This program 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 program 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 program.  If not, see [http://www.gnu.org/licenses/].
 */
package com.github.fommil.netlib.generator;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroupFile;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

@Mojo(name = "native-jni", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE)
public class NativeImplJniGenerator extends AbstractNetlibGenerator {

    protected final STGroupFile jniTemplates = new STGroupFile("com/github/fommil/netlib/generator/netlib-jni.stg",
            '$', '$');

    /**
     * The interface that we are implementing.
     */
    @Parameter(required = true)
    protected String implementing;

    /**
     * C Header files to include
     */
    @Parameter
    protected List<String> includes;

    /**
     * Prepended to the native function name.
     */
    @Parameter
    protected String prefix = "";

    /**
     * Suffixed to the native function name.
     */
    @Parameter
    protected String suffix = "";

    /**
     * Prepended to the native function parameter list.
     */
    @Parameter
    protected String firstParam;

    @Parameter
    protected String noFirstParam;

    @Parameter
    protected boolean cblas_hack;

    @Parameter
    protected boolean lapacke_hack;

    @Parameter
    protected boolean fortran_hack;

    @Parameter
    protected boolean extractChar;

    @Override
    protected String generate(List<Method> methods) throws Exception {
        ST t = jniTemplates.getInstanceOf("jni");

        if (includes == null)
            includes = Lists.newArrayList();

        includes.add(outputName.replace(".c", ".h"));
        t.add("includes", includes);

        List<String> members = Lists.newArrayList();
        for (Method method : methods) {
            members.add(render(method, false));
            if (hasOffsets(method))
                members.add(render(method, true));
        }

        t.add("members", members);

        return t.render();
    }

    private String render(Method method, boolean offsets) {
        ST f = jniTemplates.getInstanceOf("function");
        f.add("returns", jType2C(method.getReturnType()));
        f.add("fqn", (implementing + "." + method.getName()).replace(".", "_") + (offsets ? "_1offsets" : ""));
        f.add("name", prefix + method.getName() + suffix);
        List<String> params = getNetlibCParameterTypes(method, offsets);
        List<String> names = getNetlibJavaParameterNames(method, offsets);
        f.add("paramTypes", params);
        f.add("paramNames", names);
        f.add("params", getCMethodParams(method, offsets));

        if (method.getReturnType() == Void.TYPE) {
            if (lapacke_hack && Iterables.getLast(names).equals("info")) {
                f.add("assignReturn", "int returnValue = ");
            } else {
                f.add("assignReturn", "");
            }
            f.add("return", "");
        } else {
            f.add("assignReturn", jType2C(method.getReturnType()) + " returnValue = ");
            f.add("return", "return returnValue;");
        }

        List<String> init = Lists.newArrayList();
        List<String> clean = Lists.newArrayList();

        for (int i = 0; i < params.size(); i++) {
            String param = params.get(i);
            String name = names.get(i);
            ST before = jniTemplates.getInstanceOf(param + "_init");
            if (lapacke_hack && name.equals("info"))
                before = jniTemplates.getInstanceOf(param + "_info_init");
            if (before != null) {
                before.add("name", name);
                init.add(before.render());
            }

            ST after = jniTemplates.getInstanceOf(param + "_clean");
            if (lapacke_hack && name.equals("info"))
                after = jniTemplates.getInstanceOf(param + "_info_clean");
            if (after != null) {
                after.add("name", name);
                clean.add(after.render());
            }
        }
        Collections.reverse(clean);

        f.add("init", init);
        f.add("clean", clean);
        return f.render();
    }

    private List<String> getNetlibCParameterTypes(Method method, boolean offsets) {
        final List<String> types = Lists.newArrayList();
        iterateRelevantParameters(method, offsets, new ParameterCallback() {
            @Override
            public void process(int i, Class<?> param, String name, String offsetName) {
                types.add(jType2C(param));
            }
        });
        return types;
    }

    private String jType2C(Class param) {
        if (param == Void.TYPE)
            return "void";
        if (param.isArray())
            return "j" + param.getComponentType().getSimpleName() + "Array";
        return "j" + param.getSimpleName().toLowerCase();
    }

    private List<String> getCMethodParams(final Method method, final boolean offsets) {
        final LinkedList<String> params = Lists.newLinkedList();
        if (firstParam != null && !method.getName().matches(noFirstParam)) {
            params.add(firstParam);
        }

        iterateRelevantParameters(method, false, new ParameterCallback() {
            @Override
            public void process(int i, Class<?> param, String name, String offsetName) {
                if (lapacke_hack && name.equals("info"))
                    return;

                if (param == Object.class)
                    throw new UnsupportedOperationException(method + " " + param + " " + name);

                if (param == Boolean.TYPE || !param.isPrimitive()) {
                    name = "jni_" + name;
                    // NOTE: direct comparisons against StringW.class don't work as expected
                    if (!param.getSimpleName().equals("StringW") && param.getSimpleName().endsWith("W")) {
                        name = "&" + name;
                    }
                }

                if (param == String.class) {
                    if (cblas_hack) {
                        if (name.contains("trans"))
                            name = "getCblasTrans(" + name + ")";
                        else if (name.contains("uplo"))
                            name = "getCblasUpLo(" + name + ")";
                        else if (name.contains("side"))
                            name = "getCblasSide(" + name + ")";
                        else if (name.contains("diag"))
                            name = "getCblasDiag(" + name + ")";
                    }
                }

                if (!fortran_hack && param == String.class && extractChar)
                    name = name + "[0]";

                if (fortran_hack && param.isPrimitive())
                    name = "&" + name;

                if (offsets & offsetName != null) {
                    name = name + " + " + offsetName;
                }

                params.add(name);
            }
        });

        return params;
    }
}