/*
* 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.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.AnnotationTypeMemberDeclaration;
import com.google.devtools.j2objc.ast.BodyDeclaration;
import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.ast.EnumConstantDeclaration;
import com.google.devtools.j2objc.ast.FieldDeclaration;
import com.google.devtools.j2objc.ast.FunctionDeclaration;
import com.google.devtools.j2objc.ast.Initializer;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.NativeDeclaration;
import com.google.devtools.j2objc.ast.Type;
import com.google.devtools.j2objc.ast.UnitTreeVisitor;
import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.TypeUtil;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
/**
* Determines which declarations should be moved out of the public header file.
*
* @author Keith Stanger
*/
public class PrivateDeclarationResolver extends UnitTreeVisitor {
private Map<TypeElement, AbstractTypeDeclaration> typeMap = new HashMap<>();
// Collects types that must be public because they are exposed by another
// public declaration. These types and all of their supertypes must be public.
private Set<TypeElement> publicTypes = new HashSet<>();
private List<AbstractTypeDeclaration> publicNodesToVisit = new ArrayList<>();
public PrivateDeclarationResolver(CompilationUnit unit) {
super(unit);
}
@Override
public boolean visit(CompilationUnit node) {
// Map the types by their elements.
for (AbstractTypeDeclaration typeNode : node.getTypes()) {
typeMap.put(typeNode.getTypeElement(), typeNode);
}
// Identify types that are public by their declaration.
for (AbstractTypeDeclaration typeNode : node.getTypes()) {
TypeElement typeElement = typeNode.getTypeElement();
if (!ElementUtil.isPrivateInnerType(typeElement)) {
addPublicType(typeElement);
}
}
// Visit public nodes, possibly identifying additional nodes that must be
// public because they are exposed by a field or method from another type.
while (!publicNodesToVisit.isEmpty()) {
AbstractTypeDeclaration publicNode = publicNodesToVisit.remove(publicNodesToVisit.size() - 1);
publicNode.setHasPrivateDeclaration(false);
for (BodyDeclaration decl : publicNode.getBodyDeclarations()) {
decl.accept(this);
}
}
// After all public nodes are identified, mark remaining nodes and their
// declarations as private.
for (AbstractTypeDeclaration typeNode : node.getTypes()) {
if (!publicTypes.contains(typeNode.getTypeElement())) {
typeNode.setHasPrivateDeclaration(true);
for (BodyDeclaration decl : typeNode.getBodyDeclarations()) {
decl.setHasPrivateDeclaration(true);
}
}
}
return false;
}
private void addPublicType(TypeElement typeElement) {
if (typeElement == null) {
return;
}
AbstractTypeDeclaration typeNode = typeMap.get(typeElement);
if (typeNode == null) {
return;
}
if (publicTypes.add(typeElement)) {
publicNodesToVisit.add(typeNode);
}
// Make sure supertypes of public types remain public, even if declared
// private.
addPublicType(typeElement.getSuperclass());
for (TypeMirror interfaceType : typeElement.getInterfaces()) {
addPublicType(interfaceType);
}
}
private void addPublicType(TypeMirror type) {
if (type != null) {
for (TypeMirror bound : typeUtil.getUpperBounds(type)) {
addPublicType(TypeUtil.asTypeElement(bound));
}
}
}
private void addPublicType(Type typeNode) {
if (typeNode != null) {
addPublicType(typeNode.getTypeMirror());
}
}
@Override
public boolean visit(FieldDeclaration node) {
boolean isPrivate = Modifier.isPrivate(node.getModifiers());
node.setHasPrivateDeclaration(isPrivate);
if (!isPrivate) {
addPublicType(node.getTypeMirror());
}
return false;
}
@Override
public boolean visit(FunctionDeclaration node) {
boolean isPrivate = Modifier.isPrivate(node.getModifiers());
node.setHasPrivateDeclaration(isPrivate);
if (!isPrivate) {
addPublicType(node.getReturnType());
}
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
boolean isPrivate = Modifier.isPrivate(node.getModifiers());
node.setHasPrivateDeclaration(isPrivate);
if (!isPrivate) {
addPublicType(node.getReturnTypeMirror());
}
return false;
}
@Override
public boolean visit(NativeDeclaration node) {
boolean isPrivate = Modifier.isPrivate(node.getModifiers());
node.setHasPrivateDeclaration(isPrivate);
return false;
}
@Override
public boolean visit(AnnotationTypeMemberDeclaration node) {
return false;
}
@Override
public boolean visit(EnumConstantDeclaration node) {
return false;
}
@Override
public boolean visit(Initializer node) {
return false;
}
}