diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-10-15 10:54:45 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-10-15 10:54:45 +0900 |
commit | 5e552810bc3dfc820036b4b16ae53561bb7cf3c6 (patch) | |
tree | 50ee6379d4fdad236b392051a73a2825dc98fb5e /src/util.cpp | |
parent | d6f1e74d173f59fc63d7e031cd9685b4eb38fed2 (diff) | |
download | doxygen-5e552810bc3dfc820036b4b16ae53561bb7cf3c6.tar.gz doxygen-5e552810bc3dfc820036b4b16ae53561bb7cf3c6.tar.bz2 doxygen-5e552810bc3dfc820036b4b16ae53561bb7cf3c6.zip |
Imported Upstream version 1.8.15upstream/1.8.15
Diffstat (limited to 'src/util.cpp')
-rw-r--r-- | src/util.cpp | 376 |
1 files changed, 320 insertions, 56 deletions
diff --git a/src/util.cpp b/src/util.cpp index 7a4bd65..08c13fb 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -47,6 +47,7 @@ #include "searchindex.h" #include "doxygen.h" #include "textdocvisitor.h" +#include "latexdocvisitor.h" #include "portable.h" #include "parserintf.h" #include "bufstr.h" @@ -250,6 +251,7 @@ void writePageRef(OutputDocInterface &od,const char *cn,const char *mn) od.disable(OutputGenerator::Html); od.disable(OutputGenerator::Man); + od.disable(OutputGenerator::Docbook); if (Config_getBool(PDF_HYPERLINKS)) od.disable(OutputGenerator::Latex); if (Config_getBool(RTF_HYPERLINKS)) od.disable(OutputGenerator::RTF); od.startPageRef(); @@ -322,7 +324,6 @@ int guessSection(const char *name) n.right(4)==".c++" || n.right(5)==".java" || n.right(2)==".m" || - n.right(2)==".M" || n.right(3)==".mm" || n.right(3)==".ii" || // inline n.right(4)==".ixx" || @@ -332,14 +333,15 @@ int guessSection(const char *name) n.right(4)==".xml" || n.right(4)==".sql" ) return Entry::SOURCE_SEC; - if (n.right(2)==".h" || // header - n.right(3)==".hh" || - n.right(4)==".hxx" || - n.right(4)==".hpp" || - n.right(4)==".h++" || - n.right(4)==".idl" || - n.right(4)==".ddl" || - n.right(5)==".pidl" + if (n.right(2)==".h" || // header + n.right(3)==".hh" || + n.right(4)==".hxx" || + n.right(4)==".hpp" || + n.right(4)==".h++" || + n.right(4)==".idl" || + n.right(4)==".ddl" || + n.right(5)==".pidl" || + n.right(4)==".ice" ) return Entry::HEADER_SEC; return 0; } @@ -1781,7 +1783,7 @@ QCString removeRedundantWhiteSpace(const QCString &s) pc = c; i++; c = src[i]; - *dst+=c; + *dst++=c; } else if (c=='"') { @@ -1845,7 +1847,7 @@ QCString removeRedundantWhiteSpace(const QCString &s) case '*': if (i>0 && pc!=' ' && pc!='\t' && pc!=':' && pc!='*' && pc!='&' && pc!='(' && pc!='/' && - pc!='.' && (osp<9 || (pc=='>' && osp==11))) + pc!='.' && (osp<9 || !(pc=='>' && osp==11))) // avoid splitting &&, **, .*, operator*, operator->* { *dst++=' '; @@ -1855,7 +1857,11 @@ QCString removeRedundantWhiteSpace(const QCString &s) case '&': if (i>0 && isId(pc)) { - *dst++=' '; + if (nc != '=') + // avoid splitting operator&= + { + *dst++=' '; + } } *dst++=c; break; @@ -1889,11 +1895,18 @@ QCString removeRedundantWhiteSpace(const QCString &s) if (g_charAroundSpace.charMap[(uchar)pc].before && g_charAroundSpace.charMap[(uchar)nc].after && !(pc==',' && nc=='.') && - (osp<8 || (osp>=8 && isId(nc))) // e.g. "operator >>" -> "operator>>", but not "operator int" -> operatorint" + (osp<8 || (osp>=8 && pc!='"' && isId(nc)) || (osp>=8 && pc!='"' && nc!='"')) + // e.g. 'operator >>' -> 'operator>>', + // 'operator "" _x' -> 'operator""_x', + // but not 'operator int' -> 'operatorint' ) { // keep space *dst++=' '; } + else if ((pc=='*' || pc=='&' || pc=='.') && nc=='>') + { + *dst++=' '; + } } break; default: @@ -2211,6 +2224,7 @@ void writeExample(OutputList &ol,ExampleSDict *ed) //if (latexEnabled) ol.disable(OutputGenerator::Latex); ol.disable(OutputGenerator::Latex); ol.disable(OutputGenerator::RTF); + ol.disable(OutputGenerator::Docbook); // link for Html / man //printf("writeObjectLink(file=%s)\n",e->file.data()); ol.writeObjectLink(0,e->file,e->anchor,e->name); @@ -2587,7 +2601,7 @@ QCString dateToString(bool includeTime) static bool warnedOnce=FALSE; if (!warnedOnce) { - warn_uncond("Environment variable SOURCE_DATA_EPOCH must have a value smaller than or equal to %llu; actual value %llu\n",UINT_MAX,epoch); + warn_uncond("Environment variable SOURCE_DATE_EPOCH must have a value smaller than or equal to %llu; actual value %llu\n",UINT_MAX,epoch); warnedOnce=TRUE; } } @@ -2661,7 +2675,7 @@ Protection classInheritedProtectionLevel(ClassDef *cd,ClassDef *bcd,Protection p if (level==256) { err("Internal inconsistency: found class %s seem to have a recursive " - "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data()); + "inheritance relation! Please send a bug report to doxygen@gmail.com\n",cd->name().data()); } else if (cd->baseClasses()) { @@ -4190,7 +4204,7 @@ bool getDefs(const QCString &scName, //} } } - //printf(" >Succes=%d\n",mdist<maxInheritanceDepth); + //printf(" >Success=%d\n",mdist<maxInheritanceDepth); if (mdist<maxInheritanceDepth) { if (!md->isLinkable() || md->isStrongEnumValue()) @@ -4600,7 +4614,7 @@ bool resolveRef(/* in */ const char *scName, QCString fullName = substitute(tsName,"#","::"); if (fullName.find("anonymous_namespace{")==-1) { - fullName = removeRedundantWhiteSpace(substitute(fullName,".","::")); + fullName = removeRedundantWhiteSpace(substitute(fullName,".","::",3)); } else { @@ -4773,7 +4787,7 @@ QCString linkToText(SrcLangExt lang,const char *link,bool isFileName) // replace # by :: result=substitute(result,"#","::"); // replace . by :: - if (!isFileName && result.find('<')==-1) result=substitute(result,".","::"); + if (!isFileName && result.find('<')==-1) result=substitute(result,".","::",3); // strip leading :: prefix if present if (result.at(0)==':' && result.at(1)==':') { @@ -5217,6 +5231,7 @@ QCString substitute(const QCString &s,const QCString &src,const QCString &dst) int l = (int)(q-p); memcpy(r,p,l); r+=l; + if (dst) memcpy(r,dst,dstLen); r+=dstLen; } @@ -5225,6 +5240,82 @@ QCString substitute(const QCString &s,const QCString &src,const QCString &dst) return result; } + +/// substitute all occurrences of \a src in \a s by \a dst, but skip +/// each consecutive sequence of \a src where the number consecutive +/// \a src matches \a skip_seq; if \a skip_seq is negative, skip any +/// number of consecutive \a src +QCString substitute(const QCString &s,const QCString &src,const QCString &dst,int skip_seq) +{ + if (s.isEmpty() || src.isEmpty()) return s; + const char *p, *q; + int srcLen = src.length(); + int dstLen = dst.length(); + int resLen; + if (srcLen!=dstLen) + { + int count; + for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++; + resLen = s.length()+count*(dstLen-srcLen); + } + else // result has same size as s + { + resLen = s.length(); + } + QCString result(resLen+1); + char *r; + for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen) + { + // search a consecutive sequence of src + int seq = 0, skip = 0; + if (skip_seq) + { + for (const char *n=q+srcLen; qstrncmp(n,src,srcLen)==0; seq=1+skip, n+=srcLen) + ++skip; // number of consecutive src after the current one + + // verify the allowed number of consecutive src to skip + if (skip_seq > 0 && skip_seq != seq) + seq = skip = 0; + } + + // skip a consecutive sequence of src when necessary + int l = (int)((q + seq * srcLen)-p); + memcpy(r,p,l); + r+=l; + + if (skip) + { + // skip only the consecutive src found after the current one + q += skip * srcLen; + // the next loop will skip the current src, aka (p=q+srcLen) + continue; + } + + if (dst) memcpy(r,dst,dstLen); + r+=dstLen; + } + qstrcpy(r,p); + result.resize(strlen(result.data())+1); + //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data()); + return result; +} + +/// substitute all occurrences of \a srcChar in \a s by \a dstChar +QCString substitute(const QCString &s,char srcChar,char dstChar) +{ + int l=s.length(); + QCString result(l+1); + char *q=result.rawData(); + if (l>0) + { + const char *p=s.data(); + char c; + while ((c=*p++)) *q++ = (c==srcChar) ? dstChar : c; + } + *q='\0'; + return result; +} + //---------------------------------------------------------------------- QCString substituteKeywords(const QCString &s,const char *title, @@ -5851,12 +5942,73 @@ QCString convertToXML(const char *s) return growBuf.get(); } +/*! Converts a string to an DocBook-encoded string */ +QCString convertToDocBook(const char *s) +{ + static GrowBuf growBuf; + growBuf.clear(); + if (s==0) return ""; + const unsigned char *q; + int cnt; + const unsigned char *p=(const unsigned char *)s; + char c; + while ((c=*p++)) + { + switch (c) + { + case '<': growBuf.addStr("<"); break; + case '>': growBuf.addStr(">"); break; + case '&': // possibility to have a special symbol + q = p; + cnt = 2; // we have to count & and ; as well + while ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') || (*q >= '0' && *q <= '9')) + { + cnt++; + q++; + } + if (*q == ';') + { + --p; // we need & as well + DocSymbol::SymType res = HtmlEntityMapper::instance()->name2sym(QCString((char *)p).left(cnt)); + if (res == DocSymbol::Sym_Unknown) + { + p++; + growBuf.addStr("&"); + } + else + { + growBuf.addStr(HtmlEntityMapper::instance()->docbook(res)); + q++; + p = q; + } + } + else + { + growBuf.addStr("&"); + } + break; + case '\'': growBuf.addStr("'"); break; + case '"': growBuf.addStr("""); break; + case '\007': growBuf.addStr("␇"); break; + case 1: case 2: case 3: case 4: case 5: case 6: case 8: + case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: + case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: + case 27: case 28: case 29: case 30: case 31: + break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char) + default: growBuf.addChar(c); break; + } + } + growBuf.addChar(0); + return growBuf.get(); +} + /*! Converts a string to a HTML-encoded string */ QCString convertToHtml(const char *s,bool keepEntities) { static GrowBuf growBuf; growBuf.clear(); if (s==0) return ""; + growBuf.addStr(getHtmlDirEmbedingChar(getTextDirByConfig(s))); const char *p=s; char c; while ((c=*p++)) @@ -5898,11 +6050,13 @@ QCString convertToHtml(const char *s,bool keepEntities) return growBuf.get(); } -QCString convertToJSString(const char *s) +QCString convertToJSString(const char *s, bool applyTextDir) { static GrowBuf growBuf; growBuf.clear(); if (s==0) return ""; + if (applyTextDir) + growBuf.addStr(getJsDirEmbedingChar(getTextDirByConfig(s))); const char *p=s; char c; while ((c=*p++)) @@ -6403,7 +6557,7 @@ QCString mergeScopes(const QCString &leftScope,const QCString &rightScope) // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C" // case leftScope=="A::B" rightScope=="B" => result = "A::B" bool found=FALSE; - while ((i=leftScope.findRev("::",p))!=-1) + while ((i=leftScope.findRev("::",p))>0) { if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2))) { @@ -6492,6 +6646,8 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, // append documentation block to the page. pd->setDocumentation(doc,fileName,startLine); //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pd,name); + // append (x)refitems to the page. + pd->setRefItems(sli); } else // new page { @@ -6657,10 +6813,26 @@ void filterLatexString(FTextStream &t,const char *str, { switch(c) { + case 0xef: // handle U+FFFD i.e. "Replacement character" caused by octal: 357 277 275 / hexadecimal 0xef 0xbf 0xbd + // the LaTeX command \ucr has been defined in doxygen.sty + if ((unsigned char)*(p) == 0xbf && (unsigned char)*(p+1) == 0xbd) + { + t << "{\\ucr}"; + p += 2; + } + else + t << (char)c; + break; case '\\': t << "\\(\\backslash\\)"; break; case '{': t << "\\{"; break; case '}': t << "\\}"; break; case '_': t << "\\_"; break; + case '&': t << "\\&"; break; + case '%': t << "\\%"; break; + case '#': t << "\\#"; break; + case '$': t << "\\$"; break; + case '^': (usedTableLevels()>0) ? t << "\\string^" : t << (char)c; break; + case '~': (usedTableLevels()>0) ? t << "\\string~" : t << (char)c; break; case ' ': if (keepSpaces) t << "~"; else t << ' '; break; default: @@ -6672,6 +6844,16 @@ void filterLatexString(FTextStream &t,const char *str, { switch(c) { + case 0xef: // handle U+FFFD i.e. "Replacement character" caused by octal: 357 277 275 / hexadecimal 0xef 0xbf 0xbd + // the LaTeX command \ucr has been defined in doxygen.sty + if ((unsigned char)*(p) == 0xbf && (unsigned char)*(p+1) == 0xbd) + { + t << "{\\ucr}"; + p += 2; + } + else + t << (char)c; + break; case '#': t << "\\#"; break; case '$': t << "\\$"; break; case '%': t << "\\%"; break; @@ -6755,7 +6937,7 @@ void filterLatexString(FTextStream &t,const char *str, } } -QCString latexEscapeLabelName(const char *s,bool insideTabbing) +QCString latexEscapeLabelName(const char *s) { QGString result; QCString tmp(qstrlen(s)+1); @@ -6785,14 +6967,14 @@ QCString latexEscapeLabelName(const char *s,bool insideTabbing) p++; } tmp[i]=0; - filterLatexString(t,tmp.data(),insideTabbing); + filterLatexString(t,tmp,TRUE); break; } } return result.data(); } -QCString latexEscapeIndexChars(const char *s,bool insideTabbing) +QCString latexEscapeIndexChars(const char *s) { QGString result; QCString tmp(qstrlen(s)+1); @@ -6823,7 +7005,7 @@ QCString latexEscapeIndexChars(const char *s,bool insideTabbing) p++; } tmp[i]=0; - filterLatexString(t,tmp.data(),insideTabbing); + filterLatexString(t,tmp.data(),TRUE); break; } } @@ -6854,6 +7036,25 @@ QCString latexEscapePDFString(const char *s) return result.data(); } +QCString latexFilterURL(const char *s) +{ + QGString result; + FTextStream t(&result); + const char *p=s; + char c; + while ((c=*p++)) + { + switch (c) + { + case '#': t << "\\#"; break; + default: + t << c; + break; + } + } + return result.data(); +} + QCString rtfFormatBmkStr(const char *name) { @@ -7064,6 +7265,7 @@ g_lang2extMap[] = { "objective-c", "c", SrcLangExt_ObjC }, { "c", "c", SrcLangExt_Cpp }, { "c++", "c", SrcLangExt_Cpp }, + { "slice", "c", SrcLangExt_Slice }, { "python", "python", SrcLangExt_Python }, { "fortran", "fortran", SrcLangExt_Fortran }, { "fortranfree", "fortranfree", SrcLangExt_Fortran }, @@ -7169,6 +7371,7 @@ void initDefaultExtensionMapping() updateLanguageMapping(".qsf", "vhdl"); updateLanguageMapping(".md", "md"); updateLanguageMapping(".markdown", "md"); + updateLanguageMapping(".ice", "slice"); } void addCodeOnlyMappings() @@ -7320,23 +7523,23 @@ int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos) { if (((uchar)c&0xE0)==0xC0) { - bytes++; // 11xx.xxxx: >=2 byte character + bytes+=1; // 11xx.xxxx: >=2 byte character } if (((uchar)c&0xF0)==0xE0) { - bytes++; // 111x.xxxx: >=3 byte character + bytes+=2; // 111x.xxxx: >=3 byte character } if (((uchar)c&0xF8)==0xF0) { - bytes++; // 1111.xxxx: >=4 byte character + bytes+=3; // 1111.xxxx: >=4 byte character } if (((uchar)c&0xFC)==0xF8) { - bytes++; // 1111.1xxx: >=5 byte character + bytes+=4; // 1111.1xxx: >=5 byte character } if (((uchar)c&0xFE)==0xFC) { - bytes++; // 1111.1xxx: 6 byte character + bytes+=5; // 1111.1xxx: 6 byte character } } else if (c=='&') // skip over character entities @@ -7370,11 +7573,10 @@ QCString parseCommentAsText(const Definition *scope,const MemberDef *md, root->accept(visitor); delete visitor; delete root; - QCString result = convertCharEntitiesToUTF8(s.data()); + QCString result = convertCharEntitiesToUTF8(s.data()).stripWhiteSpace(); int i=0; int charCnt=0; int l=result.length(); - bool addEllipsis=FALSE; while ((i=nextUtf8CharPosition(result,l,i))<l) { charCnt++; @@ -7385,19 +7587,17 @@ QCString parseCommentAsText(const Definition *scope,const MemberDef *md, while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100) { charCnt++; - if (result.at(i)>=0 && isspace(result.at(i))) - { - addEllipsis=TRUE; - } - else if (result.at(i)==',' || - result.at(i)=='.' || - result.at(i)=='?') + if (result.at(i)==',' || + result.at(i)=='.' || + result.at(i)=='!' || + result.at(i)=='?') { + i++; // we want to be "behind" last inspected character break; } } } - if (addEllipsis || charCnt==100) result=result.left(i)+"..."; + if ( i < l) result=result.left(i)+"..."; return result.data(); } @@ -7842,8 +8042,8 @@ bool readInputFile(const char *fileName,BufStr &inBuf,bool filter,bool isSourceC int start=0; if (size>=2 && - ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM - (inBuf.at(0)==-2 && inBuf.at(1)==-1) // big endian BOM + (((uchar)inBuf.at(0)==0xFF && (uchar)inBuf.at(1)==0xFE) || // Little endian BOM + ((uchar)inBuf.at(0)==0xFE && (uchar)inBuf.at(1)==0xFF) // big endian BOM ) ) // UCS-2 encoded file { @@ -8006,7 +8206,7 @@ QCString externalRef(const QCString &relPath,const QCString &ref,bool href) return result; } -/** Writes the intensity only bitmap representated by \a data as an image to +/** Writes the intensity only bitmap represented by \a data as an image to * directory \a dir using the colors defined by HTML_COLORSTYLE_*. */ void writeColoredImgData(const char *dir,ColoredImgDataItem data[]) @@ -8205,6 +8405,7 @@ QCString langToString(SrcLangExt lang) case SrcLangExt_SQL: return "SQL"; case SrcLangExt_Tcl: return "Tcl"; case SrcLangExt_Markdown: return "Markdown"; + case SrcLangExt_Slice: return "Slice"; } return "Unknown"; } @@ -8336,12 +8537,9 @@ bool fileVisibleInIndex(FileDef *fd,bool &genSourceFile) void addDocCrossReference(MemberDef *src,MemberDef *dst) { - static bool referencedByRelation = Config_getBool(REFERENCED_BY_RELATION); - static bool referencesRelation = Config_getBool(REFERENCES_RELATION); - //printf("--> addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types - if ((referencedByRelation || dst->hasCallerGraph()) && + if ((dst->hasReferencedByRelation() || dst->hasCallerGraph()) && src->showInCallGraph() ) { @@ -8357,7 +8555,7 @@ void addDocCrossReference(MemberDef *src,MemberDef *dst) mdDecl->addSourceReferencedBy(src); } } - if ((referencesRelation || src->hasCallGraph()) && + if ((src->hasReferencesRelation() || src->hasCallGraph()) && src->showInCallGraph() ) { @@ -8444,7 +8642,7 @@ uint getUtf8CodeToUpper( const QCString& s, int idx ) //-------------------------------------------------------------------------------------- -bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses) +bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses,bool filterClasses,ClassDef::CompoundType ct) { if (nd->getNamespaceSDict()) { @@ -8456,21 +8654,41 @@ bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses) { return TRUE; } - else if (namespaceHasVisibleChild(cnd,includeClasses)) + else if (namespaceHasVisibleChild(cnd,includeClasses,filterClasses,ct)) { return TRUE; } } } - if (includeClasses && nd->getClassSDict()) + if (includeClasses) { - ClassSDict::Iterator cli(*nd->getClassSDict()); - ClassDef *cd; - for (;(cd=cli.current());++cli) + ClassSDict *d = nd->getClassSDict(); + if (filterClasses) { - if (cd->isLinkableInProject() && cd->templateMaster()==0) - { - return TRUE; + if (ct == ClassDef::Interface) + { + d = nd->getInterfaceSDict(); + } + else if (ct == ClassDef::Struct) + { + d = nd->getStructSDict(); + } + else if (ct == ClassDef::Exception) + { + d = nd->getExceptionSDict(); + } + } + + if (d) + { + ClassSDict::Iterator cli(*d); + ClassDef *cd; + for (;(cd=cli.current());++cli) + { + if (cd->isLinkableInProject() && cd->templateMaster()==0) + { + return TRUE; + } } } } @@ -8759,3 +8977,49 @@ void writeExtraLatexPackages(FTextStream &t) } } +void writeLatexSpecialFormulaChars(FTextStream &t) +{ + unsigned char minus[4]; // Superscript minus + char *pminus = (char *)minus; + unsigned char sup2[3]; // Superscript two + char *psup2 = (char *)sup2; + unsigned char sup3[3]; + char *psup3 = (char *)sup3; // Superscript three + minus[0]= 0xE2; + minus[1]= 0x81; + minus[2]= 0xBB; + minus[3]= 0; + sup2[0]= 0xC2; + sup2[1]= 0xB2; + sup2[2]= 0; + sup3[0]= 0xC2; + sup3[1]= 0xB3; + sup3[2]= 0; + + t << "\\usepackage{newunicodechar}\n" + " \\newunicodechar{" << pminus << "}{${}^{-}$}% Superscript minus\n" + " \\newunicodechar{" << psup2 << "}{${}^{2}$}% Superscript two\n" + " \\newunicodechar{" << psup3 << "}{${}^{3}$}% Superscript three\n" + "\n"; +} + +//------------------------------------------------------ + +static int g_usedTableLevels = 0; + +void incUsedTableLevels() +{ + g_usedTableLevels++; +} +void decUsedTableLevels() +{ + g_usedTableLevels--; +} +int usedTableLevels() +{ + return g_usedTableLevels; +} + +//------------------------------------------------------ + + |