# An example showing how to use the python doxmlparser module to extract some metrics from # the XML output generated by doxygen for a project. import sys import doxmlparser from doxmlparser.compound import DoxCompoundKind, DoxMemberKind, DoxSectionKind, MixedContainer class Metrics: def __init__(self): self.numClasses = 0 self.numDocClasses = 0 self.numStructs = 0 self.numUnions = 0 self.numInterfaces = 0 self.numExceptions = 0 self.numNamespaces = 0 self.numFiles = 0 self.numDocFiles = 0 self.numGroups = 0 self.numPages = 0 self.numPubMethods = 0 self.numDocPubMethods = 0 self.numProMethods = 0 self.numDocProMethods = 0 self.numPriMethods = 0 self.numDocPriMethods = 0 self.numAttributes = 0 self.numDocAttributes = 0 self.numFunctions = 0 self.numDocFunctions = 0 self.numVariables = 0 self.numDocVariables = 0 self.numParams = 0 def print(self): numMethods = self.numPubMethods + self.numProMethods + self.numPriMethods numDocMethods = self.numDocPubMethods + self.numDocProMethods + self.numDocPriMethods print("Metrics:"); print("-----------------------------------"); if self.numClasses>0: print("Classes: {:=10} ({} documented)".format(self.numClasses,self.numDocClasses)) if self.numStructs>0: print("Structs: {:=10}".format(self.numStructs)) if self.numUnions>0: print("Unions: {:=10}".format(self.numUnions)) if self.numExceptions>0: print("Exceptions: {:=10}".format(self.numExceptions)) if self.numNamespaces>0: print("Namespaces: {:=10}".format(self.numNamespaces)) if self.numFiles>0: print("Files: {:=10} ({} documented)".format(self.numFiles,self.numDocFiles)) if self.numGroups>0: print("Groups: {:=10}".format(self.numGroups)) if self.numPages>0: print("Pages: {:=10}".format(self.numPages)) if numMethods>0: print("Methods: {:=10} ({} documented)".format(numMethods,numDocMethods)) if self.numPubMethods>0: print(" Public: {:=10} ({} documented)".format(self.numPubMethods,self.numDocPubMethods)) if self.numProMethods>0: print(" Protected: {:=10} ({} documented)".format(self.numProMethods,self.numDocProMethods)) if self.numPriMethods>0: print(" Private: {:=10} ({} documented)".format(self.numPriMethods,self.numDocPriMethods)) if self.numFunctions>0: print("Functions: {:=10} ({} documented)".format(self.numFunctions,self.numDocFunctions)) if self.numAttributes>0: print("Attributes: {:=10} ({} documented)".format(self.numAttributes,self.numDocAttributes)) if self.numVariables>0: print("Variables: {:=10} ({} documented)".format(self.numVariables,self.numDocVariables)) if self.numParams>0: print("Params: {:=10}".format(self.numParams)) print("-----------------------------------"); if self.numClasses>0: print("Avg. #methods/compound: {:=10}".format(float(numMethods)/float(self.numClasses))) if numMethods>0: print("Avg. #params/method: {:=10}".format(float(self.numParams)/float(numMethods))) print("-----------------------------------"); def description_is_empty(description): for content in description.content_: if content.getCategory()==MixedContainer.CategoryText: if not content.getValue().isspace(): return False # non space-only text elif not content.getCategory()==MixedContainer.CategoryNone: return False # some internal object like a paragraph return True def is_documented(definition): return not description_is_empty(definition.get_briefdescription()) or \ not description_is_empty(definition.get_detaileddescription()) def section_is_protected(sectionkind): return sectionkind in [DoxSectionKind.PROTECTEDTYPE, DoxSectionKind.PROTECTEDFUNC, DoxSectionKind.PROTECTEDATTRIB, DoxSectionKind.PROTECTEDSLOT, DoxSectionKind.PROTECTEDSTATICFUNC, DoxSectionKind.PROTECTEDSTATICATTRIB] def section_is_private(sectionkind): return sectionkind in [DoxSectionKind.PRIVATETYPE, DoxSectionKind.PRIVATEFUNC, DoxSectionKind.PRIVATEATTRIB, DoxSectionKind.PRIVATESLOT, DoxSectionKind.PRIVATESTATICFUNC, DoxSectionKind.PRIVATESTATICATTRIB] def section_is_public(sectionkind): return not section_is_protected(sectionkind) and not section_is_private(sectionkind) def linked_text_to_string(linkedtext): str='' if linkedtext: for text_or_ref in linkedtext.content_: if text_or_ref.getCategory()==MixedContainer.CategoryText: str+=text_or_ref.getValue() else: str+=text_or_ref.getValue().get_valueOf_() return str def parse_members(compounddef,sectiondef,metrics): functionLikeKind = [DoxMemberKind.FUNCTION, DoxMemberKind.PROTOTYPE, DoxMemberKind.SIGNAL, DoxMemberKind.SLOT, DoxMemberKind.DCOP] variableLikeKind = [DoxMemberKind.VARIABLE, DoxMemberKind.PROPERTY] for memberdef in sectiondef.get_memberdef(): if compounddef.get_kind() in [DoxCompoundKind.CLASS, DoxCompoundKind.STRUCT, DoxCompoundKind.INTERFACE]: if memberdef.get_kind() in functionLikeKind: if section_is_public(sectiondef.get_kind()): metrics.numPubMethods+=1 if is_documented(memberdef): metrics.numDocPubMethods+=1 elif section_is_protected(sectiondef.get_kind()): metrics.numProMethods+=1 if is_documented(memberdef): metrics.numDocProMethods+=1 elif section_is_private(sectiondef.get_kind()): metrics.numPriMethods+=1 if is_documented(memberdef): metrics.numDocPriMethods+=1 elif memberdef.get_kind() in variableLikeKind: metrics.numAttributes+=1 if is_documented(memberdef): metrics.numDocAttributes+=1 elif compounddef.get_kind() in [DoxCompoundKind.FILE, DoxCompoundKind.NAMESPACE]: if memberdef.get_kind() in functionLikeKind: metrics.numFunctions+=1 if is_documented(memberdef): metrics.numDocFunctions+=1 elif memberdef.get_kind() in variableLikeKind: metrics.numVariables+=1 if is_documented(memberdef): metrics.numDocVariables+=1 #for param in memberdef.get_param(): # name = '' # if param.get_defname(): # name = param.get_defname() # if param.get_declname(): # name = param.get_declname() # print("param '{}':'{}'".format(linked_text_to_string(param.get_type()),name)) metrics.numParams+=len(memberdef.get_param()) if memberdef.get_type() and memberdef.get_type()!="void" and linked_text_to_string(memberdef.get_type()): metrics.numParams+=1 # count non-void return types as well #print("returns '{}'".format(linked_text_to_string(memberdef.get_type()))) def parse_sections(compounddef,metrics): for sectiondef in compounddef.get_sectiondef(): parse_members(compounddef,sectiondef,metrics) def parse_compound(inDirName,baseName,metrics): rootObj = doxmlparser.compound.parse(inDirName+"/"+baseName+".xml",True) for compounddef in rootObj.get_compounddef(): kind = compounddef.get_kind() if kind==DoxCompoundKind.CLASS: metrics.numClasses+=1 if is_documented(compounddef): metrics.numDocClasses+=1 elif kind==DoxCompoundKind.STRUCT: metrics.numStructs+=1 elif kind==DoxCompoundKind.UNION: metrics.numUnions+=1 elif kind==DoxCompoundKind.INTERFACE: metrics.numInterfaces+=1 elif kind==DoxCompoundKind.EXCEPTION: metrics.numExceptions+=1 elif kind==DoxCompoundKind.NAMESPACE: metrics.numNamespaces+=1 elif kind==DoxCompoundKind.FILE: metrics.numFiles+=1 if is_documented(compounddef): metrics.numDocFiles+=1 elif kind==DoxCompoundKind.GROUP: metrics.numGroups+=1 elif kind==DoxCompoundKind.PAGE: metrics.numPages+=1 else: continue parse_sections(compounddef,metrics) def parse_index(inDirName): metrics = Metrics() rootObj = doxmlparser.index.parse(inDirName+"/index.xml",True) for compound in rootObj.get_compound(): # for each compound defined in the index print("Processing {0}...".format(compound.get_name())) parse_compound(inDirName,compound.get_refid(),metrics) metrics.print() def usage(): print("Usage {0} ".format(sys.argv[0])) sys.exit(1) def main(): args = sys.argv[1:] if len(args)==1: parse_index(args[0]) else: usage() if __name__ == '__main__': main()