summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authordimitri <dimitri@afe2bf4a-e733-0410-8a33-86f594647bc7>2004-10-10 19:13:27 (GMT)
committerdimitri <dimitri@afe2bf4a-e733-0410-8a33-86f594647bc7>2004-10-10 19:13:27 (GMT)
commit161d3860fa7f0e80edbdb2b3105e9759cada1dbb (patch)
treea1de4b4e9cdb500ec50176a1d5a70efafc4eb88f /src
parentdeb9b8d40d7ac7d0c576ac9401aaed9e21ccdc58 (diff)
downloadDoxygen-161d3860fa7f0e80edbdb2b3105e9759cada1dbb.zip
Doxygen-161d3860fa7f0e80edbdb2b3105e9759cada1dbb.tar.gz
Doxygen-161d3860fa7f0e80edbdb2b3105e9759cada1dbb.tar.bz2
Release-1.3.9.1
Diffstat (limited to 'src')
-rw-r--r--src/code.l38
-rw-r--r--src/definition.cpp4
-rw-r--r--src/doxygen.cpp91
-rw-r--r--src/htmlgen.cpp4
-rw-r--r--src/namespacedef.cpp23
-rw-r--r--src/namespacedef.h3
-rw-r--r--src/pre.l7
-rw-r--r--src/tagreader.cpp81
-rw-r--r--src/util.cpp45
9 files changed, 192 insertions, 104 deletions
diff --git a/src/code.l b/src/code.l
index fd47652..cefd8bb 100644
--- a/src/code.l
+++ b/src/code.l
@@ -843,18 +843,18 @@ static void generateClassOrGlobalLink(BaseCodeDocInterface &ol,char *clName,
}
}
-static bool getLink(const char *className,
- const char *memberName,
- BaseCodeDocInterface &ol,
- const char *text=0)
+static bool getLinkInScope(const QCString &c, // scope
+ const QCString &m, // member
+ const char *memberText, // exact text
+ BaseCodeDocInterface &ol,
+ const char *text
+ )
{
MemberDef *md;
ClassDef *cd;
FileDef *fd;
NamespaceDef *nd;
GroupDef *gd;
- QCString m=removeRedundantWhiteSpace(memberName);
- QCString c=className;
//printf("Trying `%s'::`%s'\n",c.data(),m.data());
if (getDefs(c,m,"()",md,cd,fd,nd,gd,FALSE,g_sourceFileDef) &&
md->isLinkable())
@@ -894,14 +894,34 @@ static bool getLink(const char *className,
writeMultiLineCodeLink(ol,md->getReference(),
md->getOutputFileBase(),
md->anchor(),
- text ? text : memberName);
- addToSearchIndex(text ? text : memberName);
+ text ? text : memberText);
+ addToSearchIndex(text ? text : memberText);
return TRUE;
}
}
return FALSE;
}
+static bool getLink(const char *className,
+ const char *memberName,
+ BaseCodeDocInterface &ol,
+ const char *text=0)
+{
+ QCString m=removeRedundantWhiteSpace(memberName);
+ QCString c=className;
+ if (!getLinkInScope(c,m,memberName,ol,text))
+ {
+ if (!g_curClassName.isEmpty())
+ {
+ if (!c.isEmpty()) c.prepend("::");
+ c.prepend(g_curClassName);
+ return getLinkInScope(c,m,memberName,ol,text);
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
static bool generateClassMemberLink(BaseCodeDocInterface &ol,ClassDef *mcd,const char *memName)
{
if (mcd)
@@ -1761,7 +1781,7 @@ CHARLIT (("'"\\[0-7]{1,3}"'")|("'"\\."'")|("'"[^' \\\n]{1,4}"'"))
BEGIN( Body );
}
<ClassName,ClassVar>[*&]+ {
- g_type=g_curClassName;
+ g_type=g_curClassName.copy();
g_name.resize(0);
g_code->codify(yytext);
BEGIN( Body ); // variable of type struct *
diff --git a/src/definition.cpp b/src/definition.cpp
index 57d585e..4470bf2 100644
--- a/src/definition.cpp
+++ b/src/definition.cpp
@@ -164,7 +164,9 @@ void Definition::writeDocAnchorsToTagFile()
{
//printf("write an entry!\n");
if (definitionType()==TypeMember) Doxygen::tagFile << " ";
- Doxygen::tagFile << " <docanchor>" << si->label << "</docanchor>" << endl;
+ Doxygen::tagFile << " <docanchor file=\""
+ << si->fileName << "\">" << si->label
+ << "</docanchor>" << endl;
}
}
}
diff --git a/src/doxygen.cpp b/src/doxygen.cpp
index 00d4dde..57ad6a8 100644
--- a/src/doxygen.cpp
+++ b/src/doxygen.cpp
@@ -1021,11 +1021,13 @@ static void buildNamespaceList(Entry *root)
else // fresh namespace
{
QCString tagName;
+ QCString tagFileName;
if (root->tagInfo)
{
tagName=root->tagInfo->tagName;
+ tagFileName=root->tagInfo->fileName;
}
- NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,fullName,tagName);
+ NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,fullName,tagName,tagFileName);
nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->addSectionsToDefinition(root->anchors);
@@ -1143,8 +1145,7 @@ static void findUsingDirectives(Entry *root)
}
else // unknown namespace, but add it anyway.
{
- NamespaceDef *nd=new NamespaceDef(
- root->fileName,root->startLine,name);
+ NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,name);
nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->addSectionsToDefinition(root->anchors);
@@ -4241,6 +4242,49 @@ static bool findGlobalMember(Entry *root,
return TRUE;
}
+static QCString substituteTemplatesInString(
+ const QList<ArgumentList> &srcTempArgLists,
+ const QList<ArgumentList> &dstTempArgLists,
+ const QCString &src
+ )
+{
+ QCString dst;
+ QRegExp re(idMask);
+ //printf("type=%s\n",sa->type.data());
+
+ int i,p=0,l;
+ while ((i=re.match(src,p,&l))!=-1) // for each word in srcType
+ {
+ bool found=FALSE;
+ dst+=src.mid(p,i-p);
+ QCString name=src.mid(i,l);
+
+ QListIterator<ArgumentList> srclali(srcTempArgLists);
+ QListIterator<ArgumentList> dstlali(dstTempArgLists);
+ for (;srclali.current() && !found;++srclali,++dstlali)
+ {
+ ArgumentListIterator tsali(*srclali.current());
+ ArgumentListIterator tdali(*dstlali.current());
+ Argument *tsa =0,*tda=0;
+
+ for (tsali.toFirst();(tsa=tsali.current()) && !found;++tsali)
+ {
+ tda = tdali.current();
+ if (tda && name==tsa->name)
+ {
+ name=tda->name; // substitute
+ found=TRUE;
+ }
+ if (tda) ++tdali;
+ }
+ }
+ dst+=name;
+ p=i+l;
+ }
+ dst+=src.right(src.length()-p);
+ return dst;
+}
+
static void substituteTemplatesInArgList(
const QList<ArgumentList> &srcTempArgLists,
const QList<ArgumentList> &dstTempArgLists,
@@ -4253,51 +4297,22 @@ static void substituteTemplatesInArgList(
for (sali.toFirst();(sa=sali.current());++sali) // for each member argument
{
- QCString srcType = sa->type;
- QRegExp re(idMask);
- //printf("type=%s\n",sa->type.data());
-
- int i,p=0,l;
- QCString dstType;
- while ((i=re.match(srcType,p,&l))!=-1) // for each word in srcType
- {
- bool found=FALSE;
- dstType+=srcType.mid(p,i-p);
- QCString name=srcType.mid(i,l);
-
- QListIterator<ArgumentList> srclali(srcTempArgLists);
- QListIterator<ArgumentList> dstlali(dstTempArgLists);
- for (;srclali.current() && !found;++srclali,++dstlali)
- {
- ArgumentListIterator tsali(*srclali.current());
- ArgumentListIterator tdali(*dstlali.current());
- Argument *tsa =0,*tda=0;
-
- for (tsali.toFirst();(tsa=tsali.current()) && !found;++tsali)
- {
- tda = tdali.current();
- if (tda && name==tsa->name)
- {
- name=tda->name; // substitute
- found=TRUE;
- }
- if (tda) ++tdali;
- }
- }
- dstType+=name;
- p=i+l;
- }
- dstType+=srcType.right(srcType.length()-p);
+ QCString dstType = substituteTemplatesInString(
+ srcTempArgLists,dstTempArgLists,sa->type);
+ QCString dstArray = substituteTemplatesInString(
+ srcTempArgLists,dstTempArgLists,sa->array);
if (da==0)
{
da=new Argument(*sa);
dst->append(da);
da->type=dstType;
+ da->array=dstArray;
da=0;
}
else
{
da->type=dstType;
+ da->type=dstArray;
da=dst->next();
}
}
diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp
index 87b168d..8223562 100644
--- a/src/htmlgen.cpp
+++ b/src/htmlgen.cpp
@@ -1145,7 +1145,7 @@ void HtmlGenerator::startParameterType(bool first,const char *key)
t << " <td class=\"md\" nowrap align=\"right\">";
if (key) t << key;
t << "</td>" << endl;
- t << " <td></td>" << endl;
+ t << " <td class=\"md\"></td>" << endl;
t << " <td class=\"md\" nowrap>";
}
}
@@ -1185,7 +1185,7 @@ void HtmlGenerator::endParameterName(bool last,bool emptyList,bool closeBracket)
t << "</td>" << endl;
t << " </tr>" << endl;
t << " <tr>" << endl;
- t << " <td></td>" << endl;
+ t << " <td class=\"md\"></td>" << endl;
t << " <td class=\"md\">";
if (closeBracket) t << ")";
t << "&nbsp;</td>" << endl;
diff --git a/src/namespacedef.cpp b/src/namespacedef.cpp
index ee4c48b..77779be 100644
--- a/src/namespacedef.cpp
+++ b/src/namespacedef.cpp
@@ -29,11 +29,19 @@
#include "searchindex.h"
NamespaceDef::NamespaceDef(const char *df,int dl,
- const char *name,const char *lref) :
+ const char *name,const char *lref,
+ const char *fName) :
Definition(df,dl,name)
{
- fileName="namespace";
- fileName+=name;
+ if (fName)
+ {
+ fileName = stripExtension(fName);
+ }
+ else
+ {
+ fileName="namespace";
+ fileName+=name;
+ }
classSDict = new ClassSDict(17);
namespaceSDict = new NamespaceSDict(17);
m_innerCompounds = new SDict<Definition>(257);
@@ -414,7 +422,14 @@ void NamespaceDef::addUsingDeclaration(Definition *d)
QCString NamespaceDef::getOutputFileBase() const
{
- return convertNameToFile(fileName);
+ if (isReference())
+ {
+ return fileName;
+ }
+ else
+ {
+ return convertNameToFile(fileName);
+ }
}
Definition *NamespaceDef::findInnerCompound(const char *n)
diff --git a/src/namespacedef.h b/src/namespacedef.h
index 51d30d2..4432539 100644
--- a/src/namespacedef.h
+++ b/src/namespacedef.h
@@ -38,7 +38,8 @@ class NamespaceDef : public Definition
{
public:
NamespaceDef(const char *defFileName,int defLine,
- const char *name,const char *ref=0);
+ const char *name,const char *ref=0,
+ const char *refFile=0);
~NamespaceDef();
DefType definitionType() { return TypeNamespace; }
QCString getOutputFileBase() const;
diff --git a/src/pre.l b/src/pre.l
index a01b06f..8266c1c 100644
--- a/src/pre.l
+++ b/src/pre.l
@@ -856,9 +856,15 @@ QCString removeIdsAndMarkers(const char *s)
}
p++;
}
+ else // oops, not comment but division
+ {
+ result+=pc;
+ goto nextChar;
+ }
}
else
{
+nextChar:
result+=c;
char lc=tolower(c);
if (!isId(lc) && lc!='.' && lc!='-' && lc!='+') inNum=FALSE;
@@ -1507,6 +1513,7 @@ CHARLIT (("'"\\[0-7]{1,3}"'")|("'"\\."'")|("'"[^'\\\n]{1,4}"'"))
<Guard>"defined"/{B}+ {
BEGIN(DefinedExpr1);
}
+<Guard>{ID} { g_guardExpr+=yytext; }
<Guard>. { g_guardExpr+=*yytext; }
<Guard>\n {
outputChar('\n');
diff --git a/src/tagreader.cpp b/src/tagreader.cpp
index b629373..1b971a2 100644
--- a/src/tagreader.cpp
+++ b/src/tagreader.cpp
@@ -35,6 +35,22 @@
#include "defargs.h"
//#include "reflist.h"
+/*! Information about an linkable anchor */
+class TagAnchorInfo
+{
+ public:
+ TagAnchorInfo(const QString &f,const QString &l) : label(l), fileName(f) {}
+ QString label;
+ QString fileName;
+};
+
+class TagAnchorInfoList : public QList<TagAnchorInfo>
+{
+ public:
+ TagAnchorInfoList() : QList<TagAnchorInfo>() { setAutoDelete(TRUE); }
+ virtual ~TagAnchorInfoList() {}
+};
+
/*! Container for member specific info that can be read from a tagfile */
class TagMemberInfo
{
@@ -45,7 +61,7 @@ class TagMemberInfo
QString anchor;
QString arglist;
QString kind;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
Protection prot;
Specifier virt;
bool isStatic;
@@ -60,7 +76,7 @@ class TagClassInfo
~TagClassInfo() { delete bases; delete templateArguments; }
QString name;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
QList<BaseInfo> *bases;
QList<TagMemberInfo> members;
QList<QString> *templateArguments;
@@ -75,7 +91,7 @@ class TagNamespaceInfo
TagNamespaceInfo() { members.setAutoDelete(TRUE); }
QString name;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
QList<TagMemberInfo> members;
QStringList classList;
};
@@ -87,7 +103,7 @@ class TagPackageInfo
TagPackageInfo() { members.setAutoDelete(TRUE); }
QString name;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
QList<TagMemberInfo> members;
QStringList classList;
};
@@ -110,7 +126,7 @@ class TagFileInfo
QString name;
QString path;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
QList<TagMemberInfo> members;
QStringList classList;
QStringList namespaceList;
@@ -125,7 +141,7 @@ class TagGroupInfo
QString name;
QString title;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
QList<TagMemberInfo> members;
QStringList subgroupList;
QStringList classList;
@@ -142,7 +158,7 @@ class TagPageInfo
QString name;
QString title;
QString filename;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
};
/*! Container for directory specific info that can be read from a tagfile */
@@ -154,7 +170,7 @@ class TagDirInfo
QString path;
QStringList subdirList;
QStringList fileList;
- QStrList docAnchors;
+ TagAnchorInfoList docAnchors;
};
/*! Tag file parser.
@@ -317,8 +333,8 @@ class TagFileParser : public QXmlDefaultHandler
{
m_curMember = new TagMemberInfo;
m_curMember->kind = attrib.value("kind");
- QString protStr = attrib.value("protection");
- QString virtStr = attrib.value("virtualness");
+ QString protStr = attrib.value("protection");
+ QString virtStr = attrib.value("virtualness");
QString staticStr = attrib.value("static");
if (protStr=="protected")
{
@@ -364,14 +380,14 @@ class TagFileParser : public QXmlDefaultHandler
{
switch(m_state)
{
- case InClass: m_curClass->docAnchors.append(m_curString); break;
- case InFile: m_curFile->docAnchors.append(m_curString); break;
- case InNamespace: m_curNamespace->docAnchors.append(m_curString); break;
- case InGroup: m_curGroup->docAnchors.append(m_curString); break;
- case InPage: m_curPage->docAnchors.append(m_curString); break;
- case InMember: m_curMember->docAnchors.append(m_curString); break;
- case InPackage: m_curPackage->docAnchors.append(m_curString); break;
- case InDir: m_curDir->docAnchors.append(m_curString); break;
+ case InClass: m_curClass->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InFile: m_curFile->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InNamespace: m_curNamespace->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InGroup: m_curGroup->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InPage: m_curPage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InMember: m_curMember->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InPackage: m_curPackage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
+ case InDir: m_curDir->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
default: err("Error: Unexpected tag `member' found\n"); break;
}
}
@@ -424,6 +440,11 @@ class TagFileParser : public QXmlDefaultHandler
{
m_curString = "";
}
+ void startDocAnchor(const QXmlAttributes& attrib )
+ {
+ m_fileName = attrib.value("file");
+ m_curString = "";
+ }
void endType()
{
if (m_state==InMember)
@@ -640,7 +661,7 @@ class TagFileParser : public QXmlDefaultHandler
m_startElementHandlers.insert("file", new StartElementHandler(this,&TagFileParser::startStringValue));
m_startElementHandlers.insert("dir", new StartElementHandler(this,&TagFileParser::startStringValue));
m_startElementHandlers.insert("page", new StartElementHandler(this,&TagFileParser::startStringValue));
- m_startElementHandlers.insert("docanchor", new StartElementHandler(this,&TagFileParser::startStringValue));
+ m_startElementHandlers.insert("docanchor", new StartElementHandler(this,&TagFileParser::startDocAnchor));
m_startElementHandlers.insert("tagfile", new StartElementHandler(this,&TagFileParser::startIgnoreElement));
m_startElementHandlers.insert("templarg", new StartElementHandler(this,&TagFileParser::startStringValue));
m_startElementHandlers.insert("type", new StartElementHandler(this,&TagFileParser::startStringValue));
@@ -708,7 +729,7 @@ class TagFileParser : public QXmlDefaultHandler
private:
void buildMemberList(Entry *ce,QList<TagMemberInfo> &members);
- void addDocAnchors(Entry *e,QStrList &l);
+ void addDocAnchors(Entry *e,const TagAnchorInfoList &l);
QList<TagClassInfo> m_tagFileClasses;
QList<TagFileInfo> m_tagFileFiles;
QList<TagNamespaceInfo> m_tagFileNamespaces;
@@ -729,6 +750,7 @@ class TagFileParser : public QXmlDefaultHandler
TagIncludeInfo *m_curIncludes;
QCString m_curString;
QString m_tagName;
+ QString m_fileName;
State m_state;
QStack<State> m_stateStack;
};
@@ -932,24 +954,25 @@ void TagFileParser::dump()
}
}
-void TagFileParser::addDocAnchors(Entry *e,QStrList &l)
+void TagFileParser::addDocAnchors(Entry *e,const TagAnchorInfoList &l)
{
- char *s=l.first();
- while (s)
+ QListIterator<TagAnchorInfo> tli(l);
+ TagAnchorInfo *ta;
+ for (tli.toFirst();(ta=tli.current());++tli)
{
- QCString *anchorName = new QCString(s);
- if (Doxygen::sectionDict.find(*anchorName)==0)
+ if (Doxygen::sectionDict.find(ta->label)==0)
{
- SectionInfo *si=new SectionInfo(e->fileName,*anchorName,*anchorName,
+ //printf("New sectionInfo file=%s anchor=%s\n",
+ // ta->fileName.data(),ta->label.data());
+ SectionInfo *si=new SectionInfo(ta->fileName,ta->label,ta->label,
SectionInfo::Anchor,m_tagName);
- Doxygen::sectionDict.insert(*anchorName,si);
+ Doxygen::sectionDict.insert(ta->label,si);
e->anchors->append(si);
}
else
{
- err("Duplicate anchor %s found\n",anchorName->data());
+ err("Duplicate anchor %s found\n",ta->label.data());
}
- s=l.next();
}
}
diff --git a/src/util.cpp b/src/util.cpp
index fba7edb..10d3ea2 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -128,23 +128,31 @@ int iSystem(const char *command,const char *args,bool isBatchFile)
{
QTime time;
time.start();
+
+ if (command==0) return 1;
+
+ QCString fullCmd=command;
+ fullCmd=fullCmd.stripWhiteSpace();
+ if (fullCmd.at(0)!='"' && fullCmd.find(' ')!=-1)
+ {
+ // add quotes around command as it contains spaces and is not quoted already
+ fullCmd="\""+fullCmd+"\"";
+ }
+ fullCmd += " ";
+ fullCmd += args;
+ //printf("iSystem: Executing %s\n",fullCmd.data());
+
#if !defined(_WIN32) || defined(__CYGWIN__)
isBatchFile=isBatchFile;
/*! taken from the system() manpage on my Linux box */
int pid,status=0;
- if (command==0) return 1;
#ifdef _OS_SOLARIS // for Solaris we use vfork since it is more memory efficient
// on Solaris fork() duplicates the memory usage
// so we use vfork instead
- char buf[4096];
- strcpy(buf,command);
- strcat(buf," ");
- strcat(buf,args);
-
// spawn shell
if ((pid=vfork())<0)
{
@@ -152,7 +160,7 @@ int iSystem(const char *command,const char *args,bool isBatchFile)
}
else if (pid==0)
{
- execl("/bin/sh","sh","-c",buf,(char*)0);
+ execl("/bin/sh","sh","-c",fullCmd.data(),(char*)0);
_exit(127);
}
else
@@ -175,14 +183,10 @@ int iSystem(const char *command,const char *args,bool isBatchFile)
if (pid==-1) return -1;
if (pid==0)
{
- char buf[4096];
- strcpy(buf,command);
- strcat(buf," ");
- strcat(buf,args);
const char * argv[4];
argv[0] = "sh";
argv[1] = "-c";
- argv[2] = buf;
+ argv[2] = fullCmd.data();
argv[3] = 0;
execve("/bin/sh",(char * const *)argv,environ);
exit(127);
@@ -204,9 +208,6 @@ int iSystem(const char *command,const char *args,bool isBatchFile)
#else // Win32 specific
if (isBatchFile)
{
- QCString fullCmd = command;
- fullCmd += " ";
- fullCmd += args;
return system(fullCmd);
}
else
@@ -1107,7 +1108,7 @@ ClassDef *getResolvedClassRec(Definition *scope,
bestTemplSpec.resize(0);
}
else if (distance==minDistance &&
- fileScope &&
+ fileScope && bestMatch &&
fileScope->getUsedNamespaces() &&
d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
bestMatch->getOuterScope()==Doxygen::globalScope
@@ -1143,10 +1144,14 @@ ClassDef *getResolvedClassRec(Definition *scope,
{
QCString spec;
minDistance=distance;
- bestMatch = newResolveTypedef(fileScope,md,&spec);
- //printf(" bestTypeDef=%p spec=%s\n",md,spec.data());
- bestTypedef = md;
- bestTemplSpec = spec;
+ ClassDef *cd = newResolveTypedef(fileScope,md,&spec);
+ if (cd) // shouldn't be 0, but could in some weird cases
+ {
+ //printf(" bestTypeDef=%p spec=%s\n",md,spec.data());
+ bestMatch = cd;
+ bestTypedef = md;
+ bestTemplSpec = spec;
+ }
}
}
}