226 lines
9.5 KiB
Python
226 lines
9.5 KiB
Python
# An example showing how to use the python doxmlparser module to extract some metrics from
|
|
# the XML output generated by analyze 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} <xml_output_dir>".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()
|
|
|