/*******************************************************************************
* Copyright (c) 2007 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributor:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.internal.core.scanner.java;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.jboss.tools.common.model.util.EclipseJavaUtil;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.internal.core.scanner.Util;
/**
* Processes AST tree to find annotated type, fields and methods.
*
* @author Viacheslav Kabanovich
*/
public class ASTVisitorImpl extends ASTVisitor implements SeamAnnotations {
public static class TypeData {
TypeData parent = null;
List<TypeData> children = new ArrayList<TypeData>();
public IType type;
int innerLock = 0;
public AnnotatedASTNode<AbstractTypeDeclaration> annotatedType = null;
public Set<AnnotatedASTNode<FieldDeclaration>> annotatedFields = new HashSet<AnnotatedASTNode<FieldDeclaration>>();
public Set<AnnotatedASTNode<MethodDeclaration>> annotatedMethods = new HashSet<AnnotatedASTNode<MethodDeclaration>>();
AnnotatedASTNode<?> currentAnnotatedNode = null;
AnnotatedASTNode<FieldDeclaration> currentAnnotatedField = null;
AnnotatedASTNode<MethodDeclaration> currentAnnotatedMethod = null;
public boolean hasSeamComponentItself() {
if(!annotatedFields.isEmpty() || !annotatedMethods.isEmpty()) return true;
if(annotatedType != null && annotatedType.getAnnotations() != null) return true;
return false;
}
public boolean hasSeamComponent() {
if(hasSeamComponentItself()) return true;
for (TypeData c: children) {
if(c.hasSeamComponent()) return true;
}
return false;
}
}
public TypeData root = null;
TypeData current = null;
public ASTVisitorImpl() {}
public void setType(IType type) {
root = new TypeData();
root.type = type;
}
public boolean hasSeamComponent() {
return root.hasSeamComponent();
}
public boolean visit(SingleMemberAnnotation node) {
if(current.innerLock > 0) return false;
String type = resolveType(node);
if(Util.isSeamAnnotationType(type) && current.currentAnnotatedNode != null) {
current.currentAnnotatedNode.addAnnotation(new ResolvedAnnotation(type, node));
}
return false;
}
public boolean visit(NormalAnnotation node) {
if(current.innerLock > 0) return false;
String type = resolveType(node);
if(Util.isSeamAnnotationType(type) && current.currentAnnotatedNode != null) {
current.currentAnnotatedNode.addAnnotation(new ResolvedAnnotation(type, node));
}
return false;
}
public boolean visit(MarkerAnnotation node) {
if(current.innerLock > 0) return false;
String type = resolveType(node);
if(Util.isSeamAnnotationType(type) && current.currentAnnotatedNode != null) {
current.currentAnnotatedNode.addAnnotation(new ResolvedAnnotation(type, node));
}
return true;
}
boolean checkAnnotationType(Annotation node, String annotationType) {
String n = resolveType(node);
return n != null && n.equals(annotationType);
}
String resolveType(Annotation node) {
return resolveType(current.type, node);
}
static String resolveType(IType type, Annotation node) {
Name nm = node.getTypeName();
if(nm instanceof SimpleName) {
SimpleName sn = (SimpleName)nm;
String n = sn.getIdentifier();
if(type != null) {
return JavaScanner.getResolvedType(type, n);
}
} else if(nm instanceof QualifiedName) {
QualifiedName qn = (QualifiedName)nm;
return qn.getFullyQualifiedName();
}
return null;
}
public boolean visit(Block node) {
return false;
}
public boolean visit(TypeDeclaration node) {
return _visit(node);
}
public boolean visit(EnumDeclaration node) {
return _visit(node);
}
public boolean visit(AnnotationTypeDeclaration node) {
return _visit(node);
}
private boolean _visit(AbstractTypeDeclaration node) {
if(current == null) {
String n = node.getName().getFullyQualifiedName();
if(n != null && n.indexOf('.') < 0) n = EclipseJavaUtil.resolveType(root.type, n);
String nr = root.type.getFullyQualifiedName();
if(n == null || !n.equals(nr)) return false;
current = root;
}
if(current.annotatedType == null) {
current.annotatedType = new AnnotatedASTNode<AbstractTypeDeclaration>(node);
current.currentAnnotatedNode = current.annotatedType;
} else {
String n = node.getName().getFullyQualifiedName();
if(n != null && n.indexOf('.') < 0) n = EclipseJavaUtil.resolveType(current.type, n);
IType[] ts = null;
try {
ts = current.type.getTypes();
} catch (JavaModelException e) {
SeamCorePlugin.getDefault().logError(e);
}
IType t = null;
if(ts != null) for (int i = 0; t == null && i < ts.length; i++) {
try {
if(!Flags.isStatic(ts[i].getFlags())) continue;
} catch (JavaModelException e) {
continue;
}
String ni = ts[i].getFullyQualifiedName();
if(ni != null) ni = ni.replace('$', '.');
if(n == null || !n.equals(ni)) continue;
t = ts[i];
}
if(t == null) {
current.innerLock++;
return false;
} else {
TypeData d = new TypeData();
d.type = t;
d.parent = current;
current.children.add(d);
current = d;
current.annotatedType = new AnnotatedASTNode<AbstractTypeDeclaration>(node);
current.currentAnnotatedNode = current.annotatedType;
}
}
return true;
}
public void endVisit(TypeDeclaration node) {
_endVisit(node);
}
public void endVisit(AnnotationTypeDeclaration node) {
_endVisit(node);
}
public void endVisit(EnumDeclaration node) {
_endVisit(node);
}
public void _endVisit(AbstractTypeDeclaration node) {
if(current == null) return;
if(current.currentAnnotatedNode != null && current.currentAnnotatedNode.node == node) {
current.currentAnnotatedNode = null;
current = current.parent;
} else {
current.innerLock--;
}
}
public boolean visit(FieldDeclaration node) {
if(current == null || current.innerLock > 0) return false;
current.currentAnnotatedField = new AnnotatedASTNode<FieldDeclaration>(node);
current.currentAnnotatedNode = current.currentAnnotatedField;
return true;
}
public void endVisit(FieldDeclaration node) {
if(current == null || current.innerLock > 0) return;
if(current.currentAnnotatedField != null && current.currentAnnotatedField.getAnnotations() != null) {
current.annotatedFields.add(current.currentAnnotatedField);
}
current.currentAnnotatedField = null;
current.currentAnnotatedNode = current.annotatedType;
}
public boolean visit(MethodDeclaration node) {
if(current == null || current.innerLock > 0) return false;
current.currentAnnotatedMethod = new AnnotatedASTNode<MethodDeclaration>(node);
current.currentAnnotatedNode = current.currentAnnotatedMethod;
return true;
}
public void endVisit(MethodDeclaration node) {
if(current == null || current.innerLock > 0) return;
if(current.currentAnnotatedMethod != null && current.currentAnnotatedMethod.getAnnotations() != null) {
current.annotatedMethods.add(current.currentAnnotatedMethod);
}
current.currentAnnotatedMethod = null;
current.currentAnnotatedNode = current.annotatedType;
}
}