/******************************************************************************* * Copyright (c) 2011 Gerd Wuetherich (gerd@gerd-wuetherich.de). * All rights reserved. This program and the accompanying materials * are 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 * * Contributors: * Gerd Wuetherich (gerd@gerd-wuetherich.de) - initial API and implementation ******************************************************************************/ package org.bundlemaker.core.ui.editor.sourceviewer.referencedetail; import java.util.LinkedList; import java.util.List; import java.util.Stack; import org.bundlemaker.core.common.collections.GenericCache; import org.bundlemaker.core.jtype.IReference; import org.bundlemaker.core.resource.IModuleResource; import org.bundlemaker.core.resource.IModuleAwareMovableUnit; import org.bundlemaker.core.spi.parser.IReferenceDetailParser; import org.bundlemaker.core.spi.parser.IReferenceDetailParser.IPosition; import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IPackageBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.InstanceofExpression; import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.Message; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; 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.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeLiteral; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; /** * <p> * </p> * * @author Gerd Wüerich (gerd@gerd-wuetherich.de) */ public class JdtAstVisitor extends ASTVisitor { /** - */ private Stack<ITypeBinding> _typeBindings; /** */ private Message[] _messages = new Message[0]; /** */ private IProblem[] _problems = new IProblem[0]; GenericCache<String, List<IPosition>> _references = new GenericCache<String, List<IPosition>>() { /** - */ private static final long serialVersionUID = 1L; @Override protected List<IPosition> create(String key) { return new LinkedList<IPosition>(); } }; public JdtAstVisitor() { // _typeBindings = new Stack<ITypeBinding>(); } /** * <p> * </p> * * @return */ protected final GenericCache<String, List<IPosition>> getReferences() { return _references; } /** * <p> * </p> * * @return */ public Message[] getMessages() { return _messages; } /** * <p> * </p> * * @return */ public List<org.bundlemaker.core.parser.IProblem> getProblems() { List<org.bundlemaker.core.parser.IProblem> result = new LinkedList<org.bundlemaker.core.parser.IProblem>(); // // for (IProblem iProblem : _problems) { // // if (iProblem.isError()) { // // result.add(new JdtProblemAdapter(_javaSourceResource, iProblem)); // } // } return result; } /** * <p> * </p> * * @return */ public boolean hasErrors() { // for (IProblem problem : _problems) { if (problem.isError()) { return true; } } // return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. CompilationUnit) */ @Override public boolean visit(CompilationUnit node) { // get messages/problems _messages = node.getMessages(); _problems = node.getProblems(); for (IProblem problem : node.getProblems()) { System.out.println(problem); } // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. ImportDeclaration ) */ @Override public boolean visit(ImportDeclaration node) { if (node.isOnDemand()) { return false; } // binding exists if (node.resolveBinding() != null && !node.resolveBinding().isRecovered()) { // get the binding IBinding binding = node.resolveBinding(); // add referenced package if (binding instanceof IPackageBinding) { /* Reference packageReference = */ // _javaSourceResource.recordReference(((IPackageBinding) binding).getName()); } // add referenced type else if (binding instanceof ITypeBinding) { resolveTypeBinding((ITypeBinding) binding, node.getStartPosition(), node.getLength()); } // add referenced type else if (binding instanceof IVariableBinding) { IVariableBinding variableBinding = (IVariableBinding) binding; ITypeBinding typeBinding = variableBinding.getDeclaringClass(); resolveTypeBinding(typeBinding, node.getStartPosition(), node.getLength()); } } // no binding exists else { if (!node.isOnDemand() && !node.isStatic()) { addReferencedType(node.getName().getFullyQualifiedName(), node.getStartPosition(), node.getLength()); } else if (node.isOnDemand() && !node.isStatic()) { addReferencedType(node.getName().getFullyQualifiedName(), node.getStartPosition(), node.getLength()); } else if (!node.isOnDemand() && node.isStatic()) { Name importElementName = node.getName(); String fullQualifiedName = importElementName.getFullyQualifiedName().substring(0, importElementName.getFullyQualifiedName().lastIndexOf('.')); addReferencedType(fullQualifiedName, node.getStartPosition(), node.getLength()); } } // don't visit children return false; } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. TypeDeclaration) */ @SuppressWarnings("unchecked") @Override public boolean visit(TypeDeclaration node) { // declared type superclass type Type superclassType = node.getSuperclassType(); if (superclassType != null) { resolveType(superclassType); } // declared type implemented interfaces types List<Type> interfaces = node.superInterfaceTypes(); for (Type iface : interfaces) { resolveType(iface); } // ITypeBinding typeBinding = node.resolveBinding(); if (typeBinding != null) { IAnnotationBinding[] annotationBindings = typeBinding.getAnnotations(); for (IAnnotationBinding annotationBinding : annotationBindings) { resolveTypeBinding(annotationBinding.getAnnotationType(), node.getStartPosition(), node.getLength()); } } // visit the child nodes return true; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public boolean visit(EnumDeclaration node) { // add super interfaces List<Type> superInterfaces = node.superInterfaceTypes(); for (Type superInterface : superInterfaces) { resolveType(superInterface); } // IAnnotationBinding[] annotationBindings = node.resolveBinding().getAnnotations(); for (IAnnotationBinding annotationBinding : annotationBindings) { resolveTypeBinding(annotationBinding.getAnnotationType(), node.getStartPosition(), node.getLength()); } // visit the child nodes return true; } /** * {@inheritDoc} */ @Override public boolean visit(AnnotationTypeMemberDeclaration node) { // resolve the member type resolveType(node.getType()); // visit the child nodes return true; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public boolean visit(MethodDeclaration node) { // declared method return type Type returnType = node.getReturnType2(); if (returnType != null) { resolveType(returnType); } // declared method argument types List<SingleVariableDeclaration> variableDeclarations = node.parameters(); for (SingleVariableDeclaration singleVariableDeclaration : variableDeclarations) { resolveType(singleVariableDeclaration.getType()); } // declared method exception types List<Name> exceptions = node.thrownExceptions(); for (Name name : exceptions) { resolveTypeName(name, name.getStartPosition(), name.getLength()); } // visit the child nodes return true; } /** * {@inheritDoc} */ @Override public boolean visit(FieldDeclaration node) { // declared field type resolveType(node.getType()); // visit the child nodes return true; } /** * {@inheritDoc} */ @Override public boolean visit(VariableDeclarationStatement node) { // resolve type name resolveType(node.getType()); // visit the child nodes return true; } /** * {@inheritDoc} */ @Override public boolean visit(CatchClause node) { // resolve exception type resolveType(node.getException().getType()); // visit the child nodes return true; } /****************/ /** Expressions */ /****************/ /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. MarkerAnnotation) */ @Override public boolean visit(MarkerAnnotation node) { // resolve type name resolveTypeName(node.getTypeName(), node.getStartPosition(), node.getLength()); // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. NormalAnnotation) */ @Override public boolean visit(NormalAnnotation node) { // resolve type name resolveTypeName(node.getTypeName(), node.getStartPosition(), node.getLength()); // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. SingleMemberAnnotation) */ @Override public boolean visit(SingleMemberAnnotation node) { // resolve type name resolveTypeName(node.getTypeName(), node.getStartPosition(), node.getLength()); // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. ArrayCreation) */ @Override public boolean visit(ArrayCreation node) { // resolve type resolveType(node.getType()); // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. CastExpression) */ @Override public boolean visit(CastExpression node) { // resolve type resolveType(node.getType()); // visit the child nodes return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. ClassInstanceCreation) */ @Override public boolean visit(ClassInstanceCreation node) { // resolve binding IMethodBinding methodBinding = node.resolveConstructorBinding(); if (methodBinding != null) { // resolve type arguments ITypeBinding[] typeArguments = methodBinding.getParameterTypes(); for (ITypeBinding typeBinding : typeArguments) { resolveTypeBinding(typeBinding, node.getStartPosition(), node.getLength()); } // resolve type resolveType(node.getType()); } // visit the child nodes return true; } @Override public boolean visit(FieldAccess node) { // resolve type IVariableBinding variableBinding = node.resolveFieldBinding(); if (variableBinding != null) { resolveTypeBinding(variableBinding.getType(), node.getStartPosition(), node.getLength()); } // visit the child nodes return true; } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. InstanceofExpression) */ @Override public boolean visit(InstanceofExpression node) { // resolve type resolveType(node.getRightOperand()); // visit the child nodes return true; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public boolean visit(MethodInvocation node) { // access to static methods IMethodBinding methodBinding = node.resolveMethodBinding(); // resolveMethodBinding(methodBinding, node.getStartPosition(), node.getLength()); // resolve the associated expression resolveExpressionType(node.getExpression()); // resolve the type arguments List<Expression> typeArguments = node.arguments(); for (Expression exp : typeArguments) { resolveExpressionType(exp); } // visit the child nodes return true; } /** * {@inheritDoc} */ @Override public boolean visit(ConstructorInvocation node) { IMethodBinding methodBinding = node.resolveConstructorBinding(); // resolve type arguments ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); for (ITypeBinding typeBinding : parameterTypes) { resolveTypeBinding(typeBinding, node.getStartPosition(), node.getLength()); } // visit the child nodes return true; } @Override public boolean visit(SuperConstructorInvocation node) { // TODO: Zusammenlegen mit ConstructorInvocation IMethodBinding methodBinding = node.resolveConstructorBinding(); // resolve type arguments ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); for (ITypeBinding typeBinding : parameterTypes) { resolveTypeBinding(typeBinding, node.getStartPosition(), node.getLength()); } // List<Expression> typeArguments = node.arguments(); // for (Expression expression : typeArguments) { // resolveTypeBinding(expression.resolveTypeBinding(), node // .getStartPosition(), node.getLength()); // } // visit the child nodes return true; } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. QualifiedName) */ @Override public boolean visit(QualifiedName node) { // access to static fields IBinding binding = node.resolveBinding(); if (binding != null) { if (binding.getKind() == IBinding.VARIABLE) { IVariableBinding variableBinding = (IVariableBinding) binding; if (Flags.isStatic(variableBinding.getModifiers()) && variableBinding.getDeclaringClass() != null) { resolveTypeBinding(variableBinding.getDeclaringClass(), node.getStartPosition(), node.getLength()); } resolveTypeBinding(variableBinding.getType(), node.getStartPosition(), node.getLength()); } } return true; } @Override public boolean visit(SimpleName node) { IBinding binding = node.resolveBinding(); if (binding != null) { if (binding.getKind() == IBinding.METHOD) { resolveMethodBinding((IMethodBinding) binding, node.getStartPosition(), node.getLength()); } } // else if (binding.getKind() == IBinding.VARIABLE) { // // IVariableBinding variableBinding = (IVariableBinding) binding; // // ITypeBinding typeBinding = variableBinding.getType(); // // resolveTypeBinding(typeBinding, node.getStartPosition(), node // .getLength(), true, false); // } return super.visit(node); } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. ThisExpression) */ @Override public boolean visit(ThisExpression node) { // resolve type name resolveTypeName(node.getQualifier(), node.getStartPosition(), node.getLength()); // visit the child nodes return true; } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. TypeLiteral) */ @Override public boolean visit(TypeLiteral node) { // resolve type name resolveType(node.getType()); // visit the child nodes return true; } /* * (non-Javadoc) * * @seeorg.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. VariableDeclarationExpression) */ @Override public boolean visit(VariableDeclarationExpression node) { // resolve type name resolveType(node.getType()); // visit the child nodes return true; } /***************/ /** Statements */ /***************/ @Override public boolean visit(ThrowStatement node) { if (node.getExpression() != null) { resolveExpressionType(node.getExpression()); } return true; } @Override public boolean visit(ReturnStatement node) { if (node.getExpression() != null) { resolveExpressionType(node.getExpression()); } return true; } /***************/ /** Internal resolve methods **/ /***************/ private void resolveType(Type type) { // return null if type == null if (type == null) { return; } if (type.resolveBinding() != null && !type.resolveBinding().isRecovered()) { // resolve the type binding resolveTypeBinding(type.resolveBinding(), type.getStartPosition(), type.getLength()); } else { // System.out.println("Resolve Type " + type.toString()); } } /** * @param typeName * @return */ private void resolveTypeName(Name typeName, int startPosition, int length) { // return null if typeName == null if (typeName == null) { return; } // get the type binding ITypeBinding typeBinding = typeName.resolveTypeBinding(); // return null if typeBinding == null if (typeBinding == null) { // TODO: Fehlermeldung! return; } // return the qualified name addReferencedType(typeBinding.getBinaryName(), startPosition, length); } /** * <p> * </p> * * @param typeBinding * @param startPosition * @param length * @param resolveSuperTypes */ // private void resolveTypeBinding(ITypeBinding typeBinding, // int startPosition, int length, boolean resolveSuperTypes) { // // resolveTypeBinding(typeBinding, startPosition, length, // resolveSuperTypes, false); // } private void resolveTypeBinding(ITypeBinding typeBinding, int startPosition, int length) { // return null if type == null if (typeBinding == null || typeBinding.isRecovered()) { return; } // if (_typeBindings.contains(typeBinding)) { return; } else { _typeBindings.push(typeBinding); } // handle array types if (typeBinding.isArray()) { resolveTypeBinding(typeBinding.getComponentType(), startPosition, length); } // handle parameterized types else if (typeBinding.isParameterizedType()) { // add the type resolveTypeBinding(typeBinding.getErasure(), startPosition, length); // add the type parameters for (ITypeBinding iTypeBinding : typeBinding.getTypeArguments()) { resolveTypeBinding(iTypeBinding, startPosition, length); } } // handle primitive types else if (typeBinding.isPrimitive()) { // do nothing... } // handle wildcard types else if (typeBinding.isWildcardType()) { // handle bound resolveTypeBinding(typeBinding.getBound(), startPosition, length); } // handle type variable else if (typeBinding.isTypeVariable()) { ITypeBinding[] bindings = typeBinding.getTypeBounds(); for (ITypeBinding iTypeBinding : bindings) { resolveTypeBinding(iTypeBinding, startPosition, length); } } // handle capture else if (typeBinding.isCapture()) { // System.err.println("isCapture: " + typeBinding); } // handle others else { System.out.println(typeBinding.getBinaryName()); if (typeBinding.getBinaryName() != null) { addReferencedType(typeBinding.getBinaryName(), startPosition, length); } else { } } _typeBindings.pop(); } /** * <p> * </p> * * @param methodBinding */ private void resolveMethodBinding(IMethodBinding methodBinding, int startPosition, int length) { // resolve declaring class if method is static if (methodBinding != null) { // static? if (Flags.isStatic(methodBinding.getModifiers())) { ITypeBinding typeBinding = methodBinding.getDeclaringClass(); resolveTypeBinding(typeBinding, startPosition, length); } // resolve type arguments ITypeBinding[] typeArguments = methodBinding.getParameterTypes(); for (ITypeBinding typeBinding : typeArguments) { resolveTypeBinding(typeBinding, startPosition, length); } // resolve Exceptions ITypeBinding[] exceptionTypes = methodBinding.getExceptionTypes(); for (ITypeBinding exceptionType : exceptionTypes) { resolveTypeBinding(exceptionType, startPosition, length); } // resolve return type ITypeBinding returnType = methodBinding.getReturnType(); resolveTypeBinding(returnType, startPosition, length); // if (methodBinding.isParameterizedMethod()) { resolveMethodBinding(methodBinding.getMethodDeclaration(), startPosition, length); } // else if (methodBinding.isGenericMethod()) { ITypeBinding[] typeBindings = methodBinding.getTypeArguments(); for (ITypeBinding typeBinding : typeBindings) { resolveTypeBinding(typeBinding, startPosition, length); } } } } private void addReferencedType(String referencedType, int startPosition, int length) { _references.getOrCreate(referencedType).add(new Position(startPosition, length)); } /** * <p> * </p> * * @param expression */ private void resolveExpressionType(Expression expression) { if (expression != null) { ITypeBinding typeBinding = expression.resolveTypeBinding(); if (typeBinding != null) { resolveTypeBinding(typeBinding, expression.getStartPosition(), expression.getLength()); } } } /** * <p> * </p> * * @author Gerd Wütherich (gerd@gerd-wuetherich.de) */ private static class Position implements IReferenceDetailParser.IPosition { /** - */ private int offset; /** - */ private int length; /** * <p> * Creates a new instance of type {@link Position}. * </p> * * @param offset * @param length */ public Position(int offset, int length) { this.offset = offset; this.length = length; } /** * {@inheritDoc} */ @Override public int getOffset() { return offset; } /** * {@inheritDoc} */ @Override public int getLength() { return length; } } }