/**
   * Parse type variables for generics
   *
   * @param variables
   * @return
   */
  protected static TypeVar[] ParseTypeVariables(TypeVariable[] variables, ParamTag[] tags) {
    TypeVar[] vars = null;

    if (variables != null && variables.length > 0) {
      ArrayList<TypeVar> varsList = new ArrayList<TypeVar>();

      for (TypeVariable variable : variables) {
        TypeVar var = new TypeVar();
        var.name = variable.typeName();
        Type[] bounds = variable.bounds();
        if (bounds != null && bounds.length > 0) {
          ArrayList<String> list = new ArrayList<String>();

          for (Type bound : bounds) {
            list.add(bound.qualifiedTypeName());
          }

          var.bounds = list.toArray(new String[] {});
        }
        for (ParamTag tag : tags)
          if (tag.parameterName().equals(var.name)) var.comment = tag.parameterComment();

        varsList.add(var);
      }

      vars = varsList.toArray(new TypeVar[] {});
    }

    return vars;
  }
  /**
   * Prints dependencies recovered from the methods of a class. A dependency is inferred only if
   * another relation between the two classes is not already in the graph.
   *
   * @param classes
   */
  public void printInferredDependencies(ClassDoc c) {
    Options opt = optionProvider.getOptionsFor(c);

    String sourceName = c.toString();
    if (hidden(c)) return;

    Set<Type> types = new HashSet<Type>();
    // harvest method return and parameter types
    for (MethodDoc method : filterByVisibility(c.methods(false), opt.inferDependencyVisibility)) {
      types.add(method.returnType());
      for (Parameter parameter : method.parameters()) {
        types.add(parameter.type());
      }
    }
    // and the field types
    if (!opt.inferRelationships) {
      for (FieldDoc field : filterByVisibility(c.fields(false), opt.inferDependencyVisibility)) {
        types.add(field.type());
      }
    }
    // see if there are some type parameters
    if (c.asParameterizedType() != null) {
      ParameterizedType pt = c.asParameterizedType();
      types.addAll(Arrays.asList(pt.typeArguments()));
    }
    // see if type parameters extend something
    for (TypeVariable tv : c.typeParameters()) {
      if (tv.bounds().length > 0) types.addAll(Arrays.asList(tv.bounds()));
    }

    // and finally check for explicitly imported classes (this
    // assumes there are no unused imports...)
    if (opt.useImports) types.addAll(Arrays.asList(c.importedClasses()));

    // compute dependencies
    for (Type type : types) {
      // skip primitives and type variables, as well as dependencies
      // on the source class
      if (type.isPrimitive()
          || type instanceof WildcardType
          || type instanceof TypeVariable
          || c.toString().equals(type.asClassDoc().toString())) continue;

      // check if the destination is excluded from inference
      ClassDoc fc = type.asClassDoc();
      if (hidden(fc)) continue;

      // check if source and destination are in the same package and if we are allowed
      // to infer dependencies between classes in the same package
      if (!opt.inferDepInPackage && c.containingPackage().equals(fc.containingPackage())) continue;

      // if source and dest are not already linked, add a dependency
      RelationPattern rp = getClassInfo(sourceName).getRelation(fc.toString());
      if (rp == null || rp.matchesOne(new RelationPattern(RelationDirection.OUT))) {
        relation(opt, RelationType.DEPEND, c, fc, "", "", "");
      }
    }
  }