/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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.common.collect.Lists;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.AnnotationTypeDeclaration;
import com.google.devtools.j2objc.ast.BodyDeclaration;
import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.ast.EnumDeclaration;
import com.google.devtools.j2objc.ast.FieldDeclaration;
import com.google.devtools.j2objc.ast.TreeNode;
import com.google.devtools.j2objc.ast.TypeDeclaration;
import com.google.devtools.j2objc.ast.TypeDeclarationStatement;
import com.google.devtools.j2objc.ast.UnitTreeVisitor;
import com.google.devtools.j2objc.util.CaptureInfo;
import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.ErrorUtil;
import com.google.j2objc.annotations.WeakOuter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
/**
* Adds support for inner and anonymous classes, and extracts them to be
* top-level classes (also like class files). This is similar to how Java
* compilers convert inner classes into class files, which are all top-level.
*
* @author Tom Ball
*/
public class InnerClassExtractor extends UnitTreeVisitor {
private final CaptureInfo captureInfo;
private final List<AbstractTypeDeclaration> unitTypes;
// Helps keep types in the order they are visited.
private ArrayList<Integer> typeOrderStack = Lists.newArrayList();
public InnerClassExtractor(CompilationUnit unit) {
super(unit);
this.captureInfo = unit.getEnv().captureInfo();
unitTypes = unit.getTypes();
}
@Override
public boolean visit(TypeDeclaration node) {
return handleType();
}
@Override
public void endVisit(TypeDeclaration node) {
endHandleType(node);
}
@Override
public boolean visit(EnumDeclaration node) {
return handleType();
}
@Override
public void endVisit(EnumDeclaration node) {
endHandleType(node);
}
@Override
public boolean visit(AnnotationTypeDeclaration node) {
return handleType();
}
@Override
public void endVisit(AnnotationTypeDeclaration node) {
endHandleType(node);
}
private boolean handleType() {
typeOrderStack.add(unitTypes.size());
return true;
}
private void endHandleType(AbstractTypeDeclaration node) {
int insertIdx = typeOrderStack.remove(typeOrderStack.size() - 1);
TreeNode parentNode = node.getParent();
if (!(parentNode instanceof CompilationUnit)) {
// Remove this type declaration from its current location.
node.remove();
if (parentNode instanceof TypeDeclarationStatement) {
parentNode.remove();
}
addCaptureFields(node);
// Make this node non-private, if necessary, and add it to the unit's type
// list.
node.removeModifiers(Modifier.PRIVATE);
unitTypes.add(insertIdx, node);
// Check for erroneous WeakOuter annotation on static inner class.
TypeElement type = node.getTypeElement();
if (ElementUtil.isStatic(type) && ElementUtil.hasAnnotation(type, WeakOuter.class)) {
ErrorUtil.warning("static class " + type.getQualifiedName() + " has WeakOuter annotation");
}
}
}
private void addCaptureFields(AbstractTypeDeclaration node) {
List<BodyDeclaration> members = node.getBodyDeclarations().subList(0, 0);
for (VariableElement field : captureInfo.getCaptureFields(node.getTypeElement())) {
members.add(new FieldDeclaration(field, null));
}
}
}