/* * 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; } }