/**
   * Parses an annotation type definition
   *
   * @param docClass
   * @return
   */
  protected static Annotation ParseAnnotation(ClassDoc docClass) {
    AnnotationTypeDoc docAnnotation = (AnnotationTypeDoc) docClass;

    assert (docAnnotation != null);

    Annotation xmlAnnotation = new Annotation();

    xmlAnnotation.name = docClass.name();
    xmlAnnotation.qualifiedName = docClass.qualifiedName();
    xmlAnnotation.comment = docClass.commentText();
    xmlAnnotation.isIncluded = docClass.isIncluded();
    xmlAnnotation.scope = DetermineScope(docClass);

    AnnotationTypeElementDoc[] elements = docAnnotation.elements();

    if (elements != null && elements.length > 0) {
      ArrayList<AnnotationElement> elementList = new ArrayList<AnnotationElement>();

      for (AnnotationTypeElementDoc element : elements) {
        elementList.add(ParseAnnotationElement(element));
      }

      xmlAnnotation.elements = elementList.toArray(new AnnotationElement[] {});
    } else {
      log.debug("No elements in annotation: " + docClass.name());
    }

    xmlAnnotation.annotationInstances =
        ParseAnnotationInstances(docClass.annotations(), docClass.qualifiedName());
    return xmlAnnotation;
  }
  protected void generateDeclsForClass(PrintWriter pw, ClassDoc clazz, String cname)
      throws ClassNotFoundException {
    doneHandleTypes = new Hashtable();
    /* The following handle types are predefined in "typedefs.h". Suppress
    inclusion in the output by generating them "into the blue" here. */
    genHandleType(null, "java.lang.Class");
    genHandleType(null, "java.lang.ClassLoader");
    genHandleType(null, "java.lang.Object");
    genHandleType(null, "java.lang.String");
    genHandleType(null, "java.lang.Thread");
    genHandleType(null, "java.lang.ThreadGroup");
    genHandleType(null, "java.lang.Throwable");

    pw.println("/* LLNI Header for class " + clazz.qualifiedName() + " */" + lineSep);
    pw.println("#ifndef _Included_" + cname);
    pw.println("#define _Included_" + cname);
    pw.println("#include \"typedefs.h\"");
    pw.println("#include \"llni.h\"");
    pw.println("#include \"jni.h\"" + lineSep);

    forwardDecls(pw, clazz);
    structSectionForClass(pw, clazz, cname);
    methodSectionForClass(pw, clazz, cname);
    pw.println("#endif");
  }
  /**
   * Constructor.
   *
   * @param filename the file to be generated.
   * @throws IOException
   * @throws DocletAbortException
   */
  public PackageUseWriter(
      ConfigurationImpl configuration, ClassUseMapper mapper, DocPath filename, PackageDoc pkgdoc)
      throws IOException {
    super(configuration, DocPath.forPackage(pkgdoc).resolve(filename));
    this.pkgdoc = pkgdoc;

    // by examining all classes in this package, find what packages
    // use these classes - produce a map between using package and
    // used classes.
    ClassDoc[] content = pkgdoc.allClasses();
    for (int i = 0; i < content.length; ++i) {
      ClassDoc usedClass = content[i];
      Set<ClassDoc> usingClasses = mapper.classToClass.get(usedClass.qualifiedName());
      if (usingClasses != null) {
        for (Iterator<ClassDoc> it = usingClasses.iterator(); it.hasNext(); ) {
          ClassDoc usingClass = it.next();
          PackageDoc usingPackage = usingClass.containingPackage();
          Set<ClassDoc> usedClasses = usingPackageToUsedClasses.get(usingPackage.name());
          if (usedClasses == null) {
            usedClasses = new TreeSet<ClassDoc>();
            usingPackageToUsedClasses.put(Util.getPackageName(usingPackage), usedClasses);
          }
          usedClasses.add(usedClass);
        }
      }
    }
  }
 protected void write(OutputStream o, ClassDoc clazz) throws ClassNotFoundException {
   String cname = mangleClassName(clazz.qualifiedName());
   PrintWriter pw = wrapWriter(o);
   fields = clazz.fields();
   methods = clazz.methods();
   generateDeclsForClass(pw, clazz, cname);
 }
 /** Convert the class name into a corresponding URL */
 public String classToUrl(ClassDoc cd, boolean rootClass) {
   // building relative path for context and package diagrams
   if (contextDoc != null && rootClass) {
     // determine the context path, relative to the root
     String packageName = null;
     if (contextDoc instanceof ClassDoc) {
       packageName = ((ClassDoc) contextDoc).containingPackage().name();
     } else if (contextDoc instanceof PackageDoc) {
       packageName = ((PackageDoc) contextDoc).name();
     } else {
       return classToUrl(cd.qualifiedName());
     }
     return buildRelativePath(packageName, cd.containingPackage().name()) + cd.name() + ".html";
   } else {
     return classToUrl(cd.qualifiedName());
   }
 }
 FieldDefsRes(ClassDoc clazz, FieldDefsRes parent, boolean bottomMost) {
   this.className = clazz.qualifiedName();
   this.parent = parent;
   this.bottomMost = bottomMost;
   int byteSize = 0;
   if (parent == null) this.s = "";
   else this.s = parent.s;
 }
 /**
  * Return the qualified name of the <code>ClassDoc</code> if it's qualifier is not excluded.
  * Otherwise, return the unqualified <code>ClassDoc</code> name.
  *
  * @param cd the <code>ClassDoc</code> to check.
  */
 public String getClassName(ClassDoc cd) {
   PackageDoc pd = cd.containingPackage();
   if (pd != null && shouldExcludeQualifier(cd.containingPackage().name())) {
     return cd.name();
   } else {
     return cd.qualifiedName();
   }
 }
  /**
   * Parses an interface type definition
   *
   * @param docClass
   * @return
   */
  protected static Interface ParseInterface(ClassDoc docClass) {
    assert (docClass != null);

    Interface xmlInterface = new Interface();

    xmlInterface.name = docClass.name();
    xmlInterface.qualifiedName = docClass.qualifiedName();
    xmlInterface.comment = docClass.commentText();
    xmlInterface.isIncluded = docClass.isIncluded();
    xmlInterface.scope = DetermineScope(docClass);
    xmlInterface.typeVariables =
        ParseTypeVariables(docClass.typeParameters(), docClass.typeParamTags());

    Type[] interfaces = docClass.interfaceTypes();

    ArrayList<String> interfaceTypeNames = new ArrayList<String>();
    if (interfaces != null && interfaces.length > 0) {
      for (Type interfaceType : interfaces) {
        interfaceTypeNames.add(interfaceType.qualifiedTypeName());
      }

      xmlInterface.interfaces = interfaceTypeNames.toArray(new String[] {});
    }

    MethodDoc[] methods = docClass.methods();

    if (methods != null && methods.length > 0) {
      ArrayList<Method> methodList = new ArrayList<Method>();

      for (MethodDoc method : methods) {
        methodList.add(ParseMethod(method));
      }

      xmlInterface.methods = methodList.toArray(new Method[] {});
    } else {
      log.debug("No methods in interface: " + docClass.name());
    }

    xmlInterface.annotationInstances =
        ParseAnnotationInstances(docClass.annotations(), docClass.qualifiedName());
    return xmlInterface;
  }
  /**
   * Parses the enum type definition
   *
   * @param docClass
   * @return
   */
  protected static Enum ParseEnum(ClassDoc docClass) {
    assert (docClass != null);

    Enum xmlEnum = new Enum();

    xmlEnum.name = docClass.name();
    xmlEnum.qualifiedName = docClass.qualifiedName();
    xmlEnum.comment = docClass.commentText();
    xmlEnum.isIncluded = docClass.isIncluded();
    xmlEnum.scope = DetermineScope(docClass);
    Type superClassType = docClass.superclassType();
    if (superClassType != null) {
      xmlEnum.superClass = superClassType.qualifiedTypeName();
    }

    Type[] interfaces = docClass.interfaceTypes();

    ArrayList<String> interfaceTypeNames = new ArrayList<String>();
    if (interfaces != null && interfaces.length > 0) {
      for (Type interfaceType : interfaces) {
        interfaceTypeNames.add(interfaceType.qualifiedTypeName());
      }
    }

    xmlEnum.extendedFrom = interfaceTypeNames.toArray(new String[] {});

    FieldDoc[] fields = docClass.enumConstants();

    if (fields != null && fields.length > 0) {
      ArrayList<EnumField> fieldList = new ArrayList<EnumField>();

      for (FieldDoc field : fields) {
        fieldList.add(ParseEnumField(field));
      }

      xmlEnum.fields = fieldList.toArray(new EnumField[] {});
    }

    xmlEnum.annotationInstances =
        ParseAnnotationInstances(docClass.annotations(), docClass.qualifiedName());
    return xmlEnum;
  }
 @Override
 protected Map<PLACE_HOLDER, String[]> getDetails(PackageDoc doc) {
   Map<PLACE_HOLDER, String[]> map = new HashMap<>();
   buildDetail(map, PLACE_HOLDER.PACKAGE_NAME, doc.name());
   buildDetail(map, PLACE_HOLDER.COMMENT, DocletUtil.renderComment(doc));
   StringBuffer sb = new StringBuffer();
   ClassDoc[] docs = doc.allClasses(false);
   if (docs != null) {
     for (ClassDoc classDoc : docs) {
       sb.append("<li>");
       sb.append("<a href=\"javascript:void(0);\" onclick=\"navigateTo('")
           .append(classDoc.qualifiedName())
           .append("');\">");
       sb.append(classDoc.qualifiedName()).append("</a>");
       sb.append("</li>");
     }
   }
   buildDetail(map, PLACE_HOLDER.CHILD_CLASSES, sb.toString());
   return map;
 }
  protected void forwardDecls(PrintWriter pw, ClassDoc clazz) throws ClassNotFoundException {
    ClassDoc clazzfield = null;

    if (clazz.qualifiedName().equals("java.lang.Object")) return;
    genHandleType(pw, clazz.qualifiedName());
    ClassDoc superClass = clazz.superclass();

    if (superClass != null) {
      String superClassName = superClass.qualifiedName();
      forwardDecls(pw, superClass);
    }

    for (int i = 0; i < fields.length; i++) {
      FieldDoc field = (FieldDoc) fields[i];

      if (!field.isStatic()) {
        Type t = field.type();
        String tname = t.qualifiedTypeName();
        TypeSignature newTypeSig = new TypeSignature(root);
        String sig = newTypeSig.getTypeSignature(tname);

        if (sig.charAt(0) != '[') forwardDeclsFromSig(pw, sig);
      }
    }

    for (int i = 0; i < methods.length; i++) {
      MethodDoc method = (MethodDoc) methods[i];

      if (method.isNative()) {
        Type retType = method.returnType();
        String typesig = method.signature();
        TypeSignature newTypeSig = new TypeSignature(root);
        String sig = newTypeSig.getTypeSignature(typesig, retType);

        if (sig.charAt(0) != '[') forwardDeclsFromSig(pw, sig);
      }
    }
  }
 /** Generate the class use list. */
 protected void generateClassUseFile() throws IOException {
   Content body = getClassUseHeader();
   HtmlTree div = new HtmlTree(HtmlTag.DIV);
   div.addStyle(HtmlStyle.classUseContainer);
   if (pkgSet.size() > 0) {
     addClassUse(div);
   } else {
     div.addContent(getResource("doclet.ClassUse_No.usage.of.0", classdoc.qualifiedName()));
   }
   body.addContent(div);
   addNavLinks(false, body);
   addBottom(body);
   printHtmlDocument(null, true, body);
 }
 /**
  * Build the field information.
  *
  * @param node the XML element that specifies which components to document
  * @param fieldsContentTree content tree to which the documentation will be added
  */
 public void buildFieldInfo(XMLNode node, Content fieldsContentTree) {
   if (configuration.nocomment) {
     return;
   }
   FieldDoc field = (FieldDoc) currentMember;
   ClassDoc cd = field.containingClass();
   // Process default Serializable field.
   if ((field.tags("serial").length == 0) && !field.isSynthetic() && configuration.serialwarn) {
     configuration.message.warning(
         field.position(), "doclet.MissingSerialTag", cd.qualifiedName(), field.name());
   }
   fieldWriter.addMemberDescription(field, fieldsContentTree);
   fieldWriter.addMemberTags(field, fieldsContentTree);
 }
  /**
   * Create a new ClassGraph.
   *
   * <p>The packages passed as an argument are the ones specified on the command line.
   *
   * <p>Local URLs will be generated for these packages.
   *
   * @param root The root of docs as provided by the javadoc API
   * @param optionProvider The main option provider
   * @param contextDoc The current context for generating relative links, may be a ClassDoc or a
   *     PackageDoc (used by UMLDoc)
   */
  public ClassGraph(RootDoc root, OptionProvider optionProvider, Doc contextDoc) {
    this.optionProvider = optionProvider;
    this.collectionClassDoc = root.classNamed("java.util.Collection");
    this.mapClassDoc = root.classNamed("java.util.Map");
    this.contextDoc = contextDoc;

    // to gather the packages containing specified classes, loop thru them and gather
    // package definitions. User root.specifiedPackages is not safe, since the user
    // may specify just a list of classes (human users usually don't, but automated tools do)
    rootClasses = new HashSet<String>();
    for (ClassDoc classDoc : root.classes()) {
      rootClasses.add(classDoc.qualifiedName());
      rootClassdocs.put(classDoc.qualifiedName(), classDoc);
    }

    Options opt = optionProvider.getGlobalOptions();
    if (opt.compact) {
      linePrefix = "";
      linePostfix = "";
    } else {
      linePrefix = "\t";
      linePostfix = "\n";
    }
  }
Exemple #15
0
 /**
  * Given an array of <code>Tag</code>s representing this custom tag, return its string
  * representation.
  *
  * @param throwTags the array of <code>ThrowsTag</code>s to convert.
  * @param writer the TagletWriter that will write this tag.
  * @param alreadyDocumented the set of exceptions that have already been documented.
  * @param allowDups True if we allow duplicate throws tags to be documented.
  * @return the TagletOutput representation of this <code>Tag</code>.
  */
 protected TagletOutput throwsTagsOutput(
     ThrowsTag[] throwTags,
     TagletWriter writer,
     Set<String> alreadyDocumented,
     boolean allowDups) {
   TagletOutput result = writer.getOutputInstance();
   if (throwTags.length > 0) {
     for (int i = 0; i < throwTags.length; ++i) {
       ThrowsTag tt = throwTags[i];
       ClassDoc cd = tt.exception();
       if ((!allowDups)
           && (alreadyDocumented.contains(tt.exceptionName())
               || (cd != null && alreadyDocumented.contains(cd.qualifiedName())))) {
         continue;
       }
       if (alreadyDocumented.size() == 0) {
         result.appendOutput(writer.getThrowsHeader());
       }
       result.appendOutput(writer.throwsTagOutput(tt));
       alreadyDocumented.add(cd != null ? cd.qualifiedName() : tt.exceptionName());
     }
   }
   return result;
 }
Exemple #16
0
 /**
  * Given a class, return the closest visible super class.
  *
  * @param classDoc the class we are searching the parent for.
  * @param configuration the current configuration of the doclet.
  * @return the closest visible super class. Return null if it cannot be found (i.e. classDoc is
  *     java.lang.Object).
  */
 public static Type getFirstVisibleSuperClass(ClassDoc classDoc, Configuration configuration) {
   if (classDoc == null) {
     return null;
   }
   Type sup = classDoc.superclassType();
   ClassDoc supClassDoc = classDoc.superclass();
   while (sup != null && (!(supClassDoc.isPublic() || isLinkable(supClassDoc, configuration)))) {
     if (supClassDoc.superclass().qualifiedName().equals(supClassDoc.qualifiedName())) break;
     sup = supClassDoc.superclassType();
     supClassDoc = supClassDoc.superclass();
   }
   if (classDoc.equals(supClassDoc)) {
     return null;
   }
   return sup;
 }
  public ThrowsTagImpl(String text, ClassDocImpl contextClass, MemberDocImpl contextMember) {
    super(text);

    char[] textarr = text.toCharArray();
    int i = 0;
    for (; i < textarr.length; ++i) {
      if (!Parser.isWhitespace(textarr[i])) break;
    }
    for (; i < textarr.length; ++i) {
      if (Parser.isWhitespace(textarr[i])) {
        this.exceptionName = new String(textarr, 0, i).trim();
        this.exceptionComment = new String(textarr, i, textarr.length - i).trim();
        break;
      }
    }
    if (null != exceptionName) {
      if (contextClass == null) {
        this.exception = Main.getRootDoc().classNamed(exceptionName);
      } else {
        this.exception = contextClass.findClass(exceptionName);
      }
      if (exception != null) this.exceptionName = exception.qualifiedName();
      else {
        if (text.trim().startsWith("<")) {
          Main.getRootDoc()
              .printWarning(
                  "Expected exception name but got '"
                      + text
                      + "' in class "
                      + contextClass.getClassName());
        }
      }
    } else {
      Main.getRootDoc()
          .printWarning(
              "@throws tag in comment for "
                  + contextClass.qualifiedName()
                  + "."
                  + contextMember.name()
                  + " doesn't specify an exception.");
    }
    if (this.exceptionComment != null) {
      setBody(this.exceptionComment, contextClass, contextMember);
    }
  }
 /**
  * Get the header for the class use Listing.
  *
  * @return a content tree representing the class use header
  */
 protected Content getClassUseHeader() {
   String cltype =
       configuration.getText(classdoc.isInterface() ? "doclet.Interface" : "doclet.Class");
   String clname = classdoc.qualifiedName();
   String title = configuration.getText("doclet.Window_ClassUse_Header", cltype, clname);
   Content bodyTree = getBody(true, getWindowTitle(title));
   addTop(bodyTree);
   addNavLinks(true, bodyTree);
   ContentBuilder headContent = new ContentBuilder();
   headContent.addContent(getResource("doclet.ClassUse_Title", cltype));
   headContent.addContent(new HtmlTree(HtmlTag.BR));
   headContent.addContent(clname);
   Content heading =
       HtmlTree.HEADING(HtmlConstants.CLASS_PAGE_HEADING, true, HtmlStyle.title, headContent);
   Content div = HtmlTree.DIV(HtmlStyle.header, heading);
   bodyTree.addContent(div);
   return bodyTree;
 }
 private Map<String, List<ProgramElementDoc>> pkgDivide(
     Map<String, ? extends List<? extends ProgramElementDoc>> classMap) {
   Map<String, List<ProgramElementDoc>> map = new HashMap<>();
   List<? extends ProgramElementDoc> list = classMap.get(classdoc.qualifiedName());
   if (list != null) {
     Collections.sort(list);
     for (ProgramElementDoc doc : list) {
       PackageDoc pkg = doc.containingPackage();
       pkgSet.add(pkg);
       List<ProgramElementDoc> inPkg = map.get(pkg.name());
       if (inPkg == null) {
         inPkg = new ArrayList<>();
         map.put(pkg.name(), inPkg);
       }
       inPkg.add(doc);
     }
   }
   return map;
 }
  protected FieldDefsRes fieldDefs(ClassDoc clazz, String cname, boolean bottomMost)
      throws ClassNotFoundException {
    FieldDefsRes res;
    int offset;
    boolean didTwoWordFields = false;
    ClassDoc superclazz = clazz.superclass();

    if (superclazz != null) {
      String supername = superclazz.qualifiedName();
      res = new FieldDefsRes(clazz, fieldDefs(superclazz, cname, false), bottomMost);
      offset = res.parent.byteSize;
    } else {
      res = new FieldDefsRes(clazz, null, bottomMost);
      offset = 0;
    }

    FieldDoc[] fields = clazz.fields();

    for (int i = 0; i < fields.length; i++) {
      FieldDoc field = fields[i];

      if (doubleAlign && !didTwoWordFields && (offset % 8) == 0) {
        offset = doTwoWordFields(res, clazz, offset, cname, false);
        didTwoWordFields = true;
      }

      String tc = field.type().typeName();
      boolean twoWords = (tc.equals("long") || tc.equals("double"));

      if (!doubleAlign || !twoWords) {
        if (doField(res, field, cname, false)) offset += 4;
      }
    }

    if (doubleAlign && !didTwoWordFields) {
      if ((offset % 8) != 0) offset += 4;
      offset = doTwoWordFields(res, clazz, offset, cname, true);
    }

    res.byteSize = offset;
    return res;
  }
 /**
  * Translate from a a controller class to a Controller model.
  *
  * @param classDoc
  * @return
  */
 public static ControllerModel translateToModel(ClassDoc classDoc) {
   ControllerModel model = new ControllerModel();
   // Setup the basic data
   model.setName(classDoc.name());
   model.setClassDescription(classDoc.getRawCommentText());
   model.setFullClassName(classDoc.qualifiedName());
   // Map the annotations of the class
   Map<String, Object> annotationMap = mapAnnotation(classDoc.annotations());
   // Get the display name and path if they exist
   model.setDisplayName((String) annotationMap.get(CONTROLLER_INFO_DISPLAY_NAME));
   model.setPath((String) annotationMap.get(CONTROLLER_INFO_PATH));
   Iterator<MethodDoc> methodIt = FilterUtils.requestMappingIterator(classDoc.methods());
   List<MethodModel> methods = new LinkedList<MethodModel>();
   model.setMethods(methods);
   while (methodIt.hasNext()) {
     MethodDoc methodDoc = methodIt.next();
     MethodModel methodModel = translateMethod(methodDoc);
     methods.add(methodModel);
   }
   return model;
 }
  protected void structSectionForClass(PrintWriter pw, ClassDoc jclazz, String cname)
      throws ClassNotFoundException {

    String jname = jclazz.qualifiedName();

    if (cname.equals("java_lang_Object")) {
      pw.println("/* struct java_lang_Object is defined in typedefs.h. */");
      pw.println();
      return;
    }
    pw.println("#if !defined(__i386)");
    pw.println("#pragma pack(4)");
    pw.println("#endif");
    pw.println();
    pw.println("struct " + cname + " {");
    pw.println("    ObjHeader h;");
    pw.print(fieldDefs(jclazz, cname));

    if (jname.equals("java.lang.Class"))
      pw.println("    Class *LLNI_mask(cClass);" + "  /* Fake field; don't access (see oobj.h) */");
    pw.println("};" + lineSep + lineSep + "#pragma pack()");
    pw.println();
    return;
  }
  /**
   * Parses the data for a class type definition
   *
   * @param docClass
   * @return
   */
  protected static Class ParseClass(ClassDoc docClass) {
    assert (docClass != null);

    Class xmlClass = new Class();

    // illegal use of this class.
    assert (xmlClass != null);

    xmlClass.name = docClass.name();
    xmlClass.qualifiedName = docClass.qualifiedName();
    xmlClass.isSerializable = docClass.isSerializable();
    xmlClass.isExternalizable = docClass.isExternalizable();
    xmlClass.isAbstract = docClass.isAbstract();
    xmlClass.isException = docClass.isException();
    xmlClass.isError = docClass.isError();
    xmlClass.comment = docClass.commentText();
    xmlClass.scope = DetermineScope(docClass);
    xmlClass.isIncluded = docClass.isIncluded();
    xmlClass.typeVariables =
        ParseTypeVariables(docClass.typeParameters(), docClass.typeParamTags());
    Type superClassType = docClass.superclassType();
    if (superClassType != null) {
      xmlClass.superClass = ParseType(superClassType);
    }

    Type[] interfaces = docClass.interfaceTypes();

    ArrayList<TypeInfo> interfaceTypeNames = new ArrayList<TypeInfo>();
    if (interfaces != null && interfaces.length > 0) {
      for (Type interfaceType : interfaces) {
        interfaceTypeNames.add(ParseType(interfaceType));
      }

      xmlClass.interfaces = interfaceTypeNames.toArray(new TypeInfo[] {});
    }

    ConstructorDoc[] constructors = docClass.constructors();

    if (constructors != null && constructors.length > 0) {
      ArrayList<Constructor> constructorList = new ArrayList<Constructor>();

      for (ConstructorDoc constructor : constructors) {
        constructorList.add(ParseConstructor(constructor));
      }

      xmlClass.constructors = constructorList.toArray(new Constructor[] {});
    } else {
      log.debug("No constructors in class: " + docClass.name());
    }

    MethodDoc[] methods = docClass.methods();

    if (methods != null && methods.length > 0) {
      ArrayList<Method> methodList = new ArrayList<Method>();

      for (MethodDoc method : methods) {
        methodList.add(ParseMethod(method));
      }

      xmlClass.methods = methodList.toArray(new Method[] {});
    } else {
      log.debug("No methods in class: " + docClass.name());
    }

    FieldDoc[] fields = docClass.fields();

    if (fields != null && fields.length > 0) {
      ArrayList<Field> fieldList = new ArrayList<Field>();

      for (FieldDoc field : fields) {
        fieldList.add(ParseField(field));
      }

      xmlClass.fields = fieldList.toArray(new Field[] {});
    }

    xmlClass.annotationInstances =
        ParseAnnotationInstances(docClass.annotations(), docClass.qualifiedName());
    return xmlClass;
  }
 /**
  * Constructor.
  *
  * @param filename the file to be generated.
  * @throws IOException
  * @throws DocletAbortException
  */
 public ClassUseWriter(
     ConfigurationImpl configuration, ClassUseMapper mapper, DocPath filename, ClassDoc classdoc)
     throws IOException {
   super(configuration, filename);
   this.classdoc = classdoc;
   if (mapper.classToPackageAnnotations.containsKey(classdoc.qualifiedName()))
     pkgToPackageAnnotations =
         new TreeSet<>(mapper.classToPackageAnnotations.get(classdoc.qualifiedName()));
   configuration.currentcd = classdoc;
   this.pkgSet = new TreeSet<>();
   this.pkgToClassTypeParameter = pkgDivide(mapper.classToClassTypeParam);
   this.pkgToClassAnnotations = pkgDivide(mapper.classToClassAnnotations);
   this.pkgToMethodTypeParameter = pkgDivide(mapper.classToExecMemberDocTypeParam);
   this.pkgToMethodArgTypeParameter = pkgDivide(mapper.classToExecMemberDocArgTypeParam);
   this.pkgToFieldTypeParameter = pkgDivide(mapper.classToFieldDocTypeParam);
   this.pkgToFieldAnnotations = pkgDivide(mapper.annotationToFieldDoc);
   this.pkgToMethodReturnTypeParameter = pkgDivide(mapper.classToExecMemberDocReturnTypeParam);
   this.pkgToMethodAnnotations = pkgDivide(mapper.classToExecMemberDocAnnotations);
   this.pkgToMethodParameterAnnotations = pkgDivide(mapper.classToExecMemberDocParamAnnotation);
   this.pkgToSubclass = pkgDivide(mapper.classToSubclass);
   this.pkgToSubinterface = pkgDivide(mapper.classToSubinterface);
   this.pkgToImplementingClass = pkgDivide(mapper.classToImplementingClass);
   this.pkgToField = pkgDivide(mapper.classToField);
   this.pkgToMethodReturn = pkgDivide(mapper.classToMethodReturn);
   this.pkgToMethodArgs = pkgDivide(mapper.classToMethodArgs);
   this.pkgToMethodThrows = pkgDivide(mapper.classToMethodThrows);
   this.pkgToConstructorAnnotations = pkgDivide(mapper.classToConstructorAnnotations);
   this.pkgToConstructorParameterAnnotations = pkgDivide(mapper.classToConstructorParamAnnotation);
   this.pkgToConstructorArgs = pkgDivide(mapper.classToConstructorArgs);
   this.pkgToConstructorArgTypeParameter = pkgDivide(mapper.classToConstructorDocArgTypeParam);
   this.pkgToConstructorThrows = pkgDivide(mapper.classToConstructorThrows);
   // tmp test
   if (pkgSet.size() > 0
       && mapper.classToPackage.containsKey(classdoc.qualifiedName())
       && !pkgSet.equals(mapper.classToPackage.get(classdoc.qualifiedName()))) {
     configuration.root.printWarning(
         "Internal error: package sets don't match: "
             + pkgSet
             + " with: "
             + mapper.classToPackage.get(classdoc.qualifiedName()));
   }
   methodSubWriter = new MethodWriterImpl(this);
   constrSubWriter = new ConstructorWriterImpl(this);
   fieldSubWriter = new FieldWriterImpl(this);
   classSubWriter = new NestedClassWriterImpl(this);
   classUseTableSummary =
       configuration.getText("doclet.Use_Table_Summary", configuration.getText("doclet.classes"));
   subclassUseTableSummary =
       configuration.getText(
           "doclet.Use_Table_Summary", configuration.getText("doclet.subclasses"));
   subinterfaceUseTableSummary =
       configuration.getText(
           "doclet.Use_Table_Summary", configuration.getText("doclet.subinterfaces"));
   fieldUseTableSummary =
       configuration.getText("doclet.Use_Table_Summary", configuration.getText("doclet.fields"));
   methodUseTableSummary =
       configuration.getText("doclet.Use_Table_Summary", configuration.getText("doclet.methods"));
   constructorUseTableSummary =
       configuration.getText(
           "doclet.Use_Table_Summary", configuration.getText("doclet.constructors"));
 }
  public boolean run() {
    try {

      // setup additional classes needed for processing, generally these are java ones such as
      // java.lang.String
      // adding them here allows them to be used in @outputType
      Collection<ClassDoc> typeClasses = new ArrayList<ClassDoc>();
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.String.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Integer.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Boolean.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Float.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Double.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Character.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Long.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.lang.Byte.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.Date.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.Calendar.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.Map.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.Collection.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.Set.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.List.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.math.BigInteger.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.math.BigDecimal.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.util.UUID.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.DayOfWeek.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.Duration.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.Instant.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.LocalDate.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.LocalDateTime.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.Month.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.MonthDay.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.OffsetDateTime.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.OffsetTime.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.Period.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.Year.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.YearMonth.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.ZoneId.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.ZoneOffset.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.time.ZonedDateTime.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.net.URI.class.getName()));
      addIfNotNull(typeClasses, this.rootDoc.classNamed(java.net.URL.class.getName()));

      // filter the classes to process
      Collection<ClassDoc> docletClasses = new ArrayList<ClassDoc>();
      for (ClassDoc classDoc : this.rootDoc.classes()) {

        // see if excluded via its FQN
        boolean excludeResource = false;
        if (this.options.getExcludeResourcePrefixes() != null
            && !this.options.getExcludeResourcePrefixes().isEmpty()) {
          for (String prefix : this.options.getExcludeResourcePrefixes()) {
            String className = classDoc.qualifiedName();
            if (className.startsWith(prefix)) {
              excludeResource = true;
              break;
            }
          }
        }

        // see if the inclusion filter is set and if so this resource must match this
        if (!excludeResource
            && this.options.getIncludeResourcePrefixes() != null
            && !this.options.getIncludeResourcePrefixes().isEmpty()) {
          boolean matched = false;
          for (String prefix : this.options.getIncludeResourcePrefixes()) {
            String className = classDoc.qualifiedName();
            if (className.startsWith(prefix)) {
              matched = true;
              break;
            }
          }
          excludeResource = !matched;
        }

        if (excludeResource) {
          continue;
        }

        // see if deprecated
        if (this.options.isExcludeDeprecatedResourceClasses()
            && ParserHelper.isDeprecated(classDoc, this.options)) {
          continue;
        }

        // see if excluded via a tag
        if (ParserHelper.hasTag(classDoc, this.options.getExcludeClassTags())) {
          continue;
        }

        docletClasses.add(classDoc);
      }

      ClassDocCache classCache = new ClassDocCache(docletClasses);

      List<ApiDeclaration> declarations = null;

      // build up set of subresources
      // do simple parsing to find sub resource classes
      // these are ones referenced in the return types of methods
      // which have a path but no http method
      Map<Type, ClassDoc> subResourceClasses = new HashMap<Type, ClassDoc>();
      for (ClassDoc classDoc : docletClasses) {
        ClassDoc currentClassDoc = classDoc;
        while (currentClassDoc != null) {

          for (MethodDoc method : currentClassDoc.methods()) {
            // if the method has @Path but no Http method then its an entry point to a sub resource
            if (!ParserHelper.resolveMethodPath(method, this.options).isEmpty()
                && HttpMethod.fromMethod(method) == null) {
              ClassDoc subResourceClassDoc = classCache.findByType(method.returnType());
              if (subResourceClassDoc != null) {
                if (this.options.isLogDebug()) {
                  System.out.println(
                      "Adding return type as sub resource class : "
                          + subResourceClassDoc.name()
                          + " for method "
                          + method.name()
                          + " of referencing class "
                          + currentClassDoc.name());
                }
                subResourceClasses.put(method.returnType(), subResourceClassDoc);
              }
            }
          }

          currentClassDoc = currentClassDoc.superclass();

          // ignore parent object class
          if (!ParserHelper.hasAncestor(currentClassDoc)) {
            break;
          }
        }
      }

      // parse with the v2 parser that supports endpoints of the same resource being spread across
      // resource files
      Map<String, ApiDeclaration> resourceToDeclaration = new HashMap<String, ApiDeclaration>();
      for (ClassDoc classDoc : docletClasses) {
        CrossClassApiParser classParser =
            new CrossClassApiParser(
                this.options,
                classDoc,
                docletClasses,
                subResourceClasses,
                typeClasses,
                SWAGGER_VERSION,
                this.options.getApiVersion(),
                this.options.getApiBasePath());
        classParser.parse(resourceToDeclaration);
      }
      Collection<ApiDeclaration> declarationColl = resourceToDeclaration.values();

      if (this.options.isLogDebug()) {
        System.out.println("After parse phase api declarations are: ");
        for (ApiDeclaration apiDec : declarationColl) {
          System.out.println(
              "Api Dec: base path "
                  + apiDec.getBasePath()
                  + ", res path: "
                  + apiDec.getResourcePath());
          for (Api api : apiDec.getApis()) {
            System.out.println("Api path:" + api.getPath());
            for (Operation op : api.getOperations()) {
              System.out.println("Api nick name:" + op.getNickname() + " method " + op.getMethod());
            }
          }
        }
      }

      // add any extra declarations
      if (this.options.getExtraApiDeclarations() != null
          && !this.options.getExtraApiDeclarations().isEmpty()) {
        declarationColl = new ArrayList<ApiDeclaration>(declarationColl);
        declarationColl.addAll(this.options.getExtraApiDeclarations());
      }

      // set root path on any empty resources
      for (ApiDeclaration api : declarationColl) {
        if (api.getResourcePath() == null
            || api.getResourcePath().isEmpty()
            || api.getResourcePath().equals("/")) {
          api.setResourcePath(this.options.getResourceRootPath());
        }
      }

      // merge the api declarations
      declarationColl =
          new ApiDeclarationMerger(
                  SWAGGER_VERSION, this.options.getApiVersion(), this.options.getApiBasePath())
              .merge(declarationColl);

      // clear any empty models
      for (ApiDeclaration api : declarationColl) {
        if (api.getModels() != null && api.getModels().isEmpty()) {
          api.setModels(null);
        }
      }

      declarations = new ArrayList<ApiDeclaration>(declarationColl);

      // sort the api declarations if needed
      if (this.options.isSortResourcesByPriority()) {

        Collections.sort(
            declarations,
            new Comparator<ApiDeclaration>() {

              public int compare(ApiDeclaration dec1, ApiDeclaration dec2) {
                return Integer.compare(dec1.getPriority(), dec2.getPriority());
              }
            });

      } else if (this.options.isSortResourcesByPath()) {
        Collections.sort(
            declarations,
            new Comparator<ApiDeclaration>() {

              public int compare(ApiDeclaration dec1, ApiDeclaration dec2) {
                if (dec1 == null || dec1.getResourcePath() == null) {
                  return 1;
                }
                if (dec2 == null || dec2.getResourcePath() == null) {
                  return -1;
                }
                return dec1.getResourcePath().compareTo(dec2.getResourcePath());
              }
            });
      }

      // sort apis of each declaration
      if (this.options.isSortApisByPath()) {
        for (ApiDeclaration dec : declarations) {
          if (dec.getApis() != null) {
            Collections.sort(
                dec.getApis(),
                new Comparator<Api>() {

                  public int compare(Api o1, Api o2) {
                    if (o1 == null || o1.getPath() == null) {
                      return -1;
                    }
                    return o1.getPath().compareTo(o2.getPath());
                  }
                });
          }
        }
      }

      writeApis(declarations);
      // Copy swagger-ui into the output directory.
      if (this.options.isIncludeSwaggerUi()) {
        copyUi();
      }
      return true;
    } catch (IOException e) {
      System.err.println("Failed to write api docs, err msg: " + e.getMessage());
      e.printStackTrace();
      return false;
    }
  }
 @Override
 public DocReferenceable getClassDocRef(ClassDoc classDoc) {
   return new UnknownApiRef(classDoc.qualifiedName());
 }
  /**
   * 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;
  }
  /**
   * The entry point into the Parser class.
   *
   * @param root A RootDoc intstance obtained via the doclet API
   * @return A XML (XStream) serializable element, containing everything parsed from javadoc doclet
   */
  public static Root ParseRoot(RootDoc root) {
    processingStorage = new HashMap<PackageDoc, ParserMediary>();

    try {
      md5 = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
      log.error("unable to acquire MD5 algorithm", e);
      return null;
    }

    rootXml = new Root();

    ClassDoc[] allClasses = root.classes();

    for (ClassDoc classDoc : allClasses) {
      PackageDoc doc = classDoc.containingPackage();

      ParserMediary mediary = null;

      // the age old 'if I have it pull out existing, if I don't make a new one'
      if (processingStorage.containsKey(doc)) {
        mediary = processingStorage.get(doc);
      } else {
        mediary =
            new ParserMediary(
                doc.name(),
                doc.commentText(),
                ParseAnnotationInstances(doc.annotations(), doc.name()));

        processingStorage.put(doc, mediary);
      }

      if (classDoc.isIncluded()) {
        // dev comment--why do enums show up as ordinary class?
        if (classDoc.isOrdinaryClass() || classDoc.isException() || classDoc.isError()) {
          mediary.addClass(ParseClass(classDoc));
        } else if (classDoc.isEnum()) {
          mediary.addEnum(ParseEnum(classDoc));
        } else if (isAnnotation(classDoc)) {
          mediary.addAnnotation(ParseAnnotation(classDoc));
        } else if (classDoc.isInterface()) {
          mediary.addInterface(ParseInterface(classDoc));
        }
      } else {
        log.debug("Skipping not-included class " + classDoc.qualifiedName());
      }
    }

    if (processingStorage.size() > 0) {
      List list = new ArrayList<Package>();

      for (ParserMediary mediary : processingStorage.values()) {
        list.add(mediary.wrapup());
      }

      rootXml.packages = (Package[]) list.toArray(new Package[] {});
    } else {
      log.warn("No packages found!");
    }

    return rootXml;
  }
Exemple #29
0
  /**
   * For a given elment find the root of the tree, start JavaDoc processing for the whole tree and
   * cache all results.
   *
   * @param element any Java element of an tree
   */
  private void cache(IJavaElement element) throws ConQATException {

    IResource rootNode = ResourceTraversalUtils.returnRoot(element);

    Context context = new Context();

    PrintWriter errorWriter =
        new PrintWriter(new Stream2LoggerAdapter(LOGGER, Level.DEBUG, "JavaDoc Error"));
    PrintWriter warningWriter =
        new PrintWriter(new Stream2LoggerAdapter(LOGGER, Level.DEBUG, "JavaDoc Warning"));

    // do not store info messages
    PrintWriter infoWriter = new PrintWriter(new NullOutputStream());

    // This is correct, as the messager attaches itself to the context.
    new SimpleMessager(context, errorWriter, warningWriter, infoWriter);

    JavadocTool tool = JavadocTool.make0(context);

    ModifierFilter showAccess = new ModifierFilter(ModifierFilter.ALL_ACCESS);
    String encoding = determineEncoding(rootNode);
    String docLocale = StringUtils.EMPTY_STRING;
    boolean breakiterator = false;
    ListBuffer<String[]> options = new ListBuffer<String[]>();
    ListBuffer<String> includedElements = addAllChildren(rootNode);
    boolean docClasses = false;
    ListBuffer<String> subPackages = new ListBuffer<String>();
    ListBuffer<String> excludedPackages = new ListBuffer<String>();
    boolean quiet = false;

    try {
      RootDocImpl rootDoc =
          tool.getRootDocImpl(
              docLocale,
              encoding,
              showAccess,
              includedElements.toList(),
              options.toList(),
              breakiterator,
              subPackages.toList(),
              excludedPackages.toList(),
              docClasses,
              false,
              quiet);

      if (rootDoc == null) {
        throw new ConQATException("Could not analyze JavaDoc for " + rootNode);
      }

      Map<String, IJavaResource> classLookup =
          TraversalUtils.createIdToNodeMap((IJavaResource) rootNode);
      ClassDoc[] classes = rootDoc.classes();
      for (ClassDoc doc : classes) {
        IJavaResource tmpElement = classLookup.get(doc.qualifiedName());
        if (tmpElement instanceof IJavaElement) {
          cache.put(((IJavaElement) tmpElement).getUniformPath(), doc);
        }
      }
    } catch (Throwable ex) {
      // The dreaded JavaDoc implementation may throw all kinds of stuff,
      // including Errors. Hence, we catch throwable here. Additionally,
      // we minimally support debugging by extracting a somewhat
      // reasonable message.

      String message = ex.getMessage();
      if (message == null) {
        message = ex.getClass().getName();
        message += StringUtils.obtainStackTrace(ex);
      }

      throw new ConQATException(message, ex);
    } finally {
      errorWriter.close();
      warningWriter.close();
      infoWriter.close();
    }
  }