code: public static void extractPlaceholders(ClassNode node, Map<String, GenericsType> map) {
          if (node == null) return;
  
          if (node.isArray()) {
              extractPlaceholders(node.getComponentType(), map);
              return;
          }
  
          if (!node.isUsingGenerics() || !node.isRedirectNode()) return;
          GenericsType[] parameterized = node.getGenericsTypes();
          if (parameterized == null || parameterized.length == 0) return;
          GenericsType[] redirectGenericsTypes = node.redirect().getGenericsTypes();
          if (redirectGenericsTypes==null) redirectGenericsTypes = parameterized;
          for (int i = 0; i < redirectGenericsTypes.length; i++) {
              GenericsType redirectType = redirectGenericsTypes[i];
              if (redirectType.isPlaceholder()) {
                  String name = redirectType.getName();
                  if (!map.containsKey(name)) {
                      GenericsType value = parameterized[i];
                      map.put(name, value);
                      if (value.isWildcard()) {
                          ClassNode lowerBound = value.getLowerBound();
                          if (lowerBound!=null) {
                              extractPlaceholders(lowerBound, map);
                          }
                          ClassNode[] upperBounds = value.getUpperBounds();
                          if (upperBounds!=null) {
                              for (ClassNode upperBound : upperBounds) {
                                  extractPlaceholders(upperBound, map);
                              }
                          }
                      } else if (!value.isPlaceholder()) {
                          extractPlaceholders(value.getType(), map);
                      }
                  }
              }
          }
      }
docs: For a given classnode, fills in the supplied map with the parameterized
  types it defines.
  @param node
  @param map