All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.walkmod.sonar.visitors.DeclarationsShouldUseCollectionInterfaces Maven / Gradle / Ivy

/* 
  Copyright (C) 2016 Raquel Pau.
 
  Walkmod is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
 
  Walkmod is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.
 
  You should have received a copy of the GNU Lesser General Public License
  along with Walkmod.  If not, see .*/
package org.walkmod.sonar.visitors;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.walkmod.javalang.ast.CompilationUnit;
import org.walkmod.javalang.ast.ImportDeclaration;
import org.walkmod.javalang.ast.MethodSymbolData;
import org.walkmod.javalang.ast.Node;
import org.walkmod.javalang.ast.SymbolData;
import org.walkmod.javalang.ast.SymbolDataAware;
import org.walkmod.javalang.ast.SymbolDefinition;
import org.walkmod.javalang.ast.SymbolReference;
import org.walkmod.javalang.ast.body.FieldDeclaration;
import org.walkmod.javalang.ast.body.MethodDeclaration;
import org.walkmod.javalang.ast.body.VariableDeclarator;
import org.walkmod.javalang.ast.expr.AssignExpr;
import org.walkmod.javalang.ast.expr.Expression;
import org.walkmod.javalang.ast.expr.MethodCallExpr;
import org.walkmod.javalang.ast.expr.NameExpr;
import org.walkmod.javalang.ast.expr.VariableDeclarationExpr;
import org.walkmod.javalang.ast.stmt.ReturnStmt;
import org.walkmod.javalang.ast.type.ClassOrInterfaceType;
import org.walkmod.javalang.ast.type.Type;
import org.walkmod.javalang.compiler.symbols.RequiresSemanticAnalysis;
import org.walkmod.javalang.compiler.symbols.SymbolType;
import org.walkmod.javalang.visitors.VoidVisitorAdapter;
import org.walkmod.walkers.VisitorContext;

@RequiresSemanticAnalysis
public class DeclarationsShouldUseCollectionInterfaces extends VoidVisitorAdapter {

   private Set pendingTypes = new HashSet();

   @Override
   public void visit(VariableDeclarationExpr n, VisitorContext ctx) {
      SymbolData sd = n.getType().getSymbolData();
      if (sd != null && Collection.class.isAssignableFrom(sd.getClazz()) && !sd.getClazz().isInterface()) {
         visitVariableOrField(n, sd, n.getType(), n.getVars(), ctx);
      }
      super.visit(n, ctx);
   }

   private void visitVariableOrField(Node n, SymbolData sd, Type type, List vars,
         VisitorContext ctx) {
      if (vars != null) {
         Class clazz = null;
         boolean refactor = true;
         for (VariableDeclarator var : vars) {
            List usages = var.getUsages();

            if (usages != null) {
               GetGenericCollectionClass visitor = new GetGenericCollectionClass();

               Iterator itRef = usages.iterator();

               while (itRef.hasNext() && refactor) {
                  SymbolReference usage = itRef.next();
                  ((Node) usage).accept(visitor, ctx);
                  refactor = visitor.isRefactorizable();
               }
               if (refactor) {
                  if (clazz == null) {
                     clazz = visitor.getDeclaringClass();
                  } else if (!clazz.isAssignableFrom(visitor.getDeclaringClass())) {
                     clazz = visitor.getDeclaringClass();
                  }
               }

            }
         }
         if (refactor) {

            if (clazz == null) {
               if (!sd.getClazz().isInterface()) {
                  Class[] interfaces = sd.getClazz().getInterfaces();
                  for (int i = 0; i < interfaces.length && clazz == null; i++) {
                     if (Collection.class.isAssignableFrom(interfaces[i])) {
                        clazz = interfaces[i];
                     }
                  }
               }
            }
            if (clazz != null) {
               if (clazz.isAssignableFrom(sd.getClazz()) && !sd.getClazz().isAssignableFrom(clazz)) {
                  ClassOrInterfaceType aux = new ClassOrInterfaceType(clazz.getSimpleName());
                  GetTypeArgs resolver = new GetTypeArgs();
                  type.accept(resolver, ctx);
                  aux.setTypeArgs(resolver.typeArgs());
                  aux.setSymbolData(new SymbolType(clazz));
                  n.replaceChildNode(type, aux);
                  pendingTypes.add(clazz.getName());
               }
            }
         }

      }
   }

   @Override
   public void visit(FieldDeclaration n, VisitorContext ctx) {
      SymbolData sd = n.getType().getSymbolData();
      if (sd != null && Collection.class.isAssignableFrom(sd.getClazz()) && !sd.getClazz().isInterface()) {
         visitVariableOrField(n, sd, n.getType(), n.getVariables(), ctx);
      }
      super.visit(n, ctx);
   }

   @Override
   public void visit(CompilationUnit n, VisitorContext ctx) {
      pendingTypes.clear();
      super.visit(n, ctx);
      if (!pendingTypes.isEmpty()) {
         boolean add = true;

         List imports = n.getImports();
         for (String type : pendingTypes) {
            if (imports != null) {
               Iterator it = imports.iterator();
               boolean exists = false;
               while (it.hasNext() && !exists) {
                  ImportDeclaration id = it.next();
                  if (!id.isAsterisk()) {
                     String typeName = id.getName().toString();
                     if (typeName.equals(type)) {
                        exists = true;
                     }
                  } else {
                     List references = id.getUsages();
                     if (references != null) {
                        Iterator itRef = references.iterator();

                        while (itRef.hasNext() && !exists) {
                           SymbolReference ref = itRef.next();
                           if (ref instanceof SymbolDataAware) {
                              SymbolDataAware sda = (SymbolDataAware) ref;
                              SymbolData sd = sda.getSymbolData();
                              if (sd != null) {
                                 if (sd.getName().equals(type)) {
                                    exists = true;
                                 }
                              }
                           }
                        }
                     }
                  }
               }
               add = !exists;
            }
            if (add) {
               imports.add(new ImportDeclaration(new NameExpr(type), false, false));
            }
         }
      }

   }
   
   private class GetTypeArgs extends VoidVisitorAdapter{
      
      private List typeArgs = null;
      
      @Override
      public void visit(ClassOrInterfaceType type, VisitorContext ctx){
         typeArgs = type.getTypeArgs();
      }
      
      public List typeArgs(){
         return typeArgs;
      }
   }

   private class GetGenericCollectionClass extends VoidVisitorAdapter {

      private Class declaringClass = null;

      private boolean refactorizable = true;

      @Override
      public void visit(MethodCallExpr n, VisitorContext ctx) {
         MethodSymbolData msd = n.getSymbolData();
         if (msd != null) {
            Class clazz = msd.getMethod().getDeclaringClass();
            if (declaringClass == null) {
               declaringClass = clazz;
            } else {
               if (!declaringClass.isAssignableFrom(clazz) && clazz.isAssignableFrom(declaringClass)) {
                  declaringClass = clazz;
               }
            }
         } else {
            refactorizable = false;
         }
         super.visit(n, ctx);
      }

      public void visit(NameExpr n, VisitorContext ctx) {
         super.visit(n, ctx);
         Node parent = n.getParentNode();
         if (parent instanceof ReturnStmt) {
            Node methodDeclaration = n.getParentNode();
            while (!(methodDeclaration instanceof MethodDeclaration)) {
               methodDeclaration = methodDeclaration.getParentNode();
            }
            MethodDeclaration md = (MethodDeclaration) methodDeclaration;
            SymbolData sd = md.getType().getSymbolData();
            if (sd != null) {
               Class clazz = sd.getClazz();
               if (declaringClass == null) {
                  declaringClass = clazz;
               } else {
                  if (!declaringClass.isAssignableFrom(clazz) && clazz.isAssignableFrom(declaringClass)) {
                     declaringClass = clazz;
                  }
               }
            } else {
               refactorizable = false;
            }
         } else if (parent instanceof AssignExpr) {
            AssignExpr assign = (AssignExpr) parent;
            if (assign.getValue() == n) {
               Expression target = assign.getTarget();
               if (target instanceof SymbolReference) {
                  SymbolReference sr = (SymbolReference) target;
                  SymbolDefinition sd = sr.getSymbolDefinition();
                  if (sd instanceof SymbolDataAware) {

                     DeclarationsShouldUseCollectionInterfaces auxVisitor = new DeclarationsShouldUseCollectionInterfaces();
                     ((Node) sd).accept(auxVisitor, ctx);
                     SymbolDataAware sda = (SymbolDataAware) sd;
                     SymbolData computedSd = sda.getSymbolData();
                     if (computedSd != null) {
                        Class clazz = computedSd.getClazz();
                        if (declaringClass == null) {
                           declaringClass = clazz;
                        } else {
                           if (!declaringClass.isAssignableFrom(clazz) && clazz.isAssignableFrom(declaringClass)) {
                              declaringClass = clazz;
                           }
                        }
                     } else {
                        refactorizable = false;
                     }
                  }
               }
            }
         }
      }

      public boolean isRefactorizable() {
         return refactorizable;
      }

      public Class getDeclaringClass() {
         return declaringClass;
      }

   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy