package org.rascalmpl.library.lang.java.m3.internal; import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; 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.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.BlockComment; 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.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; 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.Initializer; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.LineComment; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperFieldAccess; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeParameter; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.type.TypeStore; @SuppressWarnings({"rawtypes", "deprecation"}) public class SourceConverter extends M3Converter { SourceConverter(TypeStore typeStore, Map<String, ISourceLocation> cache) { super(typeStore, cache); } private void visitListOfModifiers(List modif) { for (Iterator it = modif.iterator(); it.hasNext(); ) { ((ASTNode)it.next()).accept(this); } } private void visitFragments(List fragments) { for (Iterator it = fragments.iterator(); it.hasNext(); ) { ((VariableDeclarationFragment) it.next()).accept(this); } } private boolean simpleNameIsConstructorDecl(SimpleName node) { ASTNode parent = node.getParent(); if (parent instanceof MethodDeclaration) { if (((MethodDeclaration) parent).isConstructor() && ((MethodDeclaration) parent).getName() == node) { return true; } } return false; } private void addTypeDependency(ISourceLocation dependency) { if (!scopeManager.isEmpty()) { ISourceLocation parent = getParent(); if (!parent.isEqual(dependency)) { insert(typeDependency, parent, dependency); } } } public void preVisit(ASTNode node) { if (node instanceof Annotation) { insert(annotations, getParent(), resolveBinding(((Annotation) node).getTypeName())); return; } ownValue = resolveBinding(node); } public boolean visit(AnnotationTypeDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(AnnotationTypeDeclaration node) { ownValue = scopeManager.pop(); computeTypeSymbol(node); } public boolean visit(AnnotationTypeMemberDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(AnnotationTypeMemberDeclaration node) { ownValue = scopeManager.pop(); IConstructor type = bindingsResolver.resolveType(node.getType().resolveBinding(), true); insert(types, ownValue, type); } public boolean visit(AnonymousClassDeclaration node) { insert(containment, getParent(), ownValue); // enum constant declaration and classinstancecreation gives types for anonymousclasses ASTNode parent = node.getParent(); if (parent instanceof ClassInstanceCreation) { ISourceLocation superclass = resolveBinding(((ClassInstanceCreation) parent).getType()); insert(typeDependency, ownValue, superclass); IConstructor type = bindingsResolver.resolveType(((ClassInstanceCreation) parent).getType().resolveBinding(), false); insert(types, ownValue, type); if (!superclass.getScheme().contains("+interface")) { insert(extendsRelations, ownValue, superclass); } else { insert(implementsRelations, ownValue, superclass); } } else if (parent instanceof EnumConstantDeclaration) { IVariableBinding var = ((EnumConstantDeclaration) parent).resolveVariable(); insert(typeDependency, ownValue, resolveBinding(var)); if (var != null) { IConstructor type = bindingsResolver.resolveType(var.getType(), false); insert(types, ownValue, type); } } insert(declarations, ownValue, getSourceLocation(node)); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(AnonymousClassDeclaration node) { ownValue = scopeManager.pop(); } public boolean visit(BlockComment node) { insert(documentation, resolveBinding(node.getAlternateRoot()), getSourceLocation(node)); return true; } public boolean visit(ClassInstanceCreation node) { insert(methodInvocation, getParent(), ownValue); insert(uses, getSourceLocation(node), ownValue); return true; } public boolean visit(CompilationUnit node) { insert(declarations, ownValue, getSourceLocation(node)); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(CompilationUnit node) { ownValue = scopeManager.pop(); } public boolean visit(ConstructorInvocation node) { insert(methodInvocation, getParent(), ownValue); return true; } public boolean visit(EnumConstantDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(EnumConstantDeclaration node) { ownValue = scopeManager.pop(); } public boolean visit(EnumDeclaration node) { insert(containment, getParent(), ownValue); IValueList implementedInterfaces = new IValueList(values); if (!node.superInterfaceTypes().isEmpty()) { for (Iterator it = node.superInterfaceTypes().iterator(); it.hasNext();) { Type t = (Type) it.next(); implementedInterfaces.add(resolveBinding(t)); } } insert(implementsRelations, ownValue, implementedInterfaces); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(EnumDeclaration node) { ownValue = scopeManager.pop(); computeTypeSymbol(node); } private void computeTypeSymbol(AbstractTypeDeclaration node) { IConstructor type = bindingsResolver.resolveType(node.resolveBinding(), true); insert(types, ownValue, type); } public boolean visit(FieldAccess node) { insert(fieldAccess, getParent(), ownValue); return true; } public boolean visit(FieldDeclaration node) { visitFragments(node.fragments()); return false; } public boolean visit(Initializer node) { insert(containment, getParent(), ownValue); insert(declarations, ownValue, getSourceLocation(node)); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(Initializer node) { ownValue = scopeManager.pop(); } public boolean visit(Javadoc node) { ASTNode parent = node.getParent(); if (parent == null) { parent = node.getAlternateRoot(); } insert(documentation, resolveBinding(parent), getSourceLocation(node)); return false; } public boolean visit(LineComment node) { insert(documentation, resolveBinding(node.getAlternateRoot()), getSourceLocation(node)); return true; } public boolean visit(MethodDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(MethodDeclaration node) { ownValue = scopeManager.pop(); ASTNode parent = node.getParent(); if (parent instanceof TypeDeclaration) { // TODO: why can this node.resolveBinding sometimes be null? fillOverrides(node.resolveBinding(), ((TypeDeclaration)parent).resolveBinding()); } else if (parent instanceof AnonymousClassDeclaration) { fillOverrides(node.resolveBinding(), ((AnonymousClassDeclaration)parent).resolveBinding()); } IConstructor type = bindingsResolver.resolveType(node.resolveBinding(), true); insert(types, ownValue, type); } private void fillOverrides(IMethodBinding node, ITypeBinding parent) { if (node == null || parent == null) { insert(messages, values.constructor(DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE, values.string("parent or method binding is null, not proceeding with fillOverrides"), getSourceLocation(compilUnit.findDeclaringNode(node)))); return; } List<ITypeBinding> parentClass = new ArrayList<ITypeBinding>(); parentClass.addAll(Arrays.asList(parent.getInterfaces())); parentClass.add(parent.getSuperclass()); for (ITypeBinding parentBinding: parentClass) { if (parentBinding == null) { return; } for (IMethodBinding parentMethod: parentBinding.getDeclaredMethods()) { if (node.overrides(parentMethod) && node.isSubsignature(parentMethod)) { insert(methodOverrides, resolveBinding(node), resolveBinding(parentMethod)); } } fillOverrides(node, parentBinding); } } public boolean visit(MethodInvocation node) { insert(methodInvocation, getParent(), ownValue); //TODO: add to uses as well return true; } public boolean visit(Modifier node) { String modifier = node.getKeyword().toString(); insert(modifiers, getParent(), constructModifierNode(modifier)); return true; } private void generatePackageDecls(ISourceLocation parent, ISourceLocation pkg, ISourceLocation folder) { insert(declarations, pkg, folder); if (!(parent == null)) { insert(containment, parent, pkg); pkg = parent; generatePackageDecls(getParent(pkg), pkg, getParent(folder)); } } private ISourceLocation getParent(ISourceLocation sourceLoc) { File file = new File(sourceLoc.getPath()); String parent = file.getParent(); if (parent != null && !parent.equals("/")) { parent = parent.replaceAll(Matcher.quoteReplacement("\\"), "/"); String authority = null; if (sourceLoc.hasAuthority()) authority = sourceLoc.getAuthority(); try { return values.sourceLocation(sourceLoc.getScheme(), authority, parent); } catch (URISyntaxException e) { throw new RuntimeException("Should not happen", e); } } return null; // there is no parent; } public boolean visit(PackageDeclaration node) { IPackageBinding binding = node.resolveBinding(); if (binding != null) { generatePackageDecls(getParent((ISourceLocation) ownValue), (ISourceLocation) ownValue, getParent(loc)); insert(containment, ownValue, getParent()); } else { insert(messages, values.constructor(DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE, values.string("Unresolved binding for: " + node), values.sourceLocation(loc, 0, 0))); } scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(PackageDeclaration node) { ownValue = scopeManager.pop(); } public boolean visit(QualifiedName node) { if (((ISourceLocation) ownValue).getScheme().equals("java+field")) { insert(fieldAccess, getParent(), ownValue); } return true; } public boolean visit(SimpleName node) { insert(names, values.string(node.getIdentifier()), ownValue); if (!simpleNameIsConstructorDecl(node)) { addTypeDependency(resolveBinding(node.resolveTypeBinding())); if (!node.isDeclaration()) { addTypeDependency(resolveDeclaringClass(node.resolveBinding())); } } return true; } public void endVisit(SimpleName node) { if ((node.isDeclaration() || simpleNameIsConstructorDecl(node))) { insert(declarations, ownValue, getSourceLocation(compilUnit.findDeclaringNode(node.resolveBinding()))); } else { insert(uses, getSourceLocation(node), ownValue); if (((ISourceLocation) ownValue).getScheme().equals("java+field")) { insert(fieldAccess, getParent(), ownValue); } } } public boolean visit(SingleVariableDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); return true; } public void endVisit(SingleVariableDeclaration node) { ownValue = scopeManager.pop(); IConstructor type = bindingsResolver.resolveType(node.getType().resolveBinding(), false); insert(types, ownValue, type); } public boolean visit(SuperConstructorInvocation node) { insert(methodInvocation, getParent(), ownValue); return true; } public boolean visit(SuperFieldAccess node) { insert(fieldAccess, getParent(), ownValue); return true; } public boolean visit(SuperMethodInvocation node) { insert(methodInvocation, getParent(), ownValue); return true; } public boolean visit(TypeDeclaration node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); IValueList extendsClass = new IValueList(values); IValueList implementsInterfaces = new IValueList(values); if (node.getAST().apiLevel() == AST.JLS2) { if (node.getSuperclass() != null) { extendsClass.add(resolveBinding(node.getSuperclass())); } if (!node.superInterfaces().isEmpty()) { for (Iterator it = node.superInterfaces().iterator(); it.hasNext();) { Name n = (Name) it.next(); implementsInterfaces.add(resolveBinding(n)); } } } else if (node.getAST().apiLevel() >= AST.JLS3) { if (node.getSuperclassType() != null) { extendsClass.add(resolveBinding(node.getSuperclassType())); } if (!node.superInterfaceTypes().isEmpty()) { for (Iterator it = node.superInterfaceTypes().iterator(); it.hasNext();) { Type t = (Type) it.next(); implementsInterfaces.add(resolveBinding(t)); } } } if (node.isInterface()) { insert(extendsRelations, ownValue, implementsInterfaces); } else { insert(extendsRelations, ownValue, extendsClass); insert(implementsRelations, ownValue, implementsInterfaces); } return true; } public void endVisit(TypeDeclaration node) { ownValue = scopeManager.pop(); computeTypeSymbol(node); } public boolean visit(TypeParameter node) { IValueList extendsList = new IValueList(values); if (!node.typeBounds().isEmpty()) { for (Iterator it = node.typeBounds().iterator(); it.hasNext();) { Type t = (Type) it.next(); extendsList.add(resolveBinding(t)); } } insert(containment, getParent(), ownValue); return true; } public boolean visit(VariableDeclarationExpression node) { visitFragments(node.fragments()); return false; } public boolean visit(VariableDeclarationFragment node) { insert(containment, getParent(), ownValue); scopeManager.push((ISourceLocation) ownValue); IVariableBinding binding = node.resolveBinding(); if (binding != null) { IConstructor type = bindingsResolver.resolveType(binding.getType(), false); insert(types, ownValue, type); } else { insert(messages, values.constructor(DATATYPE_RASCAL_MESSAGE_ERROR_NODE_TYPE, values.string("No binding for: " + node), values.sourceLocation(loc, 0, 0))); } ASTNode parentASTNode = node.getParent(); if (parentASTNode instanceof FieldDeclaration) { FieldDeclaration parent = (FieldDeclaration)parentASTNode; parent.getType().accept(this); visitListOfModifiers(parent.modifiers()); if (parent.getJavadoc() != null) { parent.getJavadoc().accept(this); } } else if (parentASTNode instanceof VariableDeclarationExpression) { VariableDeclarationExpression parent = (VariableDeclarationExpression)parentASTNode; parent.getType().accept(this); visitListOfModifiers(parent.modifiers()); } else { VariableDeclarationStatement parent = (VariableDeclarationStatement)parentASTNode; parent.getType().accept(this); visitListOfModifiers(parent.modifiers()); } scopeManager.pop(); return true; } public boolean visit(VariableDeclarationStatement node) { visitFragments(node.fragments()); return false; } }