/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.devtools.j2objc.gen;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.EnumConstantDeclaration;
import com.google.devtools.j2objc.ast.EnumDeclaration;
import com.google.devtools.j2objc.ast.Expression;
import com.google.devtools.j2objc.ast.FieldDeclaration;
import com.google.devtools.j2objc.ast.FunctionDeclaration;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.NativeDeclaration;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.Statement;
import com.google.devtools.j2objc.ast.VariableDeclarationFragment;
import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.NameTable;
import com.google.devtools.j2objc.util.TypeUtil;
import com.google.j2objc.annotations.Property;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
/**
* Generates implementation code for an AbstractTypeDeclaration node.
*
* @author Tom Ball, Keith Stanger
*/
public class TypeImplementationGenerator extends TypeGenerator {
private static final ImmutableSet<String> NSNUMBER_DESIGNATED_INITIALIZERS = ImmutableSet.of(
"initWithBool:",
"initWithChar:",
"initWithDouble:",
"initWithFloat:",
"initWithInt:",
"initWithInteger:",
"initWithLong:",
"initWithLongLong:",
"initWithShort:",
"initWithUnsignedChar:",
"initWithUnsignedInt:",
"initWithUnsignedInteger:",
"initWithUnsignedLong:",
"initWithUnsignedLongLong:",
"initWithUnsignedShort:");
private TypeImplementationGenerator(SourceBuilder builder, AbstractTypeDeclaration node) {
super(builder, node);
}
public static void generate(SourceBuilder builder, AbstractTypeDeclaration node) {
new TypeImplementationGenerator(builder, node).generate();
}
protected void generate() {
syncFilename(compilationUnit.getSourceFilePath());
printInitFlagDefinition();
printStaticVars();
printEnumValuesArray();
if (!typeElement.getKind().isInterface() || needsCompanionClass()) {
newline();
syncLineNumbers(typeNode.getName()); // avoid doc-comment
printf("@implementation %s\n", typeName);
printProperties();
printStaticAccessors();
printInnerDeclarations();
printInitializeMethod();
println("\n@end");
}
printOuterDeclarations();
printTypeLiteralImplementation();
}
private void printInitFlagDefinition() {
if (hasInitializeMethod()) {
printf("\nJ2OBJC_INITIALIZED_DEFN(%s)\n", typeName);
}
}
private static final Predicate<VariableDeclarationFragment> PROPERTIES =
new Predicate<VariableDeclarationFragment>() {
@Override
public boolean apply(VariableDeclarationFragment fragment) {
VariableElement varElement = fragment.getVariableElement();
return ElementUtil.hasAnnotation(varElement, Property.class)
&& !ElementUtil.isStatic(varElement);
}
};
private void printProperties() {
Iterable<VariableDeclarationFragment> fields =
Iterables.filter(getInstanceFields(), PROPERTIES);
if (Iterables.isEmpty(fields)) {
return;
}
newline();
for (VariableDeclarationFragment fragment : fields) {
VariableElement varElement = fragment.getVariableElement();
String propertyName = nameTable.getVariableBaseName(varElement);
String varName = nameTable.getVariableShortName(varElement);
println("@synthesize " + propertyName + " = " + varName + ";");
}
}
private static final Predicate<VariableDeclarationFragment> NEEDS_DEFINITION =
new Predicate<VariableDeclarationFragment>() {
@Override
public boolean apply(VariableDeclarationFragment fragment) {
return !ElementUtil.isPrimitiveConstant(fragment.getVariableElement())
// Private static vars are defined in the private declaration.
&& !((FieldDeclaration) fragment.getParent()).hasPrivateDeclaration();
}
};
private void printStaticVars() {
Iterable<VariableDeclarationFragment> fields =
Iterables.filter(getStaticFields(), NEEDS_DEFINITION);
if (Iterables.isEmpty(fields)) {
return;
}
newline();
for (VariableDeclarationFragment fragment : fields) {
VariableElement varElement = fragment.getVariableElement();
Expression initializer = fragment.getInitializer();
String name = nameTable.getVariableQualifiedName(varElement);
String objcType = getDeclarationType(varElement);
objcType += objcType.endsWith("*") ? "" : " ";
if (initializer != null) {
String cast = !varElement.asType().getKind().isPrimitive()
&& ElementUtil.isVolatile(varElement) ? "(void *)" : "";
printf("%s%s = %s%s;\n", objcType, name, cast, generateExpression(initializer));
} else {
printf("%s%s;\n", objcType, name);
}
}
}
/**
* Prints the list of static variable and/or enum constant accessor methods.
*/
protected void printStaticAccessors() {
if (!options.staticAccessorMethods()) {
return;
}
for (VariableDeclarationFragment fragment : getStaticFields()) {
if (!((FieldDeclaration) fragment.getParent()).hasPrivateDeclaration()) {
VariableElement varElement = fragment.getVariableElement();
TypeMirror type = varElement.asType();
boolean isVolatile = ElementUtil.isVolatile(varElement);
boolean isPrimitive = type.getKind().isPrimitive();
String accessorName = nameTable.getStaticAccessorName(varElement);
String varName = nameTable.getVariableQualifiedName(varElement);
String objcType = nameTable.getObjCType(type);
String typeSuffix = isPrimitive ? NameTable.capitalize(TypeUtil.getName(type)) : "Id";
if (isVolatile) {
printf("\n+ (%s)%s {\n return JreLoadVolatile%s(&%s);\n}\n",
objcType, accessorName, typeSuffix, varName);
} else {
printf("\n+ (%s)%s {\n return %s;\n}\n", objcType, accessorName, varName);
}
if (!ElementUtil.isFinal(varElement)) {
String setterFunc = isVolatile
? (isPrimitive ? "JreAssignVolatile" + typeSuffix : "JreVolatileStrongAssign")
: (isPrimitive | options.useARC() ? null : "JreStrongAssign");
if (setterFunc == null) {
printf("\n+ (void)set%s:(%s)value {\n %s = value;\n}\n",
NameTable.capitalize(accessorName), objcType, varName);
} else {
printf("\n+ (void)set%s:(%s)value {\n %s(&%s, value);\n}\n",
NameTable.capitalize(accessorName), objcType, setterFunc, varName);
}
}
}
}
if (typeNode instanceof EnumDeclaration) {
for (EnumConstantDeclaration constant : ((EnumDeclaration) typeNode).getEnumConstants()) {
VariableElement varElement = constant.getVariableElement();
printf("\n+ (%s *)%s {\n return %s;\n}\n",
typeName, nameTable.getStaticAccessorName(varElement),
nameTable.getVariableQualifiedName(varElement));
}
}
}
private void printEnumValuesArray() {
if (typeNode instanceof EnumDeclaration) {
List<EnumConstantDeclaration> constants = ((EnumDeclaration) typeNode).getEnumConstants();
newline();
printf("%s *%s_values_[%s];\n", typeName, typeName, constants.size());
}
}
private void printTypeLiteralImplementation() {
if (needsTypeLiteral()) {
newline();
printf("J2OBJC_%s_TYPE_LITERAL_SOURCE(%s)\n",
isInterfaceType() ? "INTERFACE" : "CLASS", typeName);
}
}
private boolean isDesignatedInitializer(ExecutableElement method) {
if (!ElementUtil.isConstructor(method)) {
return false;
}
String selector = nameTable.getMethodSelector(method);
return selector.equals("init")
|| (typeUtil.isObjcSubtype(ElementUtil.getDeclaringClass(method), TypeUtil.NS_NUMBER)
&& NSNUMBER_DESIGNATED_INITIALIZERS.contains(selector));
}
@Override
protected void printMethodDeclaration(MethodDeclaration m) {
if (Modifier.isAbstract(m.getModifiers())) {
return;
}
newline();
boolean isDesignatedInitializer = isDesignatedInitializer(m.getExecutableElement());
if (isDesignatedInitializer) {
println("J2OBJC_IGNORE_DESIGNATED_BEGIN");
}
syncLineNumbers(m); // avoid doc-comment
String methodBody = generateStatement(m.getBody());
print(getMethodSignature(m) + " " + reindent(methodBody) + "\n");
if (isDesignatedInitializer) {
println("J2OBJC_IGNORE_DESIGNATED_END");
}
}
@Override
protected void printFunctionDeclaration(FunctionDeclaration function) {
newline();
syncLineNumbers(function); // avoid doc-comment
if (Modifier.isNative(function.getModifiers())) {
printJniFunctionAndWrapper(function);
} else {
String functionBody = generateStatement(function.getBody());
println(getFunctionSignature(function) + " " + reindent(functionBody));
}
}
private String getJniFunctionSignature(FunctionDeclaration function) {
StringBuilder sb = new StringBuilder();
sb.append(nameTable.getJniType(function.getReturnType().getTypeMirror()));
sb.append(' ');
sb.append(function.getJniSignature()).append('(');
sb.append("JNIEnv *_env_");
if (Modifier.isStatic(function.getModifiers())) {
sb.append(", jclass _cls_");
}
if (!function.getParameters().isEmpty()) {
sb.append(", ");
}
for (Iterator<SingleVariableDeclaration> iter = function.getParameters().iterator();
iter.hasNext(); ) {
VariableElement var = iter.next().getVariableElement();
String paramType = nameTable.getJniType(var.asType());
sb.append(paramType + ' ' + nameTable.getVariableBaseName(var));
if (iter.hasNext()) {
sb.append(", ");
}
}
sb.append(')');
return sb.toString();
}
private void printJniFunctionAndWrapper(FunctionDeclaration function) {
// Declare the matching JNI function.
print("JNIEXPORT ");
print(getJniFunctionSignature(function));
println(";\n");
// Generate a wrapper function that calls the matching JNI function.
print(getFunctionSignature(function));
println(" {");
print(" ");
TypeMirror returnType = function.getReturnType().getTypeMirror();
if (!TypeUtil.isVoid(returnType)) {
if (returnType.getKind().isPrimitive()) {
print("return ");
} else {
printf("return (%s) ", nameTable.getObjCType(returnType));
}
}
print(function.getJniSignature());
print("(&J2ObjC_JNIEnv");
if (Modifier.isStatic(function.getModifiers())) {
printf(", %s_class_()", typeName);
}
for (SingleVariableDeclaration param : function.getParameters()) {
printf(", %s", nameTable.getVariableBaseName(param.getVariableElement()));
}
println(");");
println("}");
}
@Override
protected void printNativeDeclaration(NativeDeclaration declaration) {
newline();
String code = declaration.getImplementationCode();
if (code != null) {
println(reindent(code));
}
}
private void printInitializeMethod() {
List<Statement> initStatements = typeNode.getClassInitStatements();
if (initStatements.isEmpty()) {
return;
}
StringBuffer sb = new StringBuffer();
sb.append("{\nif (self == [" + typeName + " class]) {\n");
for (Statement statement : initStatements) {
sb.append(generateStatement(statement));
}
sb.append("J2OBJC_SET_INITIALIZED(" + typeName + ")\n");
sb.append("}\n}");
print("\n+ (void)initialize " + reindent(sb.toString()) + "\n");
}
protected String generateStatement(Statement stmt) {
return StatementGenerator.generate(stmt, getBuilder().getCurrentLine());
}
}