From 080a465b1321ff93c05ce398cd18a577e0ebae4b Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Sat, 21 Feb 2015 17:12:30 +0100 Subject: Added type constraint relations for Java generics to dot graphs and XML output --- src/arguments.cpp | 2 ++ src/arguments.h | 39 +++++++++++---------- src/classdef.cpp | 85 +++++++++++++++++++++++++++++++++++++++++----- src/classdef.h | 64 ++++++++++++++++++++++++++++++++-- src/defargs.l | 31 ++++++++++++++--- src/dot.cpp | 47 ++++++++++++++++++++++--- src/dot.h | 2 +- src/doxygen.cpp | 1 + src/scanner.l | 1 + src/util.cpp | 11 ++++-- templates/xml/compound.xsd | 1 + 11 files changed, 244 insertions(+), 40 deletions(-) diff --git a/src/arguments.cpp b/src/arguments.cpp index 2acf855..87d0438 100644 --- a/src/arguments.cpp +++ b/src/arguments.cpp @@ -54,6 +54,7 @@ ArgumentList *ArgumentList::unmarshal(StorageIntf *s) a->array = unmarshalQCString(s); a->defval = unmarshalQCString(s); a->docs = unmarshalQCString(s); + a->typeConstraint = unmarshalQCString(s); result->append(a); } result->constSpecifier = unmarshalBool(s); @@ -85,6 +86,7 @@ void ArgumentList::marshal(StorageIntf *s,ArgumentList *argList) marshalQCString(s,a->array); marshalQCString(s,a->defval); marshalQCString(s,a->docs); + marshalQCString(s,a->typeConstraint); } } marshalBool(s,argList->constSpecifier); diff --git a/src/arguments.h b/src/arguments.h index ed09869..14589c0 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -30,37 +30,39 @@ struct Argument /*! Construct a new argument. */ Argument() {} /*! Copy an argument (does a deep copy of all strings). */ - Argument(const Argument &a) - { - attrib=a.attrib.copy(); - type=a.type.copy(); - name=a.name.copy(); - defval=a.defval.copy(); - docs=a.docs.copy(); - array=a.array.copy(); + Argument(const Argument &a) + { + attrib=a.attrib; + type=a.type; + name=a.name; + array=a.array; + defval=a.defval; + docs=a.docs; + typeConstraint=a.typeConstraint; } /*! Assignment of an argument (does a deep copy of all strings). */ Argument &operator=(const Argument &a) { if (this!=&a) { - attrib=a.attrib.copy(); - type=a.type.copy(); - name=a.name.copy(); - defval=a.defval.copy(); - docs=a.docs.copy(); - array=a.array.copy(); + attrib=a.attrib; + type=a.type; + name=a.name; + array=a.array; + defval=a.defval; + docs=a.docs; + typeConstraint=a.typeConstraint; } return *this; } /*! return TRUE if this argument is documentation and the argument has a * non empty name. */ - bool hasDocumentation() const - { - return !name.isEmpty() && !docs.isEmpty(); + bool hasDocumentation() const + { + return !name.isEmpty() && !docs.isEmpty(); } - + QCString attrib; /*!< Argument's attribute (IDL only) */ QCString type; /*!< Argument's type */ QCString canType; /*!< Cached value of canonical type (after type resolution). Empty initially. */ @@ -68,6 +70,7 @@ struct Argument QCString array; /*!< Argument's array specifier (may be empty) */ QCString defval; /*!< Argument's default value (may be empty) */ QCString docs; /*!< Argument's documentation (may be empty) */ + QCString typeConstraint; /*!< Used for Java generics: */ }; /*! \brief This class represents an function or template argument list. diff --git a/src/classdef.cpp b/src/classdef.cpp index 3a103af..486ee3f 100644 --- a/src/classdef.cpp +++ b/src/classdef.cpp @@ -118,6 +118,8 @@ class ClassDefImpl UsesClassDict *usedByImplClassDict; UsesClassDict *usesIntfClassDict; + ConstraintClassDict *constraintClassDict; + /*! Template instances that exists of this class, the key in the * dictionary is the template argument list. */ @@ -216,6 +218,7 @@ void ClassDefImpl::init(const char *defFileName, const char *name, usesImplClassDict=0; usedByImplClassDict=0; usesIntfClassDict=0; + constraintClassDict=0; memberGroupSDict = 0; innerClasses = 0; subGrouping=Config_getBool("SUBGROUPING"); @@ -267,6 +270,7 @@ ClassDefImpl::~ClassDefImpl() delete usesImplClassDict; delete usedByImplClassDict; delete usesIntfClassDict; + delete constraintClassDict; delete incInfo; delete memberGroupSDict; delete innerClasses; @@ -2526,20 +2530,66 @@ bool ClassDef::hasExamples() const return result; } +void ClassDef::addTypeConstraint(const QCString &typeConstraint,const QCString &type) +{ + static bool hideUndocRelation = Config_getBool("HIDE_UNDOC_RELATIONS"); + if (typeConstraint.isEmpty() || type.isEmpty()) return; + ClassDef *cd = getClass(typeConstraint); + if (cd==0 && !hideUndocRelation) + { + cd = new ClassDef(getDefFileName(),getDefLine(),getDefColumn(),typeConstraint,ClassDef::Class); + cd->setUsedOnly(TRUE); + cd->setLanguage(getLanguage()); + Doxygen::hiddenClasses->append(typeConstraint,cd); + //printf("Adding undocumented constraint '%s' to class %s on type %s\n", + // typeConstraint.data(),name().data(),type.data()); + } + if (cd) + { + if (m_impl->constraintClassDict==0) + { + m_impl->constraintClassDict = new ConstraintClassDict(17); + m_impl->constraintClassDict->setAutoDelete(TRUE); + } + ConstraintClassDef *ccd=m_impl->constraintClassDict->find(typeConstraint); + if (ccd==0) + { + ccd = new ConstraintClassDef(cd); + m_impl->constraintClassDict->insert(typeConstraint,ccd); + } + ccd->addAccessor(type); + //printf("Adding constraint '%s' to class %s on type %s\n", + // typeConstraint.data(),name().data(),type.data()); + } +} -void ClassDef::setTemplateArguments(ArgumentList *al) +// Java Type Constrains: A +void ClassDef::addTypeConstraints() { - if (al==0) return; - if (!m_impl->tempArgs) delete m_impl->tempArgs; // delete old list if needed - m_impl->tempArgs=new ArgumentList; - ArgumentListIterator ali(*al); - Argument *a; - for (;(a=ali.current());++ali) + if (m_impl->tempArgs) { - m_impl->tempArgs->append(new Argument(*a)); + ArgumentListIterator ali(*m_impl->tempArgs); + Argument *a; + for (;(a=ali.current());++ali) + { + if (!a->typeConstraint.isEmpty()) + { + QCString typeConstraint; + int i=0,p=0; + while ((i=a->typeConstraint.find('&',p))!=-1) // typeConstraint="A &I" for C + { + typeConstraint = a->typeConstraint.mid(p,i-p).stripWhiteSpace(); + addTypeConstraint(typeConstraint,a->type); + p=i+1; + } + typeConstraint = a->typeConstraint.right(a->typeConstraint.length()-p).stripWhiteSpace(); + addTypeConstraint(typeConstraint,a->type); + } + } } } +// C# Type Constraints: D where T : C, I void ClassDef::setTypeConstraints(ArgumentList *al) { if (al==0) return; @@ -2553,6 +2603,20 @@ void ClassDef::setTypeConstraints(ArgumentList *al) } } +void ClassDef::setTemplateArguments(ArgumentList *al) +{ + if (al==0) return; + if (!m_impl->tempArgs) delete m_impl->tempArgs; // delete old list if needed + //printf("setting template args '%s' for '%s'\n",tempArgListToString(al,getLanguage()).data(),name().data()); + m_impl->tempArgs=new ArgumentList; + ArgumentListIterator ali(*al); + Argument *a; + for (;(a=ali.current());++ali) + { + m_impl->tempArgs->append(new Argument(*a)); + } +} + /*! Returns \c TRUE iff this class or a class inheriting from this class * is \e not defined in an external tag file. */ @@ -4405,6 +4469,11 @@ UsesClassDict *ClassDef::usedInterfaceClasses() const return m_impl->usesIntfClassDict; } +ConstraintClassDict *ClassDef::templateTypeConstraints() const +{ + return m_impl->constraintClassDict; +} + bool ClassDef::isTemplateArgument() const { return m_impl->isTemplArg; diff --git a/src/classdef.h b/src/classdef.h index 0729d20..d98e5de 100644 --- a/src/classdef.h +++ b/src/classdef.h @@ -24,6 +24,7 @@ #include "definition.h" +struct Argument; class MemberDef; class MemberList; class MemberDict; @@ -38,6 +39,7 @@ class MemberDef; class ExampleSDict; class MemberNameInfoSDict; class UsesClassDict; +class ConstraintClassDict; class MemberGroupSDict; class QTextStream; class PackageDef; @@ -229,6 +231,8 @@ class ClassDef : public Definition UsesClassDict *usedInterfaceClasses() const; + ConstraintClassDict *templateTypeConstraints() const; + bool isTemplateArgument() const; /** Returns the definition of a nested compound if @@ -371,6 +375,7 @@ class ClassDef : public Definition void findSectionsInDocumentation(); void addMembersToMemberGroup(); void addListReferences(); + void addTypeConstraints(); void computeAnchors(); void mergeMembers(); void sortMemberLists(); @@ -441,12 +446,13 @@ class ClassDef : public Definition void getTitleForMemberListType(MemberListType type, QCString &title,QCString &subtitle); QCString includeStatement() const; + void addTypeConstraint(const QCString &typeConstraint,const QCString &type); - ClassDefImpl *m_impl; - }; +//------------------------------------------------------------------------ + /** Class that contains information about a usage relation. */ struct UsesClassDef @@ -500,6 +506,8 @@ class UsesClassDictIterator : public QDictIterator ~UsesClassDictIterator() {} }; +//------------------------------------------------------------------------ + /** Class that contains information about an inheritance relation. */ struct BaseClassDef @@ -558,4 +566,56 @@ class BaseClassListIterator : public QListIterator QListIterator(bcl) {} }; +//------------------------------------------------------------------------ + + +/** Class that contains information about a type constraint relations. + */ +struct ConstraintClassDef +{ + ConstraintClassDef(ClassDef *cd) : classDef(cd) + { + accessors = new QDict(17); + } + ~ConstraintClassDef() + { + delete accessors; + } + void addAccessor(const char *s) + { + if (accessors->find(s)==0) + { + accessors->insert(s,(void *)666); + } + } + /** Class definition that this relation uses. */ + ClassDef *classDef; + + /** Dictionary of member types names that form the edge labels of the + * constraint relation. + */ + QDict *accessors; +}; + +/** Dictionary of constraint relations. + */ +class ConstraintClassDict : public QDict +{ + public: + ConstraintClassDict(int size) : QDict(size) {} + ~ConstraintClassDict() {} +}; + +/** Iterator class to iterate over a dictionary of constraint relations. + */ +class ConstraintClassDictIterator : public QDictIterator +{ + public: + ConstraintClassDictIterator(const QDict &d) + : QDictIterator(d) {} + ~ConstraintClassDictIterator() {} +}; + +//------------------------------------------------------------------------ + #endif diff --git a/src/defargs.l b/src/defargs.l index 164c100..70234b2 100644 --- a/src/defargs.l +++ b/src/defargs.l @@ -73,6 +73,7 @@ static QCString g_curArgName; static QCString g_curArgDocs; static QCString g_curArgAttrib; static QCString g_curArgArray; +static QCString g_curTypeConstraint; static QCString g_extraTypeChars; static int g_argRoundCount; static int g_argSharpCount; @@ -80,6 +81,7 @@ static int g_argCurlyCount; static int g_readArgContext; static int g_lastDocContext; static int g_lastDocChar; +static int g_lastExtendsContext; static QCString g_delimiter; /* ----------------------------------------------------------------- @@ -120,6 +122,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" %x FuncQual %x ReadDocBlock %x ReadDocLine +%x ReadTypeConstraint %x TrailingReturn @@ -332,8 +335,9 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" int i=l-1; while (i>=0 && (isspace((uchar)g_curArgTypeName.at(i)) || g_curArgTypeName.at(i)=='.')) i--; while (i>=0 && (isId(g_curArgTypeName.at(i)) || g_curArgTypeName.at(i)=='$')) i--; - Argument *a = new Argument; - a->attrib = g_curArgAttrib.copy(); + Argument *a = new Argument; + a->attrib = g_curArgAttrib.copy(); + a->typeConstraint = g_curTypeConstraint.stripWhiteSpace(); //printf("a->type=%s a->name=%s i=%d l=%d\n", // a->type.data(),a->name.data(),i,l); a->array.resize(0); @@ -413,6 +417,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" g_curArgDefValue.resize(0); g_curArgArray.resize(0); g_curArgDocs.resize(0); + g_curTypeConstraint.resize(0); if (*yytext==')') { BEGIN(FuncQual); @@ -424,6 +429,11 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" } } } +"extends" { + g_curTypeConstraint.resize(0); + g_lastExtendsContext=YY_START; + BEGIN(ReadTypeConstraint); + } "$"?{ID} { QCString name=yytext; //resolveDefines(yytext); if (YY_START==ReadFuncArgType && g_curArgArray=="[]") // Java style array @@ -451,13 +461,23 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" . { *g_copyArgValue += *yytext; } -"const" { +[,)>] { + unput(*yytext); + BEGIN(g_lastExtendsContext); + } +. { + g_curTypeConstraint+=yytext; + } +\n { + g_curTypeConstraint+=' '; + } +"const" { g_argList->constSpecifier=TRUE; } -"volatile" { +"volatile" { g_argList->volatileSpecifier=TRUE; } -"="{B}*"0" { +"="{B}*"0" { g_argList->pureSpecifier=TRUE; BEGIN(FuncQual); } @@ -534,6 +554,7 @@ void stringToArgumentList(const char *argsString,ArgumentList* al,QCString *extr g_curArgDocs.resize(0); g_curArgAttrib.resize(0); g_curArgArray.resize(0); + g_curTypeConstraint.resize(0); g_extraTypeChars.resize(0); g_argRoundCount = 0; g_argSharpCount = 0; diff --git a/src/dot.cpp b/src/dot.cpp index 1182e8d..cc81c44 100644 --- a/src/dot.cpp +++ b/src/dot.cpp @@ -164,7 +164,8 @@ static const char *normalEdgeColorMap[] = "firebrick4", // Private "darkorchid3", // "use" relation "grey75", // Undocumented - "orange" // template relation + "orange", // template relation + "orange" // type constraint }; static const char *normalArrowStyleMap[] = @@ -190,7 +191,8 @@ static const char *umlEdgeColorMap[] = "firebrick4", // Private "grey25", // "use" relation "grey75", // Undocumented - "orange" // template relation + "orange", // template relation + "orange" // type constraint }; static const char *umlArrowStyleMap[] = @@ -2003,6 +2005,7 @@ void DotNode::writeXML(FTextStream &t,bool isClassGraph) case EdgeInfo::Red: t << "private-inheritance"; break; case EdgeInfo::Purple: t << "usage"; break; case EdgeInfo::Orange: t << "template-instance"; break; + case EdgeInfo::Orange2: t << "type-constraint"; break; case EdgeInfo::Grey: ASSERT(0); break; } } @@ -2071,6 +2074,7 @@ void DotNode::writeDocbook(FTextStream &t,bool isClassGraph) case EdgeInfo::Red: t << "private-inheritance"; break; case EdgeInfo::Purple: t << "usage"; break; case EdgeInfo::Orange: t << "template-instance"; break; + case EdgeInfo::Orange2: t << "type-constraint"; break; case EdgeInfo::Grey: ASSERT(0); break; } } @@ -2148,6 +2152,7 @@ void DotNode::writeDEF(FTextStream &t) case EdgeInfo::Red: t << "private-inheritance"; break; case EdgeInfo::Purple: t << "usage"; break; case EdgeInfo::Orange: t << "template-instance"; break; + case EdgeInfo::Orange2: t << "type-constraint"; break; case EdgeInfo::Grey: ASSERT(0); break; } t << ';' << endl; @@ -2591,7 +2596,7 @@ void DotClassGraph::addClass(ClassDef *cd,DotNode *n,int prot, { if (Config_getBool("HIDE_UNDOC_CLASSES") && !cd->isLinkable()) return; - int edgeStyle = (label || prot==EdgeInfo::Orange) ? EdgeInfo::Dashed : EdgeInfo::Solid; + int edgeStyle = (label || prot==EdgeInfo::Orange || prot==EdgeInfo::Orange2) ? EdgeInfo::Dashed : EdgeInfo::Solid; QCString className; if (usedName) // name is a typedef { @@ -2797,6 +2802,7 @@ bool DotClassGraph::determineVisibleNodes(DotNode *rootNode, void DotClassGraph::buildGraph(ClassDef *cd,DotNode *n,bool base,int distance) { + static bool templateRelations = Config_getBool("TEMPLATE_RELATIONS"); //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n", // cd->name().data(),distance,base); // ---- Add inheritance relations @@ -2856,10 +2862,43 @@ void DotClassGraph::buildGraph(ClassDef *cd,DotNode *n,bool base,int distance) } } } + if (templateRelations && base) + { + ConstraintClassDict *dict = cd->templateTypeConstraints(); + if (dict) + { + ConstraintClassDictIterator ccdi(*dict); + ConstraintClassDef *ccd; + for (;(ccd=ccdi.current());++ccdi) + { + QCString label; + QDictIterator dvi(*ccd->accessors); + const char *s; + bool first=TRUE; + int count=0; + int maxLabels=10; + for (;(s=dvi.currentKey()) && countclassDef->name().data(),ucd->templSpecifiers.data()); + addClass(ccd->classDef,n,EdgeInfo::Orange2,label,0, + 0,TRUE,distance); + } + } + } // ---- Add template instantiation relations - static bool templateRelations = Config_getBool("TEMPLATE_RELATIONS"); if (templateRelations) { if (base) // template relations for base classes diff --git a/src/dot.h b/src/dot.h index 41a416e..df46aaf 100644 --- a/src/dot.h +++ b/src/dot.h @@ -45,7 +45,7 @@ enum EmbeddedOutputFormat { EOF_Html, EOF_LaTeX, EOF_Rtf, EOF_DocBook }; /** Attributes of an edge of a dot graph */ struct EdgeInfo { - enum Colors { Blue=0, Green=1, Red=2, Purple=3, Grey=4, Orange=5 }; + enum Colors { Blue=0, Green=1, Red=2, Purple=3, Grey=4, Orange=5, Orange2=6 }; enum Styles { Solid=0, Dashed=1 }; EdgeInfo() : m_color(0), m_style(0), m_labColor(0) {} ~EdgeInfo() {} diff --git a/src/doxygen.cpp b/src/doxygen.cpp index 54187a1..667227f 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -5018,6 +5018,7 @@ static void findUsedTemplateInstances() { rootNav->loadEntry(g_storage); findUsedClassesForClass(rootNav,cd,cd,cd,TRUE); + cd->addTypeConstraints(); rootNav->releaseEntry(); } } diff --git a/src/scanner.l b/src/scanner.l index 01d1677..21698ab 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -3592,6 +3592,7 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP}) sharpCount++; } {BN}+ { + current->type += ' '; lineCount(); } . { current->type += *yytext ; } diff --git a/src/util.cpp b/src/util.cpp index 83f65a4..ac0c1d6 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1741,9 +1741,11 @@ nextChar: } else if (i>0 && ( - (s.at(i-1)==')' && isId(c)) + (s.at(i-1)==')' && isId(c)) // ")id" -> ") id" || - (c=='\'' && s.at(i-1)==' ') + (c=='\'' && s.at(i-1)==' ') // "'id" -> "' id" + || + (i>1 && s.at(i-2)==' ' && s.at(i-1)==' ') // " id" -> " id" ) ) { @@ -2229,6 +2231,11 @@ QCString tempArgListToString(ArgumentList *al,SrcLangExt lang) result+=a->type; } } + if (!a->typeConstraint.isEmpty() && lang==SrcLangExt_Java) + { + result+=" extends "; // TODO: now Java specific, C# has where... + result+=a->typeConstraint; + } ++ali; a=ali.current(); if (a) result+=", "; diff --git a/templates/xml/compound.xsd b/templates/xml/compound.xsd index 50e532e..ff516e3 100644 --- a/templates/xml/compound.xsd +++ b/templates/xml/compound.xsd @@ -649,6 +649,7 @@ + -- cgit v0.12