
com.ochafik.lang.jnaerator.ObjectiveCGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnaerator Show documentation
Show all versions of jnaerator Show documentation
JNAerator (pronounce "generator") simply parses C and Objective-C headers and generates the corresponding JNA and Rococoa Java interfaces (it also has a very limited support for C++).
This lets Java programmers access native libraries transparently, with full IDE support and little to no hand-tweaking.
Users who are looking for ready-to-use libraries should check the NativeLibs4Java project instead.
/*
Copyright (c) 2009-2011 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 .
*/
package com.ochafik.lang.jnaerator;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import com.ochafik.util.string.StringUtils;
import org.rococoa.cocoa.foundation.NSObject;
//import org.rococoa.cocoa.foundation.NSString;
import org.rococoa.Rococoa;
import org.rococoa.ObjCObject;
import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*;
import com.ochafik.io.ReadText;
import com.ochafik.lang.jnaerator.parser.Arg;
import com.ochafik.lang.jnaerator.parser.Declaration;
import com.ochafik.lang.jnaerator.parser.DeclarationsHolder;
import com.ochafik.lang.jnaerator.parser.Declarator;
import com.ochafik.lang.jnaerator.parser.Enum;
import com.ochafik.lang.jnaerator.parser.Expression;
import com.ochafik.lang.jnaerator.parser.Function;
import com.ochafik.lang.jnaerator.parser.Identifier;
import com.ochafik.lang.jnaerator.parser.Modifier;
import com.ochafik.lang.jnaerator.parser.ModifierType;
import com.ochafik.lang.jnaerator.parser.Statement;
import com.ochafik.lang.jnaerator.parser.StoredDeclarations;
import com.ochafik.lang.jnaerator.parser.Struct;
import com.ochafik.lang.jnaerator.parser.TaggedTypeRefDeclaration;
import com.ochafik.lang.jnaerator.parser.TypeRef;
import com.ochafik.lang.jnaerator.parser.VariablesDeclaration;
import com.ochafik.lang.jnaerator.parser.Expression.AssignmentOp;
import com.ochafik.lang.jnaerator.parser.Expression.AssignmentOperator;
import com.ochafik.lang.jnaerator.parser.Expression.BinaryOperator;
import com.ochafik.lang.jnaerator.parser.Expression.Constant;
import com.ochafik.lang.jnaerator.parser.Expression.MemberRefStyle;
import com.ochafik.lang.jnaerator.parser.Statement.Block;
import com.ochafik.lang.jnaerator.parser.StoredDeclarations.TypeDef;
import com.ochafik.lang.jnaerator.parser.Struct.Type;
import com.ochafik.lang.jnaerator.parser.TypeRef.FunctionSignature;
import com.ochafik.lang.jnaerator.parser.TypeRef.SimpleTypeRef;
import com.ochafik.lang.jnaerator.parser.TypeRef.TaggedTypeRef;
import java.util.*;
import org.rococoa.ObjCClass;
import org.rococoa.cocoa.foundation.NSClass;
/*
include com/ochafik/lang/jnaerator/ObjectiveCStaticForwardsExcludeList.data
include com/ochafik/lang/jnaerator/ObjectiveCProtocolsForcedInheritanceList.data
*/
public class ObjectiveCGenerator {
String
classClassName = "_class_",
classInterfaceNameInCategoriesAndProtocols = "_static_",
classInstanceName = "_NSCLASS_",
classInstanceGetterName = "getNSClass";
boolean AUTO_RELEASE_IN_FACTORIES = false;
static Map> protocolsForcedInheritance;
public static Set getForcedProtocolParents(String protocolName) {
if (protocolsForcedInheritance == null) {
protocolsForcedInheritance = new HashMap>();
try {
InputStream in = ObjectiveCGenerator.class.getClassLoader().getResourceAsStream("com/ochafik/lang/jnaerator/ObjectiveCProtocolsForcedInheritanceList.data");
List lines = ReadText.readLines(in);
for (String line : lines) {
line = line.trim();
if (line.startsWith("//") || line.startsWith("#") || line.length() == 0)
continue;
String[] tks = line.split(":");
protocolsForcedInheritance.put(tks[0], new TreeSet(Arrays.asList(tks[1].split(","))));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
Set ret = protocolsForcedInheritance.get(protocolName);
return ret == null ? Collections.EMPTY_SET : ret;
}
static Map> methodsExcludedFromStaticForwarding;
public static boolean isMethodExcludedFromStaticForwarding(Function m) {
if (methodsExcludedFromStaticForwarding == null) {
methodsExcludedFromStaticForwarding = new HashMap>();
try {
InputStream in = ObjectiveCGenerator.class.getClassLoader().getResourceAsStream("com/ochafik/lang/jnaerator/ObjectiveCStaticForwardsExcludeList.data");
// InputStream in = new FileInputStream("/Users/ochafik/Prog/Java/sources/com/ochafik/lang/jnaerator/ObjectiveCStaticForwardsExcludeList.data");
List lines = ReadText.readLines(in);
for (String line : lines) {
line = line.trim();
if (line.startsWith("//") || line.startsWith("#") || line.length() == 0)
continue;
String[] tks = line.split("\\|");
String className = tks[0].trim(), methodSignature = tks[1].trim();
Set set = methodsExcludedFromStaticForwarding.get(className);
if (set == null)
methodsExcludedFromStaticForwarding.put(className, set = new HashSet());
set.add(methodSignature);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (!(m.getParentElement() instanceof Struct))
return false;
Struct s = (Struct)m.getParentElement();
Identifier n = s.getTag();
if (n != null && n.equals("NSObject"))// || n.equals("NSClass"))
return true;
String sig = m.computeSignature(false);
if (DeclarationsConverter.getMethodsAndTheirSignatures(NSObject.class).getSecond().contains(sig))
return true;
String cn = s.getTag() == null ? "" : s.getTag().toString();
Set set = methodsExcludedFromStaticForwarding.get(cn);
if (set != null && set.contains(sig))
return true;
set = methodsExcludedFromStaticForwarding.get("");
if (set != null && set.contains(sig))
return true;
return false;
}
public ObjectiveCGenerator(Result result) {
this.result = result;
}
final Result result;
public Struct getStruct(Identifier className, Struct.Type type) {
return Result.getMap(result.classes, type).get(className);
}
public Identifier getPackageName(Struct struct) {
if (struct == null)
return null;
String library = result.getLibrary(struct);
Identifier javaPackage = result.getLibraryPackage(library);
//if (struct.getType() == Struct.Type.ObjCClass) {
// String name = String.valueOf(struct.getTag());
//if (name.equals("NSObject"))
// javaPackage = ident(NSObject.class.getPackage().getName().split("\\."));
//else if (name.equals("NSClass"))
// javaPackage = ident(NSClass.class.getPackage().getName().split("\\."));
//else if (name.equals("NSString"))
// javaPackage = ident(NSString.class.getPackage().getName().split("\\."));
//}
if (struct.getType() == Type.ObjCProtocol)
javaPackage = ident(javaPackage, "protocols");
else if (struct.getCategoryName() != null)
javaPackage = ident(javaPackage, "categories");
return javaPackage;
}
public Identifier getFullClassName(Struct struct) {
if (struct == null)
return null;
Identifier javaPackage = getPackageName(struct);
Identifier tag = struct.getTag();
String categ = struct.getCategoryName();
return ident(javaPackage, categ == null ? tag.clone() : ident(categ));
}
public void generateObjectiveCClasses() throws IOException {
for (Struct in : Result.getMap(result.classes, Type.ObjCClass).values()) {
outputObjectiveCClass(in);
}
for (Struct protocol : Result.getMap(result.classes, Type.ObjCProtocol).values()) {
for (String parent : getForcedProtocolParents(String.valueOf(protocol.getTag())))
protocol.addParent(ident(parent));
outputObjectiveCClass(protocol);
}
}
public void outputObjectiveCClass(Struct in) throws IOException {
Identifier fullClassName = getFullClassName(in);
Signatures signatures = new Signatures();
Struct s = generateObjectiveCClass(in, signatures);
result.notifyBeforeWritingClass(fullClassName, s, signatures, result.getLibrary(in));
PrintWriter out = result.classOutputter.getClassSourceWriter(fullClassName.toString());
result.printJavaClass(getPackageName(in), s, out);
out.close();
}
static Identifier
NSObjectIdent = ident(NSObject.class),
ObjCObjectIdent = ident(ObjCObject.class),
ObjCClassIdent = ident(ObjCClass.class);
//NSClassIdent = ident(NSClass.class);
public Struct generateObjectiveCClass(Struct in, Signatures signatures) throws IOException {
boolean isProtocol = in.getType() == Type.ObjCProtocol, isCategory = in.getCategoryName() != null;
Struct instanceStruct = new Struct().addModifiers(ModifierType.Public);
instanceStruct.setCommentBefore(in.getCommentBefore());
instanceStruct.addToCommentBefore(in.getCommentAfter());
instanceStruct.setTag(isCategory ? ident(in.getCategoryName()) : in.getTag().clone());
if (isProtocol || isCategory)
instanceStruct.setType(Type.JavaInterface);
else
instanceStruct.addModifiers(ModifierType.Abstract).setType(Type.JavaClass);
Struct classStruct = new Struct();
classStruct.setTag(ident(classClassName));
classStruct.setType(Struct.Type.JavaClass);
classStruct.addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Abstract);
List
interfacesForInstance = new ArrayList();
List
parentsForInstance = new ArrayList(in.getParents());
//for (Identifier p : parentsForInstance)
// parentsForClass
boolean isNSObject = in.getTag().equals(NSObject.class.getSimpleName());
/*if (parentsForInstance.isEmpty()) {
if (isProtocol || isCategory)
parentsForInstance.add(ObjCObjectIdent);
else
if (!isNSObject)
parentsForInstance.add(NSObjectIdent);
}*/
//interfacesForClass.add(ObjCClassIdent);
if (!(isCategory || isProtocol))
for (Struct catIn : Result.getMap(result.objCCategoriesByTargetType, in.getTag()).values()) {
Identifier catId = getFullClassName(catIn);
Identifier sim = catId.resolveLastSimpleIdentifier();
String categName = catIn.getTag() + "_" + sim;
if (add(instanceStruct, createCastMethod(ident(categName), catId, false), signatures))
classStruct.addDeclaration(createCastMethod(ident(categName), ident(catId, classInterfaceNameInCategoriesAndProtocols), true));
//interfacesForInstance.add(catId);
//interfacesForClass.add(ident(catId, classInterfaceNameInCategoriesAndProtocols));
outputObjectiveCClass(catIn);
}
for (SimpleTypeRef sp : parentsForInstance) {
Identifier p = sp.getName();
String ps = p.toString();
boolean basic = ps.toString().equals(ObjCObject.class.getName()) || ps.equals(NSObject.class.getName());
Identifier id = basic ? p : result.typeConverter.findObjCClassIdent(p);
//Identifier id = result.typeConverter.findObjCClassIdent(p);
if (id != null || (!p.isPlain() && (id = p) != null)) {
if (ps.toString().equals("NSObject"))
instanceStruct.addProtocol(ident(ObjCObject.class));
else
instanceStruct.addParent(id.clone());
if (!basic)
classStruct.addParent(ident(id, classClassName));
}
}
/*for (Identifier p : interfacesForClass) {
boolean basic = p == ObjCClassIdent || p == NSObjectIdent;
Identifier id = basic ? p : result.typeConverter.findObjCClassIdent(p);
if (id != null)
classStruct.addProtocol(p);
}*/
boolean isInterface = isProtocol || isCategory;
if (instanceStruct.getParents().isEmpty()) {
if (isInterface)
instanceStruct.addParent(ident(ObjCObject.class));
else if (isNSObject)
instanceStruct.addProtocol(ident(ObjCObject.class));
else
instanceStruct.addParent(ident(NSObject.class));
}
if (classStruct.getParents().isEmpty()) {
if (isNSObject)
classStruct.addParent(ident(NSClass.class));
else
classStruct.addParent(ident(ident(NSObject.class), classClassName));
//classStruct.addProtocol(ident(ObjCClass.class));
}
for (SimpleTypeRef p : in.getProtocols()) {
Identifier id = getFullClassName(getStruct(p.getName(), Type.ObjCProtocol));
if (id != null)
interfacesForInstance.add(typeRef(id));
}
for (SimpleTypeRef id : interfacesForInstance) {
if (isProtocol || isCategory)
instanceStruct.addParent(id);
else
instanceStruct.addProtocol(id);
}
// CompoundCollection declarations = new CompoundCollection();
// declarations.addComponent(in.getDeclarations());
// for (Struct catIn : Result.getMap(result.objCCategoriesByTargetType, in.getTag()).values()) {
// for (Declaration d : catIn.getDeclarations())
// d.addToCommentBefore("From category " + catIn.getCategoryName());
// declarations.addComponent(catIn.getDeclarations());
//
// if (catIn.getCommentBefore() != null)
// instanceStruct.addToCommentBefore("Category " + catIn.getTag()+ " : " + catIn.getCommentBefore() +"
");
// }
StoredDeclarations classHolder = new VariablesDeclaration();
if (!(isProtocol || isCategory))
classHolder.addModifiers(ModifierType.Private, ModifierType.Static);
classHolder.setValueType(typeRef(classClassName));
Expression.FunctionCall call = methodCall(expr(typeRef(Rococoa.class)), MemberRefStyle.Dot, "createClass");
call.addArgument(expr(in.getTag().toString()));
call.addArgument(memberRef(expr(typeRef(classClassName)), MemberRefStyle.Dot, "class"));
Function classGetter;
if (isProtocol || isCategory) {
classGetter = null;
classHolder.addDeclarator(new Declarator.DirectDeclarator(classInstanceName, call));
} else {
classHolder.addDeclarator(new Declarator.DirectDeclarator(classInstanceName));
classGetter = new Function(Function.Type.JavaMethod, ident(classInstanceGetterName), typeRef(classClassName));
classGetter.addModifiers(ModifierType.Public, ModifierType.Static);
classGetter.setBody(new Block(
new Statement.If(
expr(
varRef(classInstanceName),
BinaryOperator.IsEqual,
Constant.newNull()
),
new Statement.ExpressionStatement(
new AssignmentOp(
varRef(classInstanceName),
AssignmentOperator.Equal,
call
)
),
null
),
new Statement.Return(varRef(classInstanceName))
));
}
Struct classInterfaceStruct = null, structThatReceivesStaticMethods;
if (isProtocol || isCategory) {
structThatReceivesStaticMethods = classInterfaceStruct = new Struct();
classInterfaceStruct.setType(Struct.Type.JavaInterface);
classInterfaceStruct.setTag(ident(classInterfaceNameInCategoriesAndProtocols));
classInterfaceStruct.addParent(ident(ObjCClass.class));
classStruct.addProtocol(ident(classInterfaceNameInCategoriesAndProtocols));
} else
structThatReceivesStaticMethods = classStruct;
if (!(isProtocol || isCategory)) {
addAllocIfMissing(in, "alloc");
addAllocIfMissing(in, "new_");
}
outputMembers(signatures, in, instanceStruct, structThatReceivesStaticMethods, in.getDeclarations(), isProtocol || isCategory);
Identifier fullClassName = getFullClassName(in);
/*
if (!isProtocol && !isCategory) {
// Output static proxies for static category methods
for (Struct catIn : Result.getMap(result.objCCategoriesByTargetType, in.getTag()).values()) {
for (Declaration d : catIn.getDeclarations()) {
if (!(d instanceof Function))
continue;
Function f = (Function)d;
if (!f.hasModifier(ModifierType.Static))
continue;
List decls = new ArrayList();
result.declarationsConverter.convertFunction(f, null, false, new DeclarationsHolder.ListWrapper(decls), fullClassName);
if (f.getModifiers().contains(ModifierType.Static)) {
for (Declaration decl : decls) {
if (!(decl instanceof Function))
continue;
Function pf = (Function)decl;
if (!signatures.methodsSignatures.add(pf.computeSignature(false)))
continue;
//if (!add(classStruct, decl, signatures, objSigs, clasSigs))
// continue;
instanceStruct.addDeclaration(createProxyCopy(pf, (Function)decl));
}
}
}
}
}*/
instanceStruct.addDeclaration(decl(classInterfaceStruct));
if (!isCategory) {// && !structThatReceivesStaticMethods.getDeclarations().isEmpty()) {
instanceStruct.addDeclaration(new TaggedTypeRefDeclaration(classStruct));
instanceStruct.addDeclaration(classGetter);
instanceStruct.addDeclaration(classHolder);
}
return instanceStruct;
}
Function createCastMethod(Identifier name, Identifier classId, boolean isStatic) {
Function m = new Function();
m.setType(Function.Type.JavaMethod);
m.addModifiers(ModifierType.Public);
m.setName(ident("as" + (isStatic ? "Static_" : "_") + name));
m.setValueType(typeRef(classId.clone()));
m.setBody(block(
new Statement.Return(
methodCall(
expr(typeRef(Rococoa.class)),
MemberRefStyle.Dot,
"cast",
varRef("this"),
result.typeConverter.typeLiteral(typeRef(classId.clone()))
)
)
));
return m;
}
private void addAllocIfMissing(Struct in, String allocName) {
Identifier n = in.getTag();
if (n.equals("NSObject") || n.equals("NSClass"))
return;
boolean hasAlloc = false;
for (Declaration d : in.getDeclarations()) {
if (d instanceof Function) {
Function f = (Function)d;
if (f.getArgs().isEmpty() && allocName.equals(f.getName())) {
hasAlloc = true;
break;
}
}
}
if (!hasAlloc)
in.addDeclaration(new Function(Function.Type.ObjCMethod, ident(allocName), typeRef(in.getTag())).addModifiers(ModifierType.Static));
}
private void outputMembers(Signatures signatures, Struct in, Struct instanceStruct,
Struct classStruct, List declarations, boolean isProtocol) throws IOException {
Identifier fullClassName = getFullClassName(in);
Set objSigs = DeclarationsConverter.getMethodsAndTheirSignatures(NSObject.class).getSecond(),
clasSigs = new HashSet();//DeclarationsConverter.getMethodsAndTheirSignatures(NSClass.class).getSecond();
int[] iChild = new int[1];
for (Declaration d : declarations) {
if (d instanceof Function) {
Function f = (Function)d;
List decls = new ArrayList();
result.declarationsConverter.convertFunction(f, null/*signatures*/, false, new DeclarationsHolder.ListWrapper(decls), fullClassName, -1);
if (f.hasModifier(ModifierType.Static)) {
for (Declaration decl : decls) {
if (!add(classStruct, decl, signatures, objSigs, clasSigs))
continue;
if (!isProtocol && decl instanceof Function) {
instanceStruct.addDeclaration(createProxyCopy(f, (Function)decl));
signatures.addMethod((Function)decl);
}
if (classStruct.getType() == Type.JavaClass) {
decl.addModifiers(ModifierType.Public, ModifierType.Abstract);
decl.reorganizeModifiers();
}
}
} else {
for (Declaration decl : decls) {
if (!add(instanceStruct, decl, signatures, objSigs))
continue;
if (!isProtocol && decl instanceof Function) {
//
Function addedF = createCreateCopyFromInit((Function)decl, instanceStruct);
signatures.addMethod((Function)decl);
instanceStruct.addDeclaration(addedF);
if (instanceStruct.getType() == Type.JavaClass) {
decl.addModifiers(ModifierType.Public, ModifierType.Abstract);
decl.reorganizeModifiers();
}
}
}
}
// } else if (d instanceof VariablesDeclaration) {
// result.declarationsConverter.convertVariablesDeclaration((VariablesDeclaration)d, instanceStruct, iChild, fullClassName);
} else if (d instanceof TaggedTypeRefDeclaration) {
TaggedTypeRef tr = ((TaggedTypeRefDeclaration) d).getTaggedTypeRef();
if (tr instanceof Struct) {
result.declarationsConverter.outputConvertedStruct((Struct)tr, signatures, instanceStruct, fullClassName, null, false);
} else if (tr instanceof Enum) {
result.declarationsConverter.convertEnum((Enum)tr, signatures, instanceStruct, fullClassName);
}
} else if (d instanceof TypeDef) {
TypeDef td = (TypeDef)d;
TypeRef tr = td.getValueType();
if (tr instanceof Struct) {
result.declarationsConverter.outputConvertedStruct((Struct)tr, signatures, instanceStruct, fullClassName, null, false);
} else if (tr instanceof FunctionSignature) {
result.declarationsConverter.convertCallback((FunctionSignature)tr, signatures, instanceStruct, fullClassName);
}
}
iChild[0]++;
}
}
private boolean add(Struct classStruct, Declaration decl, Signatures signatures, Set>... additionalMethodSignatures) {
if (decl instanceof Function) {
String sig = ((Function)decl).computeSignature(false);
for (Set> sigs : additionalMethodSignatures)
if (sigs.contains(sig))
return false;
if (signatures.addMethod(sig)) {
classStruct.addDeclaration(decl);
return true;
} else
return false;
}
classStruct.addDeclaration(decl);
return true;
}
/**
* Create a createXXXWithYYY factory
* @param meth
* @param instanceStruct
* @return
*/
private Function createCreateCopyFromInit(Function meth, TaggedTypeRef instanceStruct) {
String name = meth.getName().toString();
if (!name.matches("^init([A-Z].*|)$"))
return null;
//if (name.startsWith("init") && (name.equals("init") || !Character.isUpperCase(name.charAt("init".length()))))
Function createCopy = meth.clone();
createCopy.setCommentBefore("Factory method");
createCopy.addToCommentBefore("@see #" + meth.computeSignature(false));
createCopy.setName(ident("create" + name.substring("init".length())));
createCopy.addModifiers(ModifierType.Public, ModifierType.Static);
createCopy.reorganizeModifiers();
Expression[] args = new Expression[meth.getArgs().size()];
int i = 0;
for (Arg arg : meth.getArgs())
args[i++] = varRef(arg.getName());
Expression val = methodCall(
methodCall(
methodCall(null, null, classInstanceGetterName),
Expression.MemberRefStyle.Dot,
"alloc"
),
Expression.MemberRefStyle.Dot,
meth.getName().toString(),
args
);
if (AUTO_RELEASE_IN_FACTORIES) {
val = methodCall(val, MemberRefStyle.Dot, "autorelease");
val =
methodCall(expr(typeRef(Rococoa.class)), MemberRefStyle.Dot, "cast",
val,
memberRef(expr(typeRef(instanceStruct.getTag())), MemberRefStyle.Dot, "class")
)
;
}
createCopy.setBody(new Block(new Statement.Return(val)));
return createCopy;
}
private Function createProxyCopy(Function originalMethod, Function meth) {
if (isMethodExcludedFromStaticForwarding(originalMethod))
return null;
Function proxyCopy = meth.clone();
proxyCopy.addModifiers(ModifierType.Public, ModifierType.Static);
proxyCopy.reorganizeModifiers();
Expression[] args = new Expression[meth.getArgs().size()];
int i = 0;
for (Arg arg : meth.getArgs())
args[i++] = varRef(arg.getName());
Expression val = methodCall(methodCall(null, null, classInstanceGetterName), Expression.MemberRefStyle.Dot, meth.getName().toString(), args);
proxyCopy.setBody(new Block(
meth.getValueType() == null || "void".equals(meth.getValueType().toString()) ? stat(val) : new Statement.Return(val)
));
return proxyCopy;
}
// protected static _class_ _CLASS_ = org.rococoa.Rococoa.createClass("NSURL", _class_.class);
// public abstract class _class_ extends
// org.rococoa.NSClass {
// //org.rococoa.NSObject._class_ {
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy