/*
Copyright (c) 2009-2013 Olivier Chafik, All Rights Reserved
This file is part of JNAerator (http://jnaerator.googlecode.com/).
JNAerator is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
JNAerator 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with JNAerator. If not, see <http://www.gnu.org/licenses/>.
*/
package com.ochafik.lang.jnaerator;
import com.google.common.collect.ImmutableMap;
import org.bridj.ann.Name;
import org.bridj.BridJ;
import org.bridj.FlagSet;
import org.bridj.IntValuedEnum;
import org.bridj.StructObject;
import org.bridj.cpp.CPPObject;
import org.bridj.cpp.com.IUnknown;
import com.ochafik.lang.jnaerator.BridJTypeConversion.NL4JConversion;
import com.ochafik.lang.jnaerator.TypeConversion.ConvType;
import com.ochafik.lang.jnaerator.TypeConversion.EnumItemResult;
//import org.bridj.structs.StructIO;
//import org.bridj.structs.Array;
import java.io.IOException;
import java.util.*;
import com.ochafik.lang.jnaerator.parser.*;
import com.ochafik.lang.jnaerator.parser.Enum;
import com.ochafik.lang.jnaerator.parser.Statement.Block;
import com.ochafik.lang.jnaerator.parser.StoredDeclarations.*;
import com.ochafik.lang.jnaerator.parser.Struct.MemberVisibility;
import com.ochafik.lang.jnaerator.parser.TypeRef.*;
import com.ochafik.lang.jnaerator.parser.Expression.*;
import com.ochafik.lang.jnaerator.parser.Function.Type;
import com.ochafik.lang.jnaerator.parser.DeclarationsHolder.ListWrapper;
import com.ochafik.lang.jnaerator.parser.Declarator.*;
import com.nativelibs4java.jalico.Pair;
import com.ochafik.util.string.StringUtils;
import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*;
import com.ochafik.lang.jnaerator.parser.Function.SignatureType;
import org.bridj.*;
import org.bridj.ann.Convention;
import org.bridj.ann.Optional;
import org.bridj.cpp.CPPRuntime;
import org.bridj.objc.NSObject;
public class BridJDeclarationsConverter extends DeclarationsConverter {
public BridJDeclarationsConverter(Result result) {
super(result);
}
public void annotateActualName(ModifiableElement e, Identifier name) {
e.addAnnotation(new Annotation(typeRef(org.bridj.ann.Name.class), expr(name.toString())));
}
@Override
public Struct convertCallback(FunctionSignature functionSignature, Signatures signatures, Identifier callerLibraryName) {
Struct decl = super.convertCallback(functionSignature, signatures, callerLibraryName);
if (decl != null) {
decl.setParents(Arrays.asList((SimpleTypeRef) (FunctionSignature.Type.ObjCBlock.equals(functionSignature.getType())
? result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.ObjCBlock)
: (SimpleTypeRef) typeRef(ident(result.config.runtime.callbackClass, expr(typeRef(decl.getTag().clone())))))));
//addCallingConventionAnnotation(functionSignature.getFunction(), decl);
}
return decl;
}
@Override
public void convertEnum(Enum e, Signatures signatures, DeclarationsHolder out, Identifier libraryClassName) {
if (e.isForwardDeclaration()) {
return;
}
Identifier rawEnumName = getActualTaggedTypeName(e);
Map<String, EnumItemResult> results = result.typeConverter.getEnumValuesAndCommentsByName(e, libraryClassName);
boolean hasEnumClass = false;
if (rawEnumName != null && rawEnumName.resolveLastSimpleIdentifier().getName() != null) {
hasEnumClass = true;
Identifier enumName = result.typeConverter.getValidJavaIdentifier(rawEnumName);
if (!signatures.addClass(enumName)) {
return;
}
signatures = new Signatures();
Enum en = new Enum();
if (result.config.forceNames || !rawEnumName.equals(enumName))
annotateActualName(en, rawEnumName);
addParentNamespaceAnnotation(en, e.getParentNamespace());
if (!result.config.noComments) {
en.importComments(e, "enum values", getFileCommentContent(e));
}
en.setType(Enum.Type.Java);
en.setTag(enumName.clone());
en.addModifiers(ModifierType.Public);
out.addDeclaration(new TaggedTypeRefDeclaration(en));
Struct body = new Struct();
en.setBody(body);
boolean hasValidItem = false;
for (EnumItemResult er : results.values()) {
if (er.errorElement != null) {
out.addDeclaration(er.errorElement);
continue;
}
String itemName = result.typeConverter.getValidJavaIdentifierString(ident(er.originalItem.getName()));
Enum.EnumItem item = new Enum.EnumItem(itemName, er.convertedValue);
en.addItem(item);
hasValidItem = true;
if (!result.config.noComments) {
if (item != null) {// && hasEnumClass) {
item.importComments(er.originalItem);
}
}
}
if (hasValidItem) {
en.addInterface(ident(IntValuedEnum.class, expr(typeRef(enumName.clone()))));
String valueArgName = "value";
body.addDeclaration(new Function(Type.JavaMethod, enumName.clone(), null, new Arg(valueArgName, typeRef(Long.TYPE))).setBody(block(
stat(expr(memberRef(thisRef(), MemberRefStyle.Dot, valueArgName), AssignmentOperator.Equal, varRef(valueArgName))))));
body.addDeclaration(new VariablesDeclaration(typeRef(Long.TYPE), new DirectDeclarator(valueArgName)).addModifiers(ModifierType.Public, ModifierType.Final));
body.addDeclaration(new Function(Type.JavaMethod, ident(valueArgName), typeRef(Long.TYPE)).setBody(block(
new Statement.Return(memberRef(thisRef(), MemberRefStyle.Dot, valueArgName)))).addModifiers(ModifierType.Public));
body.addDeclaration(new Function(Type.JavaMethod, ident("iterator"), typeRef(ident(Iterator.class, expr(typeRef(enumName.clone()))))).setBody(block(
new Statement.Return(
methodCall(
methodCall(
expr(typeRef(Collections.class)),
MemberRefStyle.Dot,
"singleton",
thisRef()),
MemberRefStyle.Dot,
"iterator")))).addModifiers(ModifierType.Public));
body.addDeclaration(new Function(Type.JavaMethod, ident("fromValue"), typeRef(ident(IntValuedEnum.class, expr(typeRef(enumName.clone())))), new Arg(valueArgName, typeRef(Integer.TYPE))).setBody(block(
new Statement.Return(
methodCall(
expr(typeRef(FlagSet.class)),
MemberRefStyle.Dot,
"fromValue",
varRef(valueArgName),
methodCall(
"values"))))).addModifiers(ModifierType.Public, ModifierType.Static));
}
} else {
outputEnumItemsAsConstants(results, out, signatures, libraryClassName, hasEnumClass);
}
}
void addCallingConventionAnnotation(Function originalFunction, ModifiableElement target) {
Convention.Style cc = null;
if (originalFunction.hasModifier(ModifierType.__stdcall)) {
cc = Convention.Style.StdCall;
} else if (originalFunction.hasModifier(ModifierType.__fastcall)) {
cc = Convention.Style.FastCall;
} else if (originalFunction.hasModifier(ModifierType.__thiscall)) {
cc = Convention.Style.ThisCall;
} else if (originalFunction.hasModifier(ModifierType.__pascal)) {
cc = Convention.Style.Pascal;
}
if (cc != null) {
target.addAnnotation(new Annotation(typeRef(Convention.class), enumRef(cc)));
}
}
@Override
public void convertFunction(Function function, Signatures signatures, boolean isCallback,
DeclarationsHolder declarations, DeclarationsHolder implementations, Identifier libraryClassName,
String sig, Identifier functionName, String library, int iConstructor) throws UnsupportedConversionException {
assert implementations != null;
boolean extractingDeclarations = declarations != null && declarations.resolveHolder() != implementations.resolveHolder();
Element parent = function.getParentElement();
MemberVisibility visibility = function.getVisibility();
boolean isPublic = visibility == MemberVisibility.Public || function.hasModifier(ModifierType.Public);
boolean isPrivate = visibility == MemberVisibility.Private || function.hasModifier(ModifierType.Private);
boolean isProtected = visibility == MemberVisibility.Protected || function.hasModifier(ModifierType.Protected);
boolean isInStruct = parent instanceof Struct;
if (isInStruct && result.config.skipPrivateMembers && (isPrivate || !isPublic && !isProtected)) {
return;
}
boolean isStatic = function.hasModifier(ModifierType.Static);
boolean isConstructor = iConstructor != -1;
Function nativeMethod = new Function(Type.JavaMethod, ident(functionName), null);
if (result.config.synchronizedMethods && !isCallback) {
nativeMethod.addModifiers(ModifierType.Synchronized);
}
addCallingConventionAnnotation(function, nativeMethod);
boolean annotatedActualName = false;
if (function.getName() != null && !isCallback &&
(!functionName.toString().equals(function.getName().toString()) || result.config.forceNames))
{
annotateActualName(nativeMethod, function.getName());
annotatedActualName = true;
}
Function rawMethod = nativeMethod.clone();
//Map<String, NL4JConversion> argTypes = new LinkedHashMap<String, NL4JConversion>();
boolean isObjectiveC = function.getType() == Type.ObjCMethod;
Set<String> argNames = new TreeSet<String>();
List<Expression> superConstructorArgs = null;
if (isConstructor) {
superConstructorArgs = new ArrayList<Expression>();
superConstructorArgs.add(cast(typeRef(Void.class), nullExpr()));
superConstructorArgs.add(expr(iConstructor));
}
Identifier varArgType = null;
String varArgName = null;
NL4JConversion returnType = null;
List<NL4JConversion> paramTypes = new ArrayList<NL4JConversion>();
List<String> paramNames = new ArrayList<String>();
if (!isConstructor) {
returnType = ((BridJTypeConversion) result.typeConverter).convertTypeToNL4J(function.getValueType(), libraryClassName, null, null, -1, -1);
}
for (int iArg = 0, nArgs = function.getArgs().size(); iArg < nArgs; iArg++) {
Arg arg = function.getArgs().get(iArg);
String paramName;
boolean isVarArgs = isVarArgs(arg);
if (isVarArgs && iArg == nArgs - 1) {
// assert arg.getValueType() == null;
paramName = varArgName = chooseJavaArgName(arg.getName() == null ? "varargs" : arg.getName(), iArg + 1, argNames);
varArgType = ident(isObjectiveC ? NSObject.class : Object.class);
} else {
TypeRef argType = arg.getValueType();
if (isVarArgs) {
argType = new TypeRef.Pointer(typeRef(void.class), PointerStyle.Pointer);
}
paramName = chooseJavaArgName(arg.getName(), iArg + 1, argNames);
paramTypes.add(((BridJTypeConversion) result.typeConverter).convertTypeToNL4J(argType, libraryClassName, null, null, -1, -1));
}
paramNames.add(paramName);
if (isConstructor) {
superConstructorArgs.add(varRef(paramName));
}
}
fillIn(signatures, functionName, nativeMethod, returnType, paramTypes, paramNames, varArgType, varArgName, isCallback, false);
List<Declaration> extractibleDecls = new ArrayList<Declaration>();
Block convertedBody = null;
if (isConstructor) {
convertedBody = block(stat(methodCall("super", superConstructorArgs.toArray(new Expression[superConstructorArgs.size()]))));
} else if (result.config.convertBodies && function.getBody() != null) {
try {
Pair<Element, List<Declaration>> bodyAndExtraDeclarations = result.bridjer.convertToJava(function.getBody(), libraryClassName);
convertedBody = (Block) bodyAndExtraDeclarations.getFirst();
for (Declaration d : bodyAndExtraDeclarations.getSecond()) {
implementations.addDeclaration(d);
// extractibleDecls.add(d);
}
} catch (Exception ex) {
ex.printStackTrace(System.out);
nativeMethod.addToCommentBefore("TRANSLATION OF BODY FAILED: " + ex);
}
}
if (!result.config.noComments) {
nativeMethod.importComments(function, isCallback ? null : getFileCommentContent(function));
}
boolean generateStaticMethod = (isStatic || !isCallback && !isInStruct) &&
(declarations == null || implementations == declarations);
nativeMethod.addModifiers(
isProtected && !extractingDeclarations && !result.config.publicRawBindings ?
ModifierType.Protected : ModifierType.Public,
generateStaticMethod ? ModifierType.Static : null);
boolean isOptional = isOptionalFunction(function.getName() + "");
implementations.addDeclaration(nativeMethod);
boolean forwardedToRaw = false;
if (convertedBody == null) {
if (result.config.genRawBindings) {// && !isCallback) {
// Function rawMethod = nativeMethod.clone();
rawMethod.setArgs(Collections.EMPTY_LIST);
fillIn(signatures, functionName, rawMethod, returnType, paramTypes, paramNames, varArgType, varArgName, isCallback, true);
rawMethod.addModifiers(
extractingDeclarations || isCallback || result.config.publicRawBindings ?
ModifierType.Public : ModifierType.Protected,
isCallback ? ModifierType.Abstract : ModifierType.Native);
if (generateStaticMethod) {
rawMethod.addModifiers(ModifierType.Static);
}
if (!nativeMethod.computeSignature(SignatureType.ArgsAndRet).equals(rawMethod.computeSignature(SignatureType.ArgsAndRet))) {
implementations.addDeclaration(rawMethod);
extractibleDecls.add(rawMethod);
if (isOptional && !isCallback) {
rawMethod.addAnnotation(new Annotation(typeRef(Optional.class)));
}
List<Expression> objectToRawFollowedArgs = new ArrayList<Expression>();
List<Expression> rawToObjectFollowedArgs = new ArrayList<Expression>();
for (int i = 0, n = paramTypes.size(); i < n; i++) {
NL4JConversion paramType = paramTypes.get(i);
String paramName = paramNames.get(i);
switch (paramType.type) {
case Pointer:
objectToRawFollowedArgs.add(createGetPeerExpression(varRef(paramName)));
rawToObjectFollowedArgs.add(createPointerToAddressExpression(paramType, varRef(paramName)));
break;
case Enum:
objectToRawFollowedArgs.add(createGetEnumValueExpression(varRef(paramName)));
rawToObjectFollowedArgs.add(createEnumExpression(paramType, varRef(paramName)));
break;
// case NativeSize:
// case NativeLong:
// followedArg = methodCall(varRef(paramName), "longValue");
// break;
default:
objectToRawFollowedArgs.add(varRef(paramName));
rawToObjectFollowedArgs.add(varRef(paramName));
break;
}
}
if (varArgType != null) {
objectToRawFollowedArgs.add(varRef(varArgName));
rawToObjectFollowedArgs.add(varRef(varArgName));
}
Expression objectToRawFollowedCall =
methodCall(rawMethod.getName().toString(), objectToRawFollowedArgs.toArray(new Expression[objectToRawFollowedArgs.size()]));
Expression rawToObjectFollowedCall =
methodCall(nativeMethod.getName().toString(), rawToObjectFollowedArgs.toArray(new Expression[rawToObjectFollowedArgs.size()]));
boolean isVoid = "void".equals(String.valueOf(nativeMethod.getValueType()));
if (isVoid) {
nativeMethod.setBody(block(stat(objectToRawFollowedCall)));
if (isCallback) {
rawMethod.removeModifiers(ModifierType.Abstract);
rawMethod.setBody(block(stat(rawToObjectFollowedCall)));
}
} else {
switch (returnType.type) {
case Pointer:
objectToRawFollowedCall = createPointerToAddressExpression(returnType, objectToRawFollowedCall);
rawToObjectFollowedCall = createGetPeerExpression(rawToObjectFollowedCall);
// if (returnType.isTypedPointer) {
// objectToRawFollowedCall = new New(nativeMethod.getValueType(), objectToRawFollowedCall);
// } else {
// Expression ptrExpr = expr(typeRef(org.bridj.Pointer.class));
// Expression targetTypeExpr = result.typeConverter.typeLiteral(getSingleTypeParameter(nativeMethod.getValueType()));
// if (targetTypeExpr == null || (returnType.targetTypeConversion != null && returnType.targetTypeConversion.type == ConvType.Void)) {
// objectToRawFollowedCall = methodCall(ptrExpr, "pointerToAddress", objectToRawFollowedCall);
// } else {
// objectToRawFollowedCall = methodCall(ptrExpr, "pointerToAddress", objectToRawFollowedCall, targetTypeExpr);
// }
// }
break;
case Enum:
objectToRawFollowedCall = createEnumExpression(returnType, objectToRawFollowedCall);
rawToObjectFollowedCall = createGetEnumValueExpression(rawToObjectFollowedCall);
// objectToRawFollowedCall = methodCall(expr(typeRef(org.bridj.FlagSet.class)), "fromValue", objectToRawFollowedCall, result.typeConverter.typeLiteral(getSingleTypeParameter(nativeMethod.getValueType())));
break;
case NativeLong:
case NativeSize:
if (!rawMethod.getValueType().toString().equals("long")) {
objectToRawFollowedCall = new New(nativeMethod.getValueType().clone(), objectToRawFollowedCall);
rawToObjectFollowedCall = createGetIntegralValueExpression(rawToObjectFollowedCall);
}
default:
break;
}
nativeMethod.setBody(block(new Statement.Return(objectToRawFollowedCall)));
if (isCallback) {
rawMethod.removeModifiers(ModifierType.Abstract);
rawMethod.setBody(block(new Statement.Return(rawToObjectFollowedCall)));
} else if (function.getName() != null &&
!function.getName().equals(rawMethod.getName()) &&
!annotatedActualName) {
annotateActualName(rawMethod, function.getName());
annotatedActualName = true;
}
}
forwardedToRaw = true;
}
}
if (!forwardedToRaw) {
nativeMethod.addModifiers(isCallback ? ModifierType.Abstract : ModifierType.Native);
if (isOptional && !isCallback) {
nativeMethod.addAnnotation(new Annotation(typeRef(Optional.class)));
}
}
} else {
nativeMethod.setBody(convertedBody);
}
if (!forwardedToRaw && convertedBody == null) {
extractibleDecls.add(nativeMethod);
}
for (Declaration d : extractibleDecls) {
if (extractingDeclarations) {
if (d instanceof Function) {
Function m = (Function)d.clone();
m.setBody(null);
m.removeModifiers(ModifierType.Abstract, ModifierType.Final,
ModifierType.Static, ModifierType.Native,
ModifierType.Public, ModifierType.Protected);
declarations.addDeclaration(m);
// d.addAnnotation(new Annotation(typeRef(Override.class)));
}
}
}
}
private Expression createGetEnumValueExpression(Expression value) {
return cast(typeRef(int.class), methodCall(value, "value"));
}
private Expression createGetIntegralValueExpression(Expression value) {
return methodCall(value, "longValue");
}
private Expression createGetPeerExpression(Expression value) {
return methodCall(expr(typeRef(org.bridj.Pointer.class)), "getPeer", value);
}
private Expression createPointerToAddressExpression(NL4JConversion pointerType, Expression value) {
TypeRef tr = pointerType.getTypeRef(false);
if (pointerType.isTypedPointer) {
return new New(tr, value);
} else {
Expression ptrExpr = expr(typeRef(org.bridj.Pointer.class));
Expression targetTypeExpr = result.typeConverter.typeLiteral(getSingleTypeParameter(tr));
if (targetTypeExpr == null || (pointerType.targetTypeConversion != null && pointerType.targetTypeConversion.type == ConvType.Void)) {
return methodCall(ptrExpr, "pointerToAddress", value);
} else {
// Cast to unparameterized Pointer to avoid weird cast cases
return cast(typeRef(org.bridj.Pointer.class), methodCall(ptrExpr, "pointerToAddress", value, targetTypeExpr));
}
}
}
private Expression createEnumExpression(NL4JConversion enumType, Expression value) {
TypeRef tr = enumType.getTypeRef(false);
return methodCall(expr(typeRef(org.bridj.FlagSet.class)), "fromValue", value, result.typeConverter.typeLiteral(getSingleTypeParameter(tr)));
}
private void fillIn(Signatures signatures, Identifier functionName, Function nativeMethod, NL4JConversion returnType, List<NL4JConversion> paramTypes, List<String> paramNames, Identifier varArgType, String varArgName, boolean isCallback, boolean useRawTypes) {
for (int i = 0, n = paramTypes.size(); i < n; i++) {
NL4JConversion paramType = paramTypes.get(i);
String paramName = paramNames.get(i);
nativeMethod.addArg(paramType.annotateTypedType(new Arg(paramName, paramType.getTypeRef(useRawTypes)), useRawTypes));//.getTypedTypeRef())));
}
if (varArgType != null) {
nativeMethod.addArg(new Arg(varArgName, typeRef(varArgType.clone()))).setVarArg(true);
}
if (returnType != null) {
returnType.annotateTypedType(nativeMethod, useRawTypes);
nativeMethod.setValueType(returnType.getTypeRef(useRawTypes));
}
String natSig = nativeMethod.computeSignature(SignatureType.JavaStyle);
Identifier javaMethodName = signatures == null ? functionName : signatures.findNextMethodName(natSig, functionName);
if (!javaMethodName.equals(functionName)) {
nativeMethod.setName(javaMethodName);
}
}
@Override
public Struct convertStruct(Struct struct, Signatures signatures, Identifier callerLibraryClass, String callerLibrary, boolean onlyFields) throws IOException {
Identifier structName = getActualTaggedTypeName(struct);
if (structName == null) {
return null;
}
//if (structName.toString().contains("MonoObject"))
// structName.toString();
if (struct.isForwardDeclaration())// && !result.structsByName.get(structName).isForwardDeclaration())
{
return null;
}
if (!signatures.addClass(structName)) {
return null;
}
boolean isUnion = struct.getType() == Struct.Type.CUnion;
boolean inheritsFromStruct = false;
Identifier baseClass = null;
int parentFieldsCount = 0;
List<String> preComments = new ArrayList<String>();
for (SimpleTypeRef parentName : struct.getParents()) {
Struct parent = result.structsByName.get(parentName.getName());
if (parent == null) {
// TODO report error
continue;
}
try {
parentFieldsCount += countFieldsInStruct(parent);
} catch (UnsupportedConversionException ex) {
preComments.add("Error: " + ex);
}
baseClass = result.typeConverter.getTaggedTypeIdentifierInJava(parent);
if (baseClass != null) {
inheritsFromStruct = true;
break; // TODO handle multiple and virtual inheritage
}
}
boolean hasMemberFunctions = false;
for (Declaration d : struct.getDeclarations()) {
if (d instanceof Function) {
hasMemberFunctions = true;
break;
}
}
Constant uuid = (Constant) struct.getModifierValue(ModifierType.UUID);
if (baseClass == null) {
switch (struct.getType()) {
case CStruct:
case CUnion:
if (!hasMemberFunctions) {
baseClass = ident(StructObject.class);
break;
}
case CPPClass:
baseClass = ident(uuid == null ? CPPObject.class : IUnknown.class);
result.hasCPlusPlus = true;
break;
default:
throw new UnsupportedOperationException();
}
}
Struct structJavaClass = publicStaticClass(structName, baseClass, Struct.Type.JavaClass, struct);
if (!result.config.noStaticInit)
structJavaClass.addDeclaration(newStaticInit());
//if (result.config.microsoftCOM) {
if (uuid != null) {
structJavaClass.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.IID), uuid));
}
structJavaClass.setResolvedJavaIdentifier(ident(structName));
if (result.config.forceNames)
annotateActualName(structJavaClass, structName);
addParentNamespaceAnnotation(structJavaClass, struct.getParentNamespace());
structJavaClass.addToCommentBefore(preComments);
//System.out.println("parentFieldsCount(structName = " + structName + ") = " + parentFieldsCount);
final int iChild[] = new int[]{parentFieldsCount};
//cl.addDeclaration(new EmptyDeclaration())
Signatures childSignatures = new Signatures();
/*if (isVirtual(struct) && !onlyFields) {
String vptrName = DEFAULT_VPTR_NAME;
VariablesDeclaration vptr = new VariablesDeclaration(typeRef(VirtualTablePointer.class), new Declarator.DirectDeclarator(vptrName));
vptr.addModifiers(ModifierType.Public);
structJavaClass.addDeclaration(vptr);
childSignatures.variablesSignatures.add(vptrName);
// TODO add vptr grabber to constructor !
}*/
// private static StructIO<MyStruct> io = StructIO.getInstance(MyStruct.class);
if (isUnion) {
structJavaClass.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Union)));
}
int iVirtual = 0, iConstructor = 0;
//List<Declaration> children = new ArrayList<Declaration>();
boolean succeeded = true;
for (Declaration d : struct.getDeclarations()) {
//if (isUnion)
// iChild[0] = 0;
if (d instanceof VariablesDeclaration) {
succeeded = convertVariablesDeclaration((VariablesDeclaration) d, childSignatures, structJavaClass, iChild, false, structName, callerLibraryClass, callerLibrary) && succeeded;
} else if (!onlyFields) {
if (d instanceof TaggedTypeRefDeclaration) {
TaggedTypeRef tr = ((TaggedTypeRefDeclaration) d).getTaggedTypeRef();
if (tr instanceof Struct) {
outputConvertedStruct((Struct) tr, childSignatures, structJavaClass, callerLibrary, false);
} else if (tr instanceof Enum) {
convertEnum((Enum) tr, childSignatures, structJavaClass, callerLibraryClass);
}
} else if (d instanceof TypeDef) {
TypeDef td = (TypeDef) d;
TypeRef tr = td.getValueType();
if (tr instanceof Struct) {
outputConvertedStruct((Struct) tr, childSignatures, structJavaClass, callerLibrary, false);
} else {
FunctionSignature fs = null;
if (tr instanceof FunctionSignature) {
fs = (FunctionSignature) tr;
} else if (tr instanceof TypeRef.Pointer) {
TypeRef target = ((TypeRef.Pointer) tr).getTarget();
if (target instanceof FunctionSignature) {
fs = (FunctionSignature) target;
}
}
if (fs != null) {
convertCallback(fs, childSignatures, structJavaClass, callerLibraryClass);
}
}
} else if (result.config.genCPlusPlus && d instanceof Function) {
Function f = (Function) d;
boolean isVirtual = f.hasModifier(ModifierType.Virtual);
boolean isConstructor = f.getName().equals(structName) && (f.getValueType() == null || f.getValueType().toString().equals("void"));
if (isConstructor && f.getArgs().isEmpty()) {
continue; // default constructor was already generated
}
String library = result.getLibrary(struct);
if (library == null) {
continue;
}
List<Declaration> decls = new ArrayList<Declaration>();
DeclarationsHolder out = new ListWrapper(decls);
convertFunction(f, childSignatures, false, out, out, callerLibraryClass, isConstructor ? iConstructor : -1);
for (Declaration md : decls) {
if (!(md instanceof Function)) {
continue;
}
Function method = (Function) md;
boolean commentOut = false;
if (isVirtual) {
method.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Virtual), expr(iVirtual)));
} else if (method.getValueType() == null) {
method.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Constructor), expr(iConstructor)));
isConstructor = true;
}
if (method.getName().toString().equals("operator")) {
commentOut = true;
}
if (commentOut) {
structJavaClass.addDeclaration(new EmptyDeclaration(method.toString()));
} else {
structJavaClass.addDeclaration(method);
}
}
if (isVirtual) {
iVirtual++;
}
if (isConstructor) {
iConstructor++;
}
}
}
}
if (succeeded) {
Function defaultConstructor = new Function(Type.JavaMethod, ident(structName), null).setBody(block(stat(methodCall("super")))).addModifiers(ModifierType.Public);
if (childSignatures.addMethod(defaultConstructor)) {
structJavaClass.addDeclaration(defaultConstructor);
}
String ptrName = "pointer";
Function castConstructor = new Function(Type.JavaMethod, ident(structName), null, new Arg(ptrName, typeRef(result.config.runtime.pointerClass))).setBody(block(stat(methodCall("super", varRef(ptrName))))).addModifiers(ModifierType.Public);
if (childSignatures.addMethod(castConstructor)) {
structJavaClass.addDeclaration(castConstructor);
}
} else {
structJavaClass.addModifiers(ModifierType.Abstract);
}
return structJavaClass;
}
Map<Identifier, Boolean> structsVirtuality = new LinkedHashMap<Identifier, Boolean>();
public boolean isVirtual(Struct struct) {
Identifier name = getActualTaggedTypeName(struct);
Boolean bVirtual = structsVirtuality.get(name);
if (bVirtual == null) {
boolean hasVirtualParent = false, hasVirtualMembers = false;
for (SimpleTypeRef parentName : struct.getParents()) {
Struct parentStruct = result.structsByName.get(parentName.getName());
if (parentStruct == null) {
if (result.config.verbose) {
System.out.println("Failed to resolve parent '" + parentName + "' for struct '" + name + "'");
}
continue;
}
if (isVirtual(parentStruct)) {
hasVirtualParent = true;
break;
}
}
for (Declaration mb : struct.getDeclarations()) {
if (mb.hasModifier(ModifierType.Virtual)) {
hasVirtualMembers = true;
break;
}
}
bVirtual = hasVirtualMembers && !hasVirtualParent;
structsVirtuality.put(name, bVirtual);
}
return bVirtual;
}
protected String ioVarName = "io", ioStaticVarName = "IO";
public List<Declaration> convertVariablesDeclarationToBridJ(String name, TypeRef mutatedType, int[] iChild, int bits, boolean isGlobal, Identifier holderName, Identifier callerLibraryName, String callerLibrary, Element... toImportDetailsFrom) throws UnsupportedConversionException {
name = result.typeConverter.getValidJavaArgumentName(ident(name)).toString();
//convertVariablesDeclaration(name, mutatedType, out, iChild, callerLibraryName);
final boolean useRawTypes = false;
//Expression initVal = null;
int fieldIndex = iChild[0];
//convertTypeToNL4J(TypeRef valueType, Identifier libraryClassName, Expression structPeerExpr, Expression structIOExpr, Expression valueExpr, int fieldIndex, int bits) throws UnsupportedConversionException {
NL4JConversion conv = ((BridJTypeConversion) result.typeConverter).convertTypeToNL4J(
mutatedType,
callerLibraryName,
thisField("io"),
varRef(name),
fieldIndex,
bits);
if (conv == null) {
throw new UnsupportedConversionException(mutatedType, "failed to convert type to Java");
} else if (conv.isUndefined) {
throw new UnsupportedConversionException(mutatedType, "failed to convert type to Java (undefined type)");
} else if ("void".equals(String.valueOf(conv.getTypeRef(useRawTypes)))) {
throw new UnsupportedConversionException(mutatedType, "void type !");
//out.add(new EmptyDeclaration("SKIPPED:", v.formatComments("", true, true, false), v.toString()));
}
Function convDecl = new Function();
conv.annotateTypedType(convDecl, useRawTypes);
convDecl.setType(Type.JavaMethod);
convDecl.addModifiers(ModifierType.Public);
if (conv.arrayLengths != null) {
convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Length),
new OpaqueExpression("{" + StringUtils.implode(conv.arrayLengths, ", ") + "}")));
}
if (conv.bits != null) {
convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Bits), conv.bits));
}
if (conv.byValue) {
convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.ByValue)));
}
for (Element e : toImportDetailsFrom) {
convDecl.importDetails(e, false);
}
convDecl.importDetails(mutatedType, true);
//convDecl.importDetails(javaType, true);
// convDecl.importDetails(v, false);
// convDecl.importDetails(vs, false);
// convDecl.importDetails(valueType, false);
// valueType.stripDetails();
convDecl.moveAllCommentsBefore();
convDecl.deDioxygenizeCommentBefore();
convDecl.setName(ident(name));
if (!isGlobal) {
convDecl.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Field), expr(fieldIndex)));
}
convDecl.setValueType(conv.getTypeRef(useRawTypes));
TypeRef javaType = convDecl.getValueType();
String pointerGetSetMethodSuffix = StringUtils.capitalize(javaType.toString());
List<Declaration> out = new ArrayList<Declaration>();
boolean addedGetterOrSetter = false;
Expression getGlobalPointerExpr = methodCall(methodCall(expr(typeRef(BridJ.class)), "getNativeLibrary", expr(callerLibrary)), "getSymbolPointer", expr(name));
String getterName = "get";
String setterName = "set";
if (isGlobal) {
if (conv.pointerGetterName != null &&
conv.pointerSetterName != null) {
getterName = conv.pointerGetterName;
setterName = conv.pointerSetterName;
} else {
// if (conv.type == ConvType.NativeLong || conv.cLong) {
// getterName = "getCLong";
// setterName = "setCLong";
// } else if (conv.type == ConvType.NativeSize || conv.nativeSize) {
// getterName = "getSizeT";
// setterName = "setSizeT";
// } else {
getGlobalPointerExpr = methodCall(getGlobalPointerExpr, "as", result.typeConverter.typeLiteral(javaType.clone()));
}
}
if (conv.getFieldExpr != null) {
Function getter = convDecl.clone();
if (isGlobal) {
getter.setBody(block(
tryRethrow(new Statement.Return(cast(javaType.clone(), methodCall(getGlobalPointerExpr, getterName))))));
} else {
getter.setBody(block(
new Statement.Return(conv.getFieldExpr)));
}
out.add(getter);
addedGetterOrSetter = true;
}
if (!conv.readOnly && conv.setFieldExpr != null) {
Function setter = convDecl.clone();
setter.setValueType(typeRef(holderName.clone()));//Void.TYPE));
setter.addArg(new Arg(name, javaType));
//setter.addModifiers(ModifierType.Native);
if (isGlobal) {
setter.setBody(block(
tryRethrow(block(
stat(methodCall(getGlobalPointerExpr, setterName, varRef(name))),
new Statement.Return(thisRef())))));
} else {
setter.setBody(block(
stat(conv.setFieldExpr),
new Statement.Return(thisRef())));
}
out.add(setter);
addedGetterOrSetter = true;
if (result.config.scalaStructSetters) {
setter = new Function();
setter.setType(Type.JavaMethod);
setter.setName(ident(name + "_$eq"));
setter.setValueType(javaType.clone());
setter.addArg(new Arg(name, javaType.clone()));
setter.addModifiers(ModifierType.Public, ModifierType.Final);
setter.setBody(block(
stat(methodCall(name, varRef(name))),
new Statement.Return(varRef(name))));
out.add(setter);
}
}
if (!addedGetterOrSetter) {
out.add(new EmptyDeclaration("Failed to convert value " + name + " of type " + mutatedType));
}
return out;
}
@Override
public boolean convertVariablesDeclaration(VariablesDeclaration v, Signatures signatures, DeclarationsHolder out, int[] iChild, boolean isGlobal, Identifier holderName, Identifier callerLibraryClass, String callerLibrary) {
try {
TypeRef valueType = v.getValueType();
for (Declarator vs : v.getDeclarators()) {
if (vs.getDefaultValue() != null) {
continue;
}
String name = vs.resolveName();
if (name == null || name.length() == 0) {
name = "anonymous" + (nextAnonymousFieldId++);
}
TypeRef mutatedType = valueType;
if (!(vs instanceof DirectDeclarator)) {
mutatedType = (TypeRef) vs.mutateTypeKeepingParent(valueType);
vs = new DirectDeclarator(vs.resolveName());
}
//Declarator d = v.getDeclarators().get(0);
List<Declaration> vds = convertVariablesDeclarationToBridJ(name, mutatedType, iChild, vs.getBits(), isGlobal, holderName, callerLibraryClass, callerLibrary, v, vs);
if (vs.getBits() > 0) {
for (Declaration vd : vds) {
vd.addAnnotation(new Annotation(result.config.runtime.typeRef(JNAeratorConfig.Runtime.Ann.Bits), expr(vs.getBits())));
}
}
for (Declaration vd : vds) {
if (vd instanceof Function) {
if (!signatures.addMethod((Function) vd)) {
continue;
}
}
vd.importDetails(mutatedType, true);
vd.moveAllCommentsBefore();
if (!(mutatedType instanceof Primitive) && !result.config.noComments) {
vd.addToCommentBefore("C type : " + mutatedType);
}
out.addDeclaration(vd);
}
//}
}
return true;
} catch (Throwable e) {
if (!(e instanceof UnsupportedConversionException)) {
e.printStackTrace();
}
// if (!result.config.limitComments)
out.addDeclaration(new EmptyDeclaration(e.toString()));
return false;
} finally {
iChild[0]++;
}
}
int nextAnonymousFieldId;
@Override
protected void configureCallbackStruct(Struct callbackStruct) {
callbackStruct.setType(Struct.Type.JavaClass);
callbackStruct.addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Abstract);
}
@Override
protected Struct createFakePointerClass(Identifier fakePointer) {
Struct ptClass = result.declarationsConverter.publicStaticClass(fakePointer, ident(TypedPointer.class), Struct.Type.JavaClass, null);
String addressVarName = "address";
ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null,
new Arg(addressVarName, typeRef(long.class))).addModifiers(ModifierType.Public).setBody(
block(stat(methodCall("super", varRef(addressVarName))))));
ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null,
new Arg(addressVarName, typeRef(org.bridj.Pointer.class))).addModifiers(ModifierType.Public).setBody(
block(stat(methodCall("super", varRef(addressVarName))))));
return ptClass;
}
@Override
protected void fillLibraryMapping(Result result, SourceFiles sourceFiles, DeclarationsHolder declarations, DeclarationsHolder implementations, String library, Identifier javaPackage, Expression nativeLibFieldExpr) throws IOException {
super.fillLibraryMapping(result, sourceFiles, declarations, implementations, library, javaPackage, nativeLibFieldExpr);
if (implementations instanceof ModifiableElement) {
ModifiableElement minterf = (ModifiableElement) implementations;
List<String> deps = result.config.dependencies.get(library);
Map<String, Expression> namedArguments = null;
if (deps != null) {
List<Expression> depExprs = new ArrayList<Expression>();
for (String dep : deps)
depExprs.add(expr(dep));
namedArguments = ImmutableMap.<String, Expression>of(
"dependencies",
NewArray.newAnnotationArrayValue(depExprs));
}
minterf.addAnnotation(new Annotation(typeRef(org.bridj.ann.Library.class), expr(library), namedArguments));
minterf.addAnnotation(new Annotation(typeRef(org.bridj.ann.Runtime.class), classLiteral(result.hasCPlusPlus ? CPPRuntime.class : CRuntime.class)));
}
}
@Override
public void generateLibraryFiles(SourceFiles sourceFiles, Result result, JNAeratorConfig config) throws IOException {
for (String library : result.libraries) {
if (library == null) {
continue; // to handle code defined in macro-expanded expressions
}
Identifier javaPackage = result.javaPackageByLibrary.get(library);
Identifier implementationsSimpleClassName = result.getLibraryClassSimpleName(library);
Identifier declarationsSimpleClassName = result.getLibraryDeclarationsClassSimpleName(library);
Identifier implementationsFullClassName = result.getLibraryClassFullName(library);//ident(javaPackage, libraryClassName);
Identifier declarationsFullClassName = result.getLibraryDeclarationsClassFullName(library);
//if (!result.objCClasses.isEmpty())
// out.println("import org.rococoa.ID;");
Struct implementations = new Struct();
implementations.setType(Struct.Type.JavaClass);
implementations.addToCommentBefore("Wrapper for library <b>" + library + "</b>",
result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null));
implementations.addModifiers(ModifierType.Public);
implementations.setTag(implementationsSimpleClassName);
implementations.addParent(ident(config.runtime.libraryClass, expr(typeRef(implementationsSimpleClassName))));
if (declarationsFullClassName != null) {
implementations.addProtocol(declarationsFullClassName.clone());
}
if (!config.noStaticInit)
implementations.addDeclaration(newStaticInit());
implementations.setResolvedJavaIdentifier(implementationsFullClassName);
Struct declarations;
if (declarationsFullClassName != null) {
declarations = new Struct();
declarations.setType(Struct.Type.JavaInterface);
declarations.addToCommentBefore("Interface for library <b>" + library + "</b>",
result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null));
declarations.addModifiers(ModifierType.Public);
declarations.setTag(declarationsSimpleClassName.clone());
declarations.setResolvedJavaIdentifier(declarationsFullClassName);
} else {
declarations = implementations;
}
// String libFileOrDirArgName = "libraryFileOrDirectory";
// Function constr = new Function(Function.Type.JavaMethod, fullLibraryClassName.resolveLastSimpleIdentifier().clone(), null, new Arg(libFileOrDirArgName, typeRef(File.class)));
// constr.addModifiers(ModifierType.Public);
// constr.setBody(block(stat(methodCall("super", varRef(libFileOrDirArgName)))));
// interf.addDeclaration(constr);
//
// constr = new Function(Function.Type.JavaMethod, fullLibraryClassName.resolveLastSimpleIdentifier().clone(), null);
// constr.addModifiers(ModifierType.Public);
// constr.addThrown(typeRef(FileNotFoundException.class));
// constr.setBody(block(stat(methodCall("super", classLiteral(typeRef(fullLibraryClassName.clone()))))));
// interf.addDeclaration(constr);
fillLibraryMapping(result, sourceFiles, declarations, implementations, library, javaPackage, varRef("this"));
writeLibraryInterface(result, sourceFiles, declarations, library, javaPackage);
if (declarations != implementations) {
writeLibraryInterface(result, sourceFiles, implementations, library, javaPackage);
}
}
}
private Function newStaticInit() {
Function f = new Function(Function.Type.StaticInit, null, null).setBody(block(
stat(methodCall(
expr(typeRef(BridJ.class)),
Expression.MemberRefStyle.Dot,
"register")))).addModifiers(ModifierType.Static);
return f;
}
private void addParentNamespaceAnnotation(ModifiableElement dest, Identifier parentNamespace) {
if (parentNamespace != null) {
dest.addAnnotation(new Annotation(typeRef(org.bridj.ann.Namespace.class), expr(parentNamespace.toString())));
}
}
}