Cpp header file generation

This commit is contained in:
aFedasenka 2012-10-05 00:21:12 +02:00
parent 63308d89af
commit c575ae7ccb
3 changed files with 962 additions and 8 deletions

View file

@ -8,9 +8,9 @@ import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.CompilationUnit;
import com.google.common.io.Files;
import com.google.devtools.j2cpp.gen.CppHeaderGenerator;
import com.google.devtools.j2objc.J2ObjC.Language;
import com.google.devtools.j2objc.gen.ObjectiveCHeaderGenerator;
import com.google.devtools.j2objc.gen.ObjectiveCImplementationGenerator;
//import com.google.devtools.j2objc.gen.ObjectiveCImplementationGenerator;
public abstract class CppGenerationTest extends GenerationTest {
@ -22,10 +22,11 @@ public abstract class CppGenerationTest extends GenerationTest {
System.out.println("-----------------------------HEADER-----------------");
System.out.println(getTranslatedFile(type+".h"));
System.out.println();
System.out.println();
System.out.println("------------------------------------------------");
System.out.println("-------------------SOURCE-------------------");
System.out.println(getTranslatedFile(type+".m"));
// TODO commented for testing a header only
// System.out.println();
// System.out.println("------------------------------------------------");
// System.out.println("-------------------SOURCE-------------------");
// System.out.println(getTranslatedFile(type+".m"));
}
protected void assertNoCompilationErrors(CompilationUnit unit) {
@ -43,7 +44,7 @@ public abstract class CppGenerationTest extends GenerationTest {
CompilationUnit unit = translateType(typeName, source);
assertNoCompilationErrors(unit);
String sourceName = typeName + ".java";
ObjectiveCHeaderGenerator.generate(sourceName, source, unit);
ObjectiveCImplementationGenerator.generate(sourceName, Language.OBJECTIVE_C, unit, source);
CppHeaderGenerator.generate(sourceName, source, unit);
// ObjectiveCImplementationGenerator.generate(sourceName, Language.OBJECTIVE_C, unit, source);
}
}

View file

@ -0,0 +1,600 @@
package com.google.devtools.j2cpp.gen;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.BlockComment;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.devtools.j2objc.J2ObjC;
import com.google.devtools.j2objc.Options;
import com.google.devtools.j2objc.types.HeaderImportCollector;
import com.google.devtools.j2objc.types.IOSMethod;
import com.google.devtools.j2objc.types.ImportCollector;
import com.google.devtools.j2objc.types.Types;
import com.google.devtools.j2objc.util.ErrorReportingASTVisitor;
import com.google.devtools.j2objc.util.NameTable;
import com.google.devtools.j2objc.util.UnicodeUtils;
public class CppHeaderGenerator extends CppSourceFileGenerator{
/**
* Generate an C++ header file for each type declared in a specified
* compilation unit.
*/
public static void generate(String fileName, String source, CompilationUnit unit) {
CppHeaderGenerator headerGenerator = new CppHeaderGenerator(fileName, source, unit);
headerGenerator.generate(unit);
}
private CppHeaderGenerator(String fileName, String source, CompilationUnit unit) {
super(fileName, source, unit, false);
}
@Override
protected String getSuffix() {
return ".h";
}
public void generate(CompilationUnit unit) {
println(J2ObjC.getFileHeader(getSourceFileName()));
@SuppressWarnings("unchecked")
List<AbstractTypeDeclaration> types = unit.types(); // safe by definition
Set<ITypeBinding> moreForwardTypes = sortTypes(types);
//TODO
printImportsAndForwardReferences(unit, moreForwardTypes);
for (AbstractTypeDeclaration type : types) {
newline();
generate(type);
}
save(unit);
}
@Override
public void generate(TypeDeclaration node) {
String typeName = NameTable.getFullName(node);
String superName = NameTable.getSuperClassName(node);
//DONE if extends superclass other than Object
//TODO if implements interfaces
if ((superName != null) && (!superName.equals("NSObject"))) {
printf(": public %s", superName);
}
printConstantDefines(node);
printf("class %s", typeName);
println(" {");
printInstanceVariables(node.getFields());
MethodDeclaration[] allMethods = node.getMethods();
List<MethodDeclaration> publicMethods = new ArrayList<MethodDeclaration>();
List<MethodDeclaration> protectedMethods = new ArrayList<MethodDeclaration>();
List<MethodDeclaration> privateMethods = new ArrayList<MethodDeclaration>();
for (MethodDeclaration m : allMethods) {
if (Modifier.isPublic(m.getModifiers())) {
publicMethods.add(m);
} else if (Modifier.isProtected(m.getModifiers())) {
protectedMethods.add(m);
} else if (Modifier.isPrivate(m.getModifiers())) {
privateMethods.add(m);
}
}
if (publicMethods.size() > 0) {
println("\npublic: ");
printMethods(publicMethods);
}
if (protectedMethods.size() > 0) {
println("\nprotected: ");
printMethods(protectedMethods);
}
if (privateMethods.size() > 0) {
println("\nprivate: ");
printMethods(privateMethods);
}
println("};\n");
}
@Override
protected void generate(AnnotationTypeDeclaration node) {
String typeName = NameTable.getFullName(node);
printf("@protocol %s < NSObject >\n@end\n", typeName);
}
@Override
protected void generate(EnumDeclaration node) {
printConstantDefines(node);
String typeName = NameTable.getFullName(node);
@SuppressWarnings("unchecked")
List<EnumConstantDeclaration> constants = node.enumConstants();
// C doesn't allow empty enum declarations. Java does, so we skip the
// C enum declaration and generate the type declaration.
if (!constants.isEmpty()) {
println("typedef enum {");
// Strip enum type suffix.
String bareTypeName = typeName.endsWith("Enum") ?
typeName.substring(0, typeName.length() - 4) : typeName;
// Print C enum typedef.
indent();
int ordinal = 0;
for (EnumConstantDeclaration constant : constants) {
printIndent();
printf("%s_%s = %d,\n", bareTypeName, constant.getName().getIdentifier(), ordinal++);
}
unindent();
printf("} %s;\n\n", bareTypeName);
}
List<FieldDeclaration> fields = Lists.newArrayList();
List<MethodDeclaration> methods = Lists.newArrayList();
for (Object decl : node.bodyDeclarations()) {
if (decl instanceof FieldDeclaration) {
fields.add((FieldDeclaration) decl);
} else if (decl instanceof MethodDeclaration) {
methods.add((MethodDeclaration) decl);
}
}
// Print enum type.
printf("@interface %s : JavaLangEnum < NSCopying", typeName);
ITypeBinding enumType = Types.getTypeBinding(node);
for (ITypeBinding intrface : enumType.getInterfaces()) {
if (!intrface.getName().equals(("Cloneable"))) { // Cloneable handled below.
printf(", %s", NameTable.getFullName(intrface));
}
}
println(" > {");
FieldDeclaration[] fieldDeclarations = fields.toArray(new FieldDeclaration[0]);
printInstanceVariables(fieldDeclarations);
println("}");
// printProperties(fieldDeclarations);
for (EnumConstantDeclaration constant : constants) {
printf("+ (%s *)%s;\n", typeName, NameTable.getName(constant.getName()));
}
println("+ (IOSObjectArray *)values;");
printf("+ (%s *)valueOfWithNSString:(NSString *)name;\n", typeName);
println("- (id)copyWithZone:(NSZone *)zone;");
printMethods(methods);
println("@end");
}
@Override
protected String methodDeclaration(MethodDeclaration m) {
return super.methodDeclaration(m) + ";\n";
}
@Override
protected String mappedMethodDeclaration(MethodDeclaration method, IOSMethod mappedMethod) {
return super.mappedMethodDeclaration(method, mappedMethod) + ";\n";
}
@Override
protected String constructorDeclaration(MethodDeclaration m) {
return super.constructorDeclaration(m) + ";\n";
}
@Override
protected void printStaticConstructorDeclaration(MethodDeclaration m) {
// Don't do anything.
}
@Override
protected void printMethod(MethodDeclaration m) {
IMethodBinding binding = Types.getMethodBinding(m);
if (!binding.isSynthetic()) {
super.printMethod(m);
}
}
private void printImportsAndForwardReferences(CompilationUnit unit, Set<ITypeBinding> forwards) {
HeaderImportCollector collector = new HeaderImportCollector();
collector.collect(unit, getSourceFileName());
Set<ImportCollector.Import> imports = collector.getImports();
Set<ImportCollector.Import> superTypes = collector.getSuperTypes();
// Print forward declarations.
Set<String> forwardStmts = Sets.newTreeSet();
for (ImportCollector.Import imp : imports) {
forwardStmts.add(createForwardDeclaration(imp.getTypeName(), imp.isInterface()));
}
for (ITypeBinding forward : forwards) {
forwardStmts.add(
createForwardDeclaration(NameTable.getFullName(forward), forward.isInterface()));
}
if (!forwardStmts.isEmpty()) {
for (String stmt : forwardStmts) {
println(stmt);
}
newline();
}
// Print native imports.
int endOfImportText = unit.types().isEmpty() ? unit.getLength()
: ((ASTNode) unit.types().get(0)).getStartPosition();
@SuppressWarnings("unchecked")
List<Comment> comments = unit.getCommentList(); // safe by definition
for (Comment c : comments) {
int start = c.getStartPosition();
if (start >= endOfImportText) {
break;
}
if (c instanceof BlockComment) {
String nativeImport = extractNativeCode(start, c.getLength());
if (nativeImport != null) { // if it has a JSNI section
println(nativeImport.trim());
}
}
}
// Print collected imports.
// println("#import \"JreEmulation.h\"");
if (!superTypes.isEmpty()) {
Set<String> importStmts = Sets.newTreeSet();
for (ImportCollector.Import imp : superTypes) {
importStmts.add(createImport(imp));
}
for (String stmt : importStmts) {
println(stmt);
}
}
}
protected String createForwardDeclaration(String typeName, boolean isInterface) {
return String.format("@%s %s;", isInterface ? "protocol" : "class", typeName);
}
protected String createImport(ImportCollector.Import imp) {
return String.format("#include \"%s.h\"", imp.getImportFileName());
}
/**
* If an inner type is defined before any of its super types are, put the
* super types first. Otherwise, keep the order as is, to make debugging
* easier. For field and method references to a following type, add a
* forward type.
*
* @return the set of forward types to declare.
*/
static Set<ITypeBinding> sortTypes(List<AbstractTypeDeclaration> types) {
final List<AbstractTypeDeclaration> typesCopy =
new ArrayList<AbstractTypeDeclaration>(types);
final Map<String, AbstractTypeDeclaration> index = Maps.newHashMap();
for (AbstractTypeDeclaration type : typesCopy) {
index.put(Types.getTypeBinding(type).getBinaryName(), type);
}
final Multimap<String, String> references = HashMultimap.create();
final Multimap<String, String> superTypes = HashMultimap.create();
// Collect all references to other types, but track super types
// separately from other references.
for (AbstractTypeDeclaration type : typesCopy) {
final String typeName = Types.getTypeBinding(type).getBinaryName();
ErrorReportingASTVisitor collector = new ErrorReportingASTVisitor() {
protected void addSuperType(Type type) {
ITypeBinding binding = type == null ? null : Types.getTypeBinding(type);
if (binding != null && isMember(binding)) {
superTypes.put(typeName, binding.getBinaryName());
}
}
private void addReference(Type type) {
ITypeBinding binding = type == null ? null : Types.getTypeBinding(type);
if (binding != null && isMember(binding)) {
references.put(typeName, binding.getBinaryName());
}
}
// Only collect references to types members.
private boolean isMember(ITypeBinding binding) {
return index.containsKey(binding.getBinaryName());
}
@Override
public boolean visit(FieldDeclaration node) {
addReference(node.getType());
return true;
}
@Override
public boolean visit(MethodDeclaration node) {
addReference(node.getReturnType2());
@SuppressWarnings("unchecked")
List<SingleVariableDeclaration> params = node.parameters();
for (SingleVariableDeclaration param : params) {
addReference(param.getType());
}
return true;
}
@Override
public boolean visit(TypeDeclaration node) {
ITypeBinding binding = Types.getTypeBinding(node);
if (binding.isEqualTo(Types.getNSObject())) {
return false;
}
addSuperType(node.getSuperclassType());
for (Iterator<?> iterator = node.superInterfaceTypes().iterator(); iterator.hasNext();) {
Object o = iterator.next();
if (o instanceof Type) {
addSuperType((Type) o);
} else {
throw new AssertionError("unknown AST type: " + o.getClass());
}
}
return true;
}
};
collector.run(type);
}
// Do a topological sort on the types declared in this unit, with an edge
// in the graph denoting a type inheritance. Super types will end up
// higher up in the sort.
types.clear();
LinkedHashSet<AbstractTypeDeclaration> rootTypes = Sets.newLinkedHashSet();
for (AbstractTypeDeclaration type : typesCopy) {
String name = Types.getTypeBinding(type).getBinaryName();
if (!superTypes.containsValue(name)) {
rootTypes.add(type);
}
}
while (!rootTypes.isEmpty()) {
AbstractTypeDeclaration type =
(AbstractTypeDeclaration) rootTypes.toArray()[rootTypes.size() - 1];
rootTypes.remove(type);
types.add(0, type);
ITypeBinding binding = Types.getTypeBinding(type);
String typeName = binding.getBinaryName();
// Copy the values to avoid a ConcurrentModificationException.
List<String> values = Lists.newArrayList(superTypes.get(typeName));
for (String superTypeName : values) {
superTypes.remove(typeName, superTypeName);
if (!superTypes.containsValue(superTypeName)) {
AbstractTypeDeclaration superType = index.get(superTypeName);
rootTypes.add(superType);
}
}
}
assert types.size() == typesCopy.size();
// For all other references, if the referred to type is declared
// after the reference, add a forward reference.
final Set<ITypeBinding> moreForwardTypes = Sets.newHashSet();
for (Map.Entry<String, String> entry : references.entries()) {
AbstractTypeDeclaration referrer = index.get(entry.getKey());
AbstractTypeDeclaration referred = index.get(entry.getValue());
if (types.indexOf(referred) > types.indexOf(referrer)) {
// Referred to type occurs after the reference; add a forward decl
moreForwardTypes.add(Types.getTypeBinding(referred));
}
}
return moreForwardTypes;
}
private void printInstanceVariables(FieldDeclaration[] fields) {
indent();
String lastAccess = "@protected";
for (FieldDeclaration field : fields) {
if ((field.getModifiers() & Modifier.STATIC) == 0) {
@SuppressWarnings("unchecked")
List<VariableDeclarationFragment> vars = field.fragments(); // safe by definition
assert !vars.isEmpty();
VariableDeclarationFragment var = vars.get(0);
if (var.getName().getIdentifier().startsWith("this$") && superDefinesVariable(var)) {
// Don't print, as it shadows an inner field in a super class.
continue;
}
String access = accessScope(field.getModifiers());
if (!access.equals(lastAccess)) {
print(' ');
println(access);
lastAccess = access;
}
printIndent();
if (Types.isWeakReference(Types.getVariableBinding(var)) && Options.useARC()) {
print("__weak ");
}
ITypeBinding varType = Types.getTypeBinding(vars.get(0));
String objcType = NameTable.javaRefToObjC(varType);
boolean needsAsterisk = !varType.isPrimitive() && !objcType.matches("id|id<.*>|Class");
if (needsAsterisk && objcType.endsWith(" *")) {
// Strip pointer from type, as it will be added when appending fragment.
// This is necessary to create "Foo *one, *two;" declarations.
objcType = objcType.substring(0, objcType.length() - 2);
}
print(objcType);
print(' ');
for (Iterator<?> it = field.fragments().iterator(); it.hasNext(); ) {
VariableDeclarationFragment f = (VariableDeclarationFragment) it.next();
if (needsAsterisk) {
print('*');
}
String name = NameTable.getName(f.getName());
print(NameTable.javaFieldToObjC(name));
if (it.hasNext()) {
print(", ");
}
}
println(";");
}
}
unindent();
}
private void printProperties(FieldDeclaration[] fields) {
int nPrinted = 0;
for (FieldDeclaration field : fields) {
if ((field.getModifiers() & Modifier.STATIC) == 0) {
ITypeBinding type = Types.getTypeBinding(field.getType());
@SuppressWarnings("unchecked")
List<VariableDeclarationFragment> vars = field.fragments(); // safe by definition
for (VariableDeclarationFragment var : vars) {
if (var.getName().getIdentifier().startsWith("this$") && superDefinesVariable(var)) {
// Don't print, as it shadows an inner field in a super class.
continue;
}
print("@property (nonatomic, ");
IVariableBinding varBinding = Types.getVariableBinding(var);
if (type.isPrimitive()) {
print("assign");
} else if (Types.isWeakReference(varBinding) ||
(varBinding.getName().startsWith("this$") &&
Types.hasWeakAnnotation(varBinding.getDeclaringClass()))) {
print(Options.useARC() ? "weak" : "assign");
} else if (type.isEqualTo(Types.getNSString())) {
print("copy");
} else {
print(Options.useARC() ? "strong" : "retain");
}
String typeString = NameTable.javaRefToObjC(type);
if (!typeString.endsWith("*")) {
typeString += " ";
}
println(String.format(") %s%s;", typeString, NameTable.getName(var.getName())));
nPrinted++;
}
}
}
if (nPrinted > 0) {
newline();
}
}
private void printConstantDefines(AbstractTypeDeclaration node) {
ITypeBinding type = Types.getTypeBinding(node);
boolean hadConstant = false;
for (IVariableBinding field : type.getDeclaredFields()) {
if (Types.isPrimitiveConstant(field)) {
printf("#define %s ", NameTable.getPrimitiveConstantName(field));
Object value = field.getConstantValue();
assert value != null;
if (value instanceof Boolean) {
println(((Boolean) value).booleanValue() ? "TRUE" : "FALSE");
} else if (value instanceof Character) {
char c = ((Character) value).charValue();
String convertedChar = UnicodeUtils.escapeCharacter(c);
if (convertedChar != null) {
if (convertedChar.equals("'") || convertedChar.equals("\\")) {
printf("'\\%s'\n", convertedChar);
} else {
printf("'%s'\n", convertedChar);
}
} else {
// The Java char constant is likely not a valid C character; just
// print it as an int.
printf("0x%4x\n", c & 0xffff);
}
} else if (value instanceof Long) {
long l = ((Long) value).longValue();
if (l == Long.MIN_VALUE) {
println("-0x7fffffffffffffffLL - 1");
} else {
println(value.toString());
}
} else if (value instanceof Integer) {
long l = ((Integer) value).intValue();
if (l == Integer.MIN_VALUE) {
println("-0x7fffffff - 1");
} else {
println(value.toString());
}
} else if (value instanceof Float) {
float f = ((Float) value).floatValue();
if (Float.isNaN(f)) {
println("NAN");
} else if (f == Float.POSITIVE_INFINITY) {
println("INFINITY");
} else if (f == Float.NEGATIVE_INFINITY) {
// FP representations are symmetrical.
println("-INFINITY");
} else if (f == Float.MAX_VALUE) {
println("__FLT_MAX__");
} else if (f == Float.MIN_VALUE) {
println("__FLT_MIN__");
} else {
println(value.toString());
}
} else if (value instanceof Double) {
double d = ((Double) value).doubleValue();
if (Double.isNaN(d)) {
println("NAN");
} else if (d == Double.POSITIVE_INFINITY) {
println("INFINITY");
} else if (d == Double.NEGATIVE_INFINITY) {
// FP representations are symmetrical.
println("-INFINITY");
} else if (d == Double.MAX_VALUE) {
println("__DBL_MAX__");
} else if (d == Double.MIN_VALUE) {
println("__DBL_MIN__");
} else {
println(value.toString());
}
} else {
println(value.toString());
}
hadConstant = true;
}
}
if (hadConstant) {
newline();
}
}
private String accessScope(int modifiers) {
if (Options.inlineFieldAccess()) {
// Need direct access to fields possibly from inner classes that are
// promoted to top level classes, so must make all fields public.
return "@public";
}
if ((modifiers & Modifier.PUBLIC) > 0) {
return "@public";
}
if ((modifiers & Modifier.PROTECTED) > 0) {
return "@protected";
}
if ((modifiers & Modifier.PRIVATE) > 0) {
return "@private";
}
return "@package";
}
}

View file

@ -0,0 +1,353 @@
package com.google.devtools.j2cpp.gen;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.gen.SourceFileGenerator;
import com.google.devtools.j2objc.types.IOSMethod;
import com.google.devtools.j2objc.types.IOSParameter;
import com.google.devtools.j2objc.types.Types;
import com.google.devtools.j2objc.util.NameTable;
public abstract class CppSourceFileGenerator extends SourceFileGenerator {
/**
* Create a new generator.
*
* @param sourceFileName the name of the source file being translated
* @param outputDirectory the top-level directory for output file(s)
*/
protected CppSourceFileGenerator(String sourceFileName, String source,
CompilationUnit unit, boolean emitLineDirectives) {
super(sourceFileName, source, unit, emitLineDirectives);
}
/**
* Generate an output source file from the specified type declaration.
*/
public void generate(AbstractTypeDeclaration node) {
if (node instanceof TypeDeclaration) {
generate((TypeDeclaration) node);
} else if (node instanceof EnumDeclaration) {
generate((EnumDeclaration) node);
} else if (node instanceof AnnotationTypeDeclaration) {
generate((AnnotationTypeDeclaration) node);
}
}
protected abstract void generate(TypeDeclaration node);
protected abstract void generate(EnumDeclaration node);
protected abstract void generate(AnnotationTypeDeclaration node);
public void save(CompilationUnit node) {
save(getOutputFileName(node));
}
/**
* Print a list of methods.
*/
protected void printMethods(List<MethodDeclaration> methods) {
for (MethodDeclaration m : methods) {
syncLineNumbers(m.getName()); // avoid doc-comment
IMethodBinding binding = Types.getMethodBinding(m);
IOSMethod iosMethod = Types.getMappedMethod(binding);
if (iosMethod != null) {
print(mappedMethodDeclaration(m, iosMethod));
} else if (m.isConstructor()) {
print(constructorDeclaration(m));
} else if (Modifier.isStatic(m.getModifiers()) &&
NameTable.CLINIT_NAME.equals(m.getName().getIdentifier())) {
printStaticConstructorDeclaration(m);
} else if (!isMainMethod(m) && !isInterfaceConstantAccessor(binding)) {
printMethod(m);
}
}
}
/**
* Returns true if the specified method binding describes an accessor for
* an interface constant.
*/
protected boolean isInterfaceConstantAccessor(IMethodBinding binding) {
return binding.getDeclaringClass().isInterface()
&& !Modifier.isAbstract(binding.getModifiers());
}
/**
* Returns a list of those methods that define accessors to interface
* constants. For most interfaces, the returned list will be empty.
*/
protected List<MethodDeclaration> findInterfaceConstantAccessors(
List<MethodDeclaration> methods) {
List<MethodDeclaration> results = Lists.newArrayList();
for (MethodDeclaration m : methods) {
if (isInterfaceConstantAccessor(Types.getMethodBinding(m))) {
results.add(m);
}
}
return results;
}
protected void printMethod(MethodDeclaration m) {
print(methodDeclaration(m));
}
/**
* Create an C++ method or constructor declaration string for an
* inlined method.
*/
protected String mappedMethodDeclaration(MethodDeclaration method, IOSMethod mappedMethod) {
StringBuffer sb = new StringBuffer();
boolean isStatic = (method.getModifiers() & Modifier.STATIC) > 0;
// Explicitly test hashCode() because of NSObject's hash return value.
String baseDeclaration;
if (mappedMethod.getName().equals("hash")) {
baseDeclaration = "- (NSUInteger)hash";
} else {
baseDeclaration = String.format("%s (%s)%s", isStatic ? "static" : "",
NameTable.javaRefToCpp(method.getReturnType2()), mappedMethod.getName());
}
sb.append(baseDeclaration);
Iterator<IOSParameter> iosParameters = mappedMethod.getParameters().iterator();
if (iosParameters.hasNext()) {
@SuppressWarnings("unchecked")
List<SingleVariableDeclaration> parameters = method.parameters();
IOSParameter first = iosParameters.next();
SingleVariableDeclaration var = parameters.get(first.getIndex());
addTypeAndName(first, var, sb);
if (iosParameters.hasNext()) {
sb.append(mappedMethod.isVarArgs() ? ", " : " ");
IOSParameter next = iosParameters.next();
sb.append(next.getParameterName());
var = parameters.get(next.getIndex());
addTypeAndName(next, var, sb);
}
}
return sb.toString();
}
private void addTypeAndName(IOSParameter iosParameter, SingleVariableDeclaration var,
StringBuffer sb) {
sb.append(":(");
sb.append(iosParameter.getType());
sb.append(')');
sb.append(var.getName().getIdentifier());
}
/**
* Create an C++ method declaration string.
*/
protected String methodDeclaration(MethodDeclaration m) {
assert !m.isConstructor();
StringBuffer sb = new StringBuffer();
boolean isStatic = Modifier.isStatic(m.getModifiers());
IMethodBinding binding = Types.getMethodBinding(m);
String methodName = NameTable.getName(binding);
String baseDeclaration = String.format("\t %s %s %s", isStatic ? "static " : "", NameTable.javaRefToCpp(m.getReturnType2()), methodName);
sb.append(baseDeclaration);
@SuppressWarnings("unchecked")
List<SingleVariableDeclaration> params = m.parameters(); // safe by definition
parametersDeclaration(Types.getOriginalMethodBinding(binding), params, baseDeclaration, sb);
return sb.toString();
}
/**
* Create an C++ constructor declaration string.
*/
protected String constructorDeclaration(MethodDeclaration m) {
assert m.isConstructor();
StringBuffer sb = new StringBuffer();
String baseDeclaration = "- (id)init";
sb.append(baseDeclaration);
@SuppressWarnings("unchecked")
List<SingleVariableDeclaration> params = m.parameters(); // safe by definition
parametersDeclaration(Types.getMethodBinding(m), params, baseDeclaration, sb);
return sb.toString();
}
/**
* Print an C++ constructor declaration string.
*/
protected abstract void printStaticConstructorDeclaration(MethodDeclaration m);
private void parametersDeclaration(IMethodBinding method, List<SingleVariableDeclaration> params,
String baseDeclaration, StringBuffer sb) throws AssertionError {
if (!params.isEmpty()) {
ITypeBinding[] parameterTypes = method.getParameterTypes();
int nParams = params.size();
sb.append(" (");
for (int i = 0; i < nParams; i++) {
SingleVariableDeclaration param = params.get(i);
String fieldName = getParameterName(param);
ITypeBinding typeBinding = parameterTypes[i];
boolean isTypeVariable = typeBinding.isTypeVariable();
String type = isTypeVariable ?
NameTable.getParameterTypeName(NameTable.ID_TYPE, typeBinding) :
NameTable.getParameterTypeName(NameTable.javaTypeToCpp(param.getType(), true), typeBinding);
sb.append(" ").append(type).append(" ").append(fieldName);
if (i<nParams-1) {
sb.append(",");
} else {
sb.append(" )");
}
}
}
// TODO
// if (method.isConstructor() && method.getDeclaringClass().isEnum()) {
// // If enum constant type, append name and ordinal.
// if (params.isEmpty()) {
// sb.append("WithNSString:(NSString *)name withInt:(int)ordinal");
// } else {
// sb.append('\n');
// String keyword = "withNSString";
// sb.append(pad(baseDeclaration.length() - keyword.length()));
// sb.append(keyword);
// sb.append(":(NSString *)name\n");
// keyword = "withInt";
// sb.append(pad(baseDeclaration.length() - keyword.length()));
// sb.append(keyword);
// sb.append(":(int)ordinal");
// }
// }
}
protected String getParameterName(SingleVariableDeclaration param) {
String name = NameTable.getName(param.getName());
if (NameTable.isReservedName(name)) {
name += "Arg";
}
return name;
}
private String parameterKeyword(Type type, ITypeBinding typeBinding) {
String typeName = NameTable.javaTypeToCpp(type, true);
return parameterKeyword(typeName, typeBinding);
}
/**
* Returns a parameter name, which consists of a prefix ("with") and
* a type name that doesn't conflict with core names. For example,
* "Foo" returns "withFoo", "Long" returns "withLong", and "long"
* returns "withLongInt", so as not to conflict with the previous
* example.
*
* For array types, the name returned is the type of the array's
* element followed by "Array".
*/
public static String parameterKeyword(String typeName, ITypeBinding typeBinding) {
return "with" + NameTable.capitalize(NameTable.getParameterTypeName(typeName, typeBinding));
}
/**
* Returns true if the specified method declaration is for a Java main
*/
protected boolean isMainMethod(MethodDeclaration m) {
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
if (m.getName().getIdentifier().equals("main")) {
List<?> args = m.parameters();
if (args.size() == 1) {
SingleVariableDeclaration var = (SingleVariableDeclaration) args.get(0);
// Use original binding, since we can't tell if it's a String
// array after translation, since IOSObjectArray just holds objects.
ITypeBinding type = var.resolveBinding().getType();
ITypeBinding stringType = m.getAST().resolveWellKnownType("java.lang.String");
return type.isArray() && type.getComponentType().isEqualTo(stringType);
}
}
}
return false;
}
/**
* Returns a function declaration string from a specified class and method.
*/
protected String makeFunctionDeclaration(AbstractTypeDeclaration cls,
MethodDeclaration method) {
StringBuffer sb = new StringBuffer();
Type returnType = method.getReturnType2();
ITypeBinding binding = Types.getTypeBinding(returnType);
if (binding.isEnum()) {
sb.append(NameTable.javaTypeToCpp(returnType, true));
} else {
sb.append(NameTable.javaRefToCpp(returnType));
}
sb.append(' ');
sb.append(NameTable.makeFunctionName(cls, method));
sb.append('(');
for (Iterator<?> iterator = method.parameters().iterator(); iterator.hasNext(); ) {
Object o = iterator.next();
if (o instanceof SingleVariableDeclaration) {
SingleVariableDeclaration param = (SingleVariableDeclaration) o;
String fieldType = NameTable.javaRefToCpp(param.getType());
String fieldName = param.getName().getIdentifier();
sb.append(String.format("%s %s", fieldType, fieldName));
if (iterator.hasNext()) {
sb.append(", ");
}
}
}
sb.append(')');
return sb.toString();
}
/**
* Returns true if a superclass also defines this variable.
*/
protected boolean superDefinesVariable(VariableDeclarationFragment var) {
IVariableBinding varBinding = Types.getVariableBinding(var);
ITypeBinding declaringClassBinding = varBinding.getDeclaringClass();
TypeDeclaration declaringClass =
Types.getTypeDeclaration(declaringClassBinding, getUnit().types());
if (declaringClass == null) {
return false;
}
String name = var.getName().getIdentifier();
ITypeBinding type = varBinding.getType();
return superDefinesVariable(declaringClass, name, type);
}
private boolean superDefinesVariable(TypeDeclaration declaringClass, String name,
ITypeBinding type) {
ITypeBinding superClazzBinding = Types.getTypeBinding(declaringClass.getSuperclassType());
TypeDeclaration superClazz = Types.getTypeDeclaration(superClazzBinding, getUnit().types());
if (superClazz == null) {
return false;
}
for (FieldDeclaration field : superClazz.getFields()) {
@SuppressWarnings("unchecked")
List<VariableDeclarationFragment> vars = field.fragments(); // safe by definition
for (VariableDeclarationFragment var : vars) {
if (var.getName().getIdentifier().equals(name)) {
ITypeBinding varType = Types.getTypeBinding(var);
if (varType.isEqualTo(type)) {
return true;
}
}
}
}
return superDefinesVariable(superClazz, name, type);
}
}