/**
  * Return the full name of a relation's node c. This may involve appending the port :p for the
  * standard nodes whose outline is rendered through an inner table.
  *
  * @param c the node's class (may be null)
  * @param cName the node's class name
  */
 private String relationNode(ClassDoc c, String cName) {
   Options opt;
   if (c == null) opt = optionProvider.getOptionsFor(cName);
   else opt = optionProvider.getOptionsFor(c);
   String name = getNodeName(cName);
   return name + opt.shape.landingPort();
 }
 /** Return true if c has a @hidden tag associated with it */
 private boolean hidden(ProgramElementDoc c) {
   Tag tags[] = c.tags("hidden");
   if (tags.length > 0) return true;
   tags = c.tags("view");
   if (tags.length > 0) return true;
   Options opt;
   if (c instanceof ClassDoc) opt = optionProvider.getOptionsFor((ClassDoc) c);
   else opt = optionProvider.getOptionsFor(c.containingClass());
   return opt.matchesHideExpression(c.toString());
 }
 /** Print classes that were parts of relationships, but not parsed by javadoc */
 public void printExtraClasses(RootDoc root) {
   Set<String> names = new HashSet<String>(classnames.keySet());
   for (String className : names) {
     ClassInfo info = getClassInfo(className);
     if (!info.nodePrinted) {
       ClassDoc c = root.classNamed(className);
       if (c != null) {
         printClass(c, false);
       } else {
         Options opt = optionProvider.getOptionsFor(className);
         if (opt.matchesHideExpression(className)) continue;
         w.println("\t// " + className);
         w.print("\t" + info.name + "[label=");
         externalTableStart(opt, className, classToUrl(className));
         innerTableStart();
         int idx = className.lastIndexOf(".");
         if (opt.postfixPackage && idx > 0 && idx < (className.length() - 1)) {
           String packageName = className.substring(0, idx);
           String cn = className.substring(idx + 1);
           tableLine(Align.CENTER, escape(cn), opt, Font.CLASS);
           tableLine(Align.CENTER, packageName, opt, Font.PACKAGE);
         } else {
           tableLine(Align.CENTER, escape(className), opt, Font.CLASS);
         }
         innerTableEnd();
         externalTableEnd();
         if (className == null || className.length() == 0)
           w.print(", URL=\"" + classToUrl(className) + "\"");
         nodeProperties(opt);
       }
     }
   }
 }
  /**
   * 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, "", "", "");
      }
    }
  }
  private FieldRelationInfo getFieldRelationInfo(FieldDoc field) {
    Type type = field.type();
    if (type.isPrimitive() || type instanceof WildcardType || type instanceof TypeVariable)
      return null;

    if (type.dimension().endsWith("[]")) {
      return new FieldRelationInfo(type.asClassDoc(), true);
    }

    Options opt = optionProvider.getOptionsFor(type.asClassDoc());
    if (opt.matchesCollPackageExpression(type.qualifiedTypeName())) {
      Type[] argTypes = getInterfaceTypeArguments(collectionClassDoc, type);
      if (argTypes != null && argTypes.length == 1 && !argTypes[0].isPrimitive())
        return new FieldRelationInfo(argTypes[0].asClassDoc(), true);

      argTypes = getInterfaceTypeArguments(mapClassDoc, type);
      if (argTypes != null && argTypes.length == 2 && !argTypes[1].isPrimitive())
        return new FieldRelationInfo(argTypes[1].asClassDoc(), true);
    }

    return new FieldRelationInfo(type.asClassDoc(), false);
  }
  /**
   * Prints associations recovered from the fields of a class. An association is inferred only if
   * another relation between the two classes is not already in the graph.
   *
   * @param classes
   */
  public void printInferredRelations(ClassDoc c) {
    Options opt = optionProvider.getOptionsFor(c);

    // check if the source is excluded from inference
    if (hidden(c)) return;

    for (FieldDoc field : c.fields(false)) {
      if (hidden(field)) continue;

      // skip statics
      if (field.isStatic()) continue;

      // skip primitives
      FieldRelationInfo fri = getFieldRelationInfo(field);
      if (fri == null) continue;

      // check if the destination is excluded from inference
      if (hidden(fri.cd)) continue;

      String destAdornment = fri.multiple ? "*" : "";
      relation(opt, opt.inferRelationshipType, c, fri.cd, "", "", destAdornment);
    }
  }
  /** Print a class's relations */
  public void printRelations(ClassDoc c) {
    Options opt = optionProvider.getOptionsFor(c);
    if (hidden(c)
        || c.name()
            .equals("")) // avoid phantom classes, they may pop up when the source uses annotations
    return;
    String className = c.toString();

    // Print generalization (through the Java superclass)
    Type s = c.superclassType();
    if (s != null
        && !s.toString().equals("java.lang.Object")
        && !c.isEnum()
        && !hidden(s.asClassDoc())) {
      ClassDoc sc = s.asClassDoc();
      w.println(
          "\t//"
              + c
              + " extends "
              + s
              + "\n"
              + "\t"
              + relationNode(sc)
              + " -> "
              + relationNode(c)
              + " [dir=back,arrowtail=empty];");
      getClassInfo(className)
          .addRelation(sc.toString(), RelationType.EXTENDS, RelationDirection.OUT);
      getClassInfo(sc.toString())
          .addRelation(className, RelationType.EXTENDS, RelationDirection.IN);
    }

    // Print generalizations (through @extends tags)
    for (Tag tag : c.tags("extends"))
      if (!hidden(tag.text())) {
        ClassDoc from = c.findClass(tag.text());
        w.println(
            "\t//"
                + c
                + " extends "
                + tag.text()
                + "\n"
                + "\t"
                + relationNode(from, tag.text())
                + " -> "
                + relationNode(c)
                + " [dir=back,arrowtail=empty];");
        getClassInfo(className)
            .addRelation(tag.text(), RelationType.EXTENDS, RelationDirection.OUT);
        getClassInfo(tag.text()).addRelation(className, RelationType.EXTENDS, RelationDirection.IN);
      }
    // Print realizations (Java interfaces)
    for (Type iface : c.interfaceTypes()) {
      ClassDoc ic = iface.asClassDoc();
      if (!hidden(ic)) {
        w.println(
            "\t//"
                + c
                + " implements "
                + ic
                + "\n\t"
                + relationNode(ic)
                + " -> "
                + relationNode(c)
                + " [dir=back,arrowtail=empty,style=dashed];");
        getClassInfo(className)
            .addRelation(ic.toString(), RelationType.IMPLEMENTS, RelationDirection.OUT);
        getClassInfo(ic.toString())
            .addRelation(className, RelationType.IMPLEMENTS, RelationDirection.IN);
      }
    }
    // Print other associations
    allRelation(opt, RelationType.ASSOC, c);
    allRelation(opt, RelationType.NAVASSOC, c);
    allRelation(opt, RelationType.HAS, c);
    allRelation(opt, RelationType.NAVHAS, c);
    allRelation(opt, RelationType.COMPOSED, c);
    allRelation(opt, RelationType.NAVCOMPOSED, c);
    allRelation(opt, RelationType.DEPEND, c);
  }
 /**
  * Return the full name of a relation's node. This may involve appending the port :p for the
  * standard nodes whose outline is rendered through an inner table.
  */
 private String relationNode(ClassDoc c) {
   Options opt = optionProvider.getOptionsFor(c);
   String name = getNodeName(c);
   return name + opt.shape.landingPort();
 }
  /**
   * Prints the class if needed.
   *
   * <p>A class is a rootClass if it's included among the classes returned by RootDoc.classes(),
   * this information is used to properly compute relative links in diagrams for UMLDoc
   */
  public String printClass(ClassDoc c, boolean rootClass) {
    ClassInfo ci;
    boolean toPrint;
    Options opt = optionProvider.getOptionsFor(c);

    String className = c.toString();
    if ((ci = getClassInfo(className)) != null) toPrint = !ci.nodePrinted;
    else {
      toPrint = true;
      ci = newClassInfo(className, true, hidden(c));
    }
    if (toPrint && !hidden(c) && (!c.isEnum() || opt.showEnumerations)) {
      // Associate classname's alias
      String r = className;
      w.println("\t// " + r);
      // Create label
      w.print("\t" + ci.name + " [label=");

      boolean showMembers =
          (opt.showAttributes && c.fields().length > 0)
              || (c.isEnum() && opt.showEnumConstants && c.enumConstants().length > 0)
              || (opt.showOperations && c.methods().length > 0)
              || (opt.showConstructors && c.constructors().length > 0);

      externalTableStart(opt, c.qualifiedName(), classToUrl(c, rootClass));

      // Calculate the number of innerTable rows we will emmit
      int nRows = 1;
      if (showMembers) {
        if (opt.showAttributes) nRows++;
        else if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) nRows++;
        if (c.isEnum() && opt.showEnumConstants) nRows++;
        if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) nRows++;
      }

      firstInnerTableStart(opt, nRows);
      if (c.isInterface()) tableLine(Align.CENTER, guilWrap(opt, "interface"));
      if (c.isEnum()) tableLine(Align.CENTER, guilWrap(opt, "enumeration"));
      stereotype(opt, c, Align.CENTER);
      Font font = c.isAbstract() && !c.isInterface() ? Font.CLASS_ABSTRACT : Font.CLASS;
      String qualifiedName = qualifiedName(opt, r);
      int startTemplate = qualifiedName.indexOf('<');
      int idx = 0;
      if (startTemplate < 0) idx = qualifiedName.lastIndexOf('.');
      else idx = qualifiedName.lastIndexOf('.', startTemplate);
      if (opt.showComment)
        tableLine(Align.LEFT, htmlNewline(escape(c.commentText())), opt, Font.CLASS);
      else if (opt.postfixPackage && idx > 0 && idx < (qualifiedName.length() - 1)) {
        String packageName = qualifiedName.substring(0, idx);
        String cn = className.substring(idx + 1);
        tableLine(Align.CENTER, escape(cn), opt, font);
        tableLine(Align.CENTER, packageName, opt, Font.PACKAGE);
      } else {
        tableLine(Align.CENTER, escape(qualifiedName), opt, font);
      }
      tagvalue(opt, c);
      firstInnerTableEnd(opt, nRows);

      /*
       * Warning: The boolean expressions guarding innerTableStart()
       * in this block, should match those in the code block above
       * marked: "Calculate the number of innerTable rows we will emmit"
       */
      if (showMembers) {
        if (opt.showAttributes) {
          innerTableStart();
          FieldDoc[] fields = c.fields();
          // if there are no fields, print an empty line to generate proper HTML
          if (fields.length == 0) tableLine(Align.LEFT, "");
          else attributes(opt, c.fields());
          innerTableEnd();
        } else if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) {
          // show an emtpy box if we don't show attributes but
          // we show operations
          innerTableStart();
          tableLine(Align.LEFT, "");
          innerTableEnd();
        }
        if (c.isEnum() && opt.showEnumConstants) {
          innerTableStart();
          FieldDoc[] ecs = c.enumConstants();
          // if there are no constants, print an empty line to generate proper HTML
          if (ecs.length == 0) {
            tableLine(Align.LEFT, "");
          } else {
            for (FieldDoc fd : c.enumConstants()) {
              tableLine(Align.LEFT, fd.name());
            }
          }
          innerTableEnd();
        }
        if (!c.isEnum() && (opt.showConstructors || opt.showOperations)) {
          innerTableStart();
          boolean printedLines = false;
          if (opt.showConstructors) printedLines |= operations(opt, c.constructors());
          if (opt.showOperations) printedLines |= operations(opt, c.methods());

          if (!printedLines)
            // if there are no operations nor constructors,
            // print an empty line to generate proper HTML
            tableLine(Align.LEFT, "");

          innerTableEnd();
        }
      }
      externalTableEnd();
      w.print(", URL=\"" + classToUrl(c, rootClass) + "\"");
      nodeProperties(opt);

      // If needed, add a note for this node
      int ni = 0;
      for (Tag t : c.tags("note")) {
        String noteName = "n" + ni + "c" + ci.name;
        w.print("\t// Note annotation\n");
        w.print("\t" + noteName + " [label=");
        externalTableStart(
            UmlGraph.getCommentOptions(), c.qualifiedName(), classToUrl(c, rootClass));
        innerTableStart();
        tableLine(
            Align.LEFT, htmlNewline(escape(t.text())), UmlGraph.getCommentOptions(), Font.CLASS);
        innerTableEnd();
        externalTableEnd();
        nodeProperties(UmlGraph.getCommentOptions());
        w.print("\t" + noteName + " -> " + relationNode(c) + "[arrowhead=none];\n");
        ni++;
      }
      ci.nodePrinted = true;
    }
    return ci.name;
  }
 /** Return true if the class name is associated to an hidden class or matches a hide expression */
 private boolean hidden(String s) {
   ClassInfo ci = getClassInfo(s);
   Options opt = optionProvider.getOptionsFor(s);
   if (ci != null) return ci.hidden || opt.matchesHideExpression(s);
   else return opt.matchesHideExpression(s);
 }