/* * 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.translate; import com.google.devtools.j2objc.ast.ArrayInitializer; import com.google.devtools.j2objc.ast.Assignment; import com.google.devtools.j2objc.ast.Block; import com.google.devtools.j2objc.ast.CompilationUnit; import com.google.devtools.j2objc.ast.EmptyStatement; import com.google.devtools.j2objc.ast.Expression; import com.google.devtools.j2objc.ast.ExpressionStatement; import com.google.devtools.j2objc.ast.FunctionInvocation; import com.google.devtools.j2objc.ast.MethodInvocation; import com.google.devtools.j2objc.ast.NativeExpression; import com.google.devtools.j2objc.ast.NumberLiteral; import com.google.devtools.j2objc.ast.SimpleName; import com.google.devtools.j2objc.ast.Statement; import com.google.devtools.j2objc.ast.SwitchCase; import com.google.devtools.j2objc.ast.SwitchStatement; import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.UnitTreeVisitor; import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.ast.VariableDeclarationStatement; import com.google.devtools.j2objc.types.ExecutablePair; import com.google.devtools.j2objc.types.FunctionElement; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.TypeUtil; import java.util.List; import javax.lang.model.element.ElementKind; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; /** * Rewrites switch statemennts to be more compatible with Objective-C code. * * @author Keith Stanger */ public class SwitchRewriter extends UnitTreeVisitor { public SwitchRewriter(CompilationUnit unit) { super(unit); } @Override public void endVisit(SwitchStatement node) { fixVariableDeclarations(node); fixStringValue(node); fixEnumValue(node); List<Statement> stmts = node.getStatements(); Statement lastStmt = stmts.get(stmts.size() - 1); if (!stmts.isEmpty() && lastStmt instanceof SwitchCase) { // Last switch case doesn't have an associated statement, so add an empty one // with the same line number as the switch case to keep line numbers synced. EmptyStatement emptyStmt = new EmptyStatement(); emptyStmt.setLineNumber(lastStmt.getLineNumber()); stmts.add(emptyStmt); } } @Override public void endVisit(SwitchCase node) { Expression expr = node.getExpression(); VariableElement var = expr != null ? TreeUtil.getVariableElement(expr) : null; if (var == null) { return; } TypeMirror type = var.asType(); if (TypeUtil.isEnum(type)) { String enumValue = NameTable.getNativeEnumName(nameTable.getFullName(TypeUtil.asTypeElement(type))) + "_" + nameTable.getVariableBaseName(var); node.setExpression(new NativeExpression(enumValue, typeUtil.getInt())); } else if (type.getKind().isPrimitive() && var.getKind() == ElementKind.LOCAL_VARIABLE) { Object value = var.getConstantValue(); if (value != null) { node.setExpression(TreeUtil.newLiteral(value, typeUtil)); } } } /** * Moves all variable declarations above the first case statement. */ private void fixVariableDeclarations(SwitchStatement node) { List<Statement> statements = node.getStatements(); Block block = new Block(); List<Statement> blockStmts = block.getStatements(); for (int i = 0; i < statements.size(); i++) { Statement stmt = statements.get(i); if (stmt instanceof VariableDeclarationStatement) { VariableDeclarationStatement declStmt = (VariableDeclarationStatement) stmt; statements.remove(i--); List<VariableDeclarationFragment> fragments = declStmt.getFragments(); for (VariableDeclarationFragment decl : fragments) { Expression initializer = decl.getInitializer(); if (initializer != null) { Assignment assignment = new Assignment( new SimpleName(decl.getVariableElement()), TreeUtil.remove(initializer)); statements.add(++i, new ExpressionStatement(assignment)); } } blockStmts.add(declStmt); } } if (blockStmts.size() > 0) { // There is at least one variable declaration, so copy this switch // statement into the new block and replace it in the parent list. node.replaceWith(block); blockStmts.add(node); } } private void fixStringValue(SwitchStatement node) { Expression expr = node.getExpression(); TypeMirror type = expr.getTypeMirror(); if (!typeUtil.isString(type)) { return; } ArrayType arrayType = typeUtil.getArrayType(type); ArrayInitializer arrayInit = new ArrayInitializer(arrayType); int idx = 0; for (Statement stmt : node.getStatements()) { if (stmt instanceof SwitchCase) { SwitchCase caseStmt = (SwitchCase) stmt; if (!caseStmt.isDefault()) { arrayInit.addExpression(TreeUtil.remove(caseStmt.getExpression())); caseStmt.setExpression(NumberLiteral.newIntLiteral(idx++, typeUtil)); } } } TypeMirror intType = typeUtil.getInt(); FunctionElement indexOfFunc = new FunctionElement("JreIndexOfStr", intType, null) .addParameters(type, arrayType, intType); FunctionInvocation invocation = new FunctionInvocation(indexOfFunc, intType); invocation.addArgument(TreeUtil.remove(expr)) .addArgument(arrayInit) .addArgument(NumberLiteral.newIntLiteral(idx, typeUtil)); node.setExpression(invocation); } private void fixEnumValue(SwitchStatement node) { Expression expr = node.getExpression(); TypeMirror type = expr.getTypeMirror(); if (!TypeUtil.isEnum(type)) { return; } DeclaredType enumType = typeUtil.getSuperclass(type); ExecutablePair ordinalMethod = typeUtil.findMethod(enumType, "ordinal"); MethodInvocation invocation = new MethodInvocation(ordinalMethod, TreeUtil.remove(expr)); node.setExpression(invocation); } }