public void recursiveTemplateSubstitution(SymbolType st, Map<String, SymbolType> typeParamsMap) {
   List<SymbolType> bounds = st.getBounds();
   if (bounds != null) {
     for (SymbolType bound : bounds) {
       recursiveTemplateSubstitution(bound, typeParamsMap);
     }
   } else {
     List<SymbolType> params = st.getParameterizedTypes();
     if (params != null) {
       List<SymbolType> paramsFinal = new LinkedList<SymbolType>();
       for (SymbolType param : params) {
         String tv = param.getTemplateVariable();
         if (tv != null) {
           SymbolType genericTypeDef = typeParamsMap.get(tv);
           if (genericTypeDef != null) {
             paramsFinal.add(genericTypeDef);
           } else {
             recursiveTemplateSubstitution(param, typeParamsMap);
             paramsFinal.add(param);
           }
         } else {
           recursiveTemplateSubstitution(param, typeParamsMap);
           paramsFinal.add(param);
         }
       }
       st.setParameterizedTypes(paramsFinal);
     }
   }
 }
    private void processSuperGenerics(
        SymbolTable table, Symbol<?> symbol, ClassOrInterfaceType type) {
      if (type != null) {

        Map<String, SymbolType> typeResolution = new HashMap<String, SymbolType>();

        ASTSymbolTypeResolver res = new ASTSymbolTypeResolver(typeResolution, table);
        SymbolType superType = type.accept(res, null);

        List<SymbolType> params = superType.getParameterizedTypes();
        Map<String, SymbolType> typeMapping = table.getTypeParams();
        if (params != null) {
          Symbol<?> superSymbol = symbol.getInnerScope().findSymbol("super");
          Scope innerScope = superSymbol.getInnerScope();

          Scope aux = null;

          if (innerScope != null) {
            aux = new Scope(superSymbol);

          } else {
            aux = new Scope();
          }
          superSymbol.setInnerScope(aux);

          table.pushScope(aux);

          Symbol<?> intermediateSuper =
              new Symbol("super", superSymbol.getType(), superSymbol.getLocation());

          if (innerScope != null) {
            intermediateSuper.setInnerScope(innerScope);
          }

          table.pushSymbol(intermediateSuper);

          // extends a parameterizable type
          TypeVariable<?>[] tps = superType.getClazz().getTypeParameters();
          for (int i = 0; i < tps.length; i++) {

            table.pushSymbol(tps[i].getName(), ReferenceType.TYPE_PARAM, params.get(i), null);
          }
          table.popScope(true);
        }
        Set<String> genericLetters = typeMapping.keySet();
        if (genericLetters != null) {
          for (String letter : genericLetters) {

            if (typeResolution.containsKey(letter)) {
              table.pushSymbol(letter, ReferenceType.TYPE, typeMapping.get(letter), null);
            }
          }
        }
      }
    }
  @Override
  public boolean filter(Method method) throws Exception {

    if (resultTypeFilters != null) {
      b2.setMethod(method);
      b2.setArgumentValues(argumentValues);

      typeMapping = b2.build(new HashMap<String, SymbolType>(typeMapping));
      SymbolType result = SymbolType.valueOf(method, typeMapping);
      List<Class<?>> classes = result.getBoundClasses();
      resultTypeFilters.setElements(classes);
      return resultTypeFilters.filterOne() != null;
    }

    return false;
  }
  public void load(SymbolTable table, List<TypeParameter> typeParams, SymbolType thisType) {
    if (typeParams != null && !typeParams.isEmpty()) {

      List<SymbolType> parameterizedTypes = new LinkedList<SymbolType>();
      for (TypeParameter tp : typeParams) {
        List<ClassOrInterfaceType> typeBounds = tp.getTypeBound();
        List<SymbolType> bounds = new LinkedList<SymbolType>();
        SymbolType st = null;
        if (typeBounds != null) {
          for (ClassOrInterfaceType type : typeBounds) {
            SymbolType paramType = ASTSymbolTypeResolver.getInstance().valueOf(type);
            if (paramType == null) {
              paramType = new SymbolType(Object.class);
            }
            bounds.add(paramType);
          }
          st = new SymbolType(bounds);

        } else {
          st = new SymbolType(Object.class);
        }
        st.setTemplateVariable(tp.getName());
        table.pushSymbol(tp.getName(), ReferenceType.TYPE_PARAM, st, tp);

        parameterizedTypes.add(st);
      }
      Map<String, SymbolType> typeParamsMap = table.getTypeParams();

      for (String key : typeParamsMap.keySet()) {
        SymbolType st = typeParamsMap.get(key);
        recursiveTemplateSubstitution(st, typeParamsMap);
      }

      if (thisType != null && !parameterizedTypes.isEmpty()) {
        thisType.setParameterizedTypes(parameterizedTypes);
      }
    }
  }
  @Override
  public Map<String, SymbolType> build(Map<String, SymbolType> obj) {
    if (obj == null) {
      obj = new HashMap<String, SymbolType>();
    }
    TypeVariable<?>[] typeParams = clazz.getTypeParameters();

    if (typeParams != null) {

      for (int i = 0; i < typeParams.length; i++) {
        if (parameterizedTypes != null) {
          if (i >= parameterizedTypes.size()) {
            SymbolType st = new SymbolType("java.lang.Object");
            st.setTemplateVariable(typeParams[i].getName());
            obj.put(typeParams[i].getName(), st);
          } else {
            if (parameterizedTypes.get(i).getName() == null
                && parameterizedTypes.get(i).hasBounds()) {
              obj.put(typeParams[i].getName(), parameterizedTypes.get(i));
            } else {
              if (!"java.lang.Object".equals(parameterizedTypes.get(i).getName())) {
                obj.put(typeParams[i].getName(), parameterizedTypes.get(i));
              } else {
                SymbolType st = new SymbolType("java.lang.Object");
                st.setTemplateVariable(typeParams[i].getName());
                obj.put(typeParams[i].getName(), st);
              }
            }
          }

        } else {
          Type[] bounds = typeParams[i].getBounds();
          SymbolType resultType = null;
          if (bounds.length == 0) {
            resultType = new SymbolType("java.lang.Object");

          } else {
            try {
              SymbolType auxSt = null;
              for (int j = 0; j < bounds.length; j++) {

                auxSt = SymbolType.valueOf(bounds[j], obj);
                if (resultType == null) {
                  resultType = auxSt;
                } else {
                  resultType = (SymbolType) resultType.merge(auxSt);
                }
              }

            } catch (InvalidTypeException e) {
              throw new RuntimeException("Error processing bounds of  type ", e);
            }
          }
          if (resultType != null) {
            obj.put(typeParams[i].getName(), resultType);
          }
        }
      }
    }
    return obj;
  }