///////////////////////////////////////////////// // Xml-Object-Saving TStrStrH TXmlObjSer::TypeNmToTagNmH; TStr TXmlObjSer::GetTagNm(const TStr& TypeNm){ TStr& XmlTagNm=TypeNmToTagNmH.AddDat(TypeNm); if (XmlTagNm.Empty()){ TChA XmlTagChA=TypeNm; for (int ChN=0; ChN0)&&(XmlTagChA.LastCh()=='_')){ XmlTagChA.Pop();} XmlTagNm=XmlTagChA; } return XmlTagNm; } void TXmlObjSer::AssertXmlHd( const PXmlTok& XmlTok, const TStr& Nm, const TStr& TypeNm){ // check if the token is full EAssertR(!XmlTok.Empty(), "Xml-Token Empty"); // if name is empty then tag=type else tag=name if (!Nm.Empty()){ // check if the token is tag if (!XmlTok->IsTag()){ TStr ArgStr1="Expected: Tag"; TStr ArgStr2=TStr("Found: ")+XmlTok->GetSymStr(); TExcept::Throw("Invalid Xml-Token", ArgStr1, ArgStr2); } if (Nm!="-"){ // check if the tag is correct if (!XmlTok->IsTag(Nm)){ TStr ArgStr1=TStr("Expected: ")+Nm; TStr ArgStr2=TStr("Found: ")+XmlTok->GetStr(); TExcept::Throw("Invalid Xml-Tag", ArgStr1, ArgStr2); } // check if the type is correct TStr TypeArgVal=XmlTok->GetStrArgVal("Type"); if (TypeArgVal!=TypeNm){ TStr ArgStr1=TStr("Expected: ")+TypeNm; TStr ArgStr2=TStr("Found: ")+TypeArgVal; TExcept::Throw("Invalid Xml-Type", ArgStr1, ArgStr2); } } } else { // check if the tag is correct if (!XmlTok->IsTag(TypeNm)){ TStr ArgStr1=TStr("Expected: ")+TypeNm; TStr ArgStr2=TStr("Found: ")+XmlTok->GetSymStr(); TExcept::Throw("Invalid Xml-Type-Tag", ArgStr1, ArgStr2); } } } bool TXmlObjSer::GetBoolArg(const PXmlTok& XmlTok, const TStr& Nm){ TStr ValStr; if (XmlTok->IsArg(Nm, ValStr)){ bool Val; if (ValStr.IsBool(Val)){ return Val; } else { TExcept::Throw("Invalid Xml-Argument Boolean-Value", Nm, ValStr); } } else { TExcept::Throw("Xml-Argument Missing", Nm); } Fail; return 0; } int TXmlObjSer::GetIntArg(const PXmlTok& XmlTok, const TStr& Nm){ TStr ValStr; if (XmlTok->IsArg(Nm, ValStr)){ int Val; if (ValStr.IsInt(Val)){ return Val; } else { TExcept::Throw("Invalid Xml-Argument Integer-Value", Nm, ValStr); } } else { TExcept::Throw("Xml-Argument Missing", Nm); } Fail; return 0; } int64 TXmlObjSer::GetInt64Arg(const PXmlTok& XmlTok, const TStr& Nm){ TStr ValStr; if (XmlTok->IsArg(Nm, ValStr)){ int64 Val; if (ValStr.IsInt64(Val)){ return Val; } else { TExcept::Throw("Invalid Xml-Argument Integer64-Value", Nm, ValStr); } } else { TExcept::Throw("Xml-Argument Missing", Nm); } Fail; return 0; } double TXmlObjSer::GetFltArg(const PXmlTok& XmlTok, const TStr& Nm){ TStr ValStr; if (XmlTok->IsArg(Nm, ValStr)){ double Val; if (ValStr.IsFlt(Val)){ return Val; } else { TExcept::Throw("Invalid Xml-Argument Double-Value", Nm, ValStr); } } else { TExcept::Throw("Xml-Argument Missing", Nm); } Fail; return 0; } ///////////////////////////////////////////////// // Xml-Object-Serialization-Tag-Name TXmlObjSerTagNm::TXmlObjSerTagNm( TSOut& _SOut, const bool& ETagP, const TStr& Nm, const TStr& TypeNm, const TStr& ArgNm, const TStr& ArgVal): TagNm(), SOut(&_SOut){ if (Nm!="-"){ SOut->PutCh('<'); if (Nm.Empty()){ SOut->PutStr(TagNm=TypeNm); } else { SOut->PutStr(TagNm=Nm); SOut->PutStr(" Type=\""); SOut->PutStr(TypeNm); SOut->PutCh('"'); } if (!ArgNm.Empty()){ SOut->PutCh(' '); SOut->PutStr(ArgNm); SOut->PutCh('='); SOut->PutCh('"'); SOut->PutStr(ArgVal); SOut->PutCh('"'); } if (ETagP){ SOut->PutCh('/'); TagNm="";} SOut->PutCh('>'); } } TXmlObjSerTagNm::TXmlObjSerTagNm( TSOut& _SOut, const bool& ETagP, const TStr& Nm, const TStr& TypeNm, const TStr& ArgNm1, const TStr& ArgVal1, const TStr& ArgNm2, const TStr& ArgVal2, const TStr& ArgNm3, const TStr& ArgVal3, const TStr& ArgNm4, const TStr& ArgVal4): TagNm(), SOut(&_SOut){ if (Nm!="-"){ SOut->PutCh('<'); if (Nm.Empty()){ SOut->PutStr(TagNm=TypeNm); } else { SOut->PutStr(TagNm=Nm); SOut->PutStr(" Type=\""); SOut->PutStr(TypeNm); SOut->PutCh('"'); } if (!ArgNm1.Empty()){ SOut->PutCh(' '); SOut->PutStr(ArgNm1); SOut->PutCh('='); SOut->PutCh('"'); SOut->PutStr(ArgVal1); SOut->PutCh('"'); } if (!ArgNm2.Empty()){ SOut->PutCh(' '); SOut->PutStr(ArgNm2); SOut->PutCh('='); SOut->PutCh('"'); SOut->PutStr(ArgVal2); SOut->PutCh('"'); } if (!ArgNm3.Empty()){ SOut->PutCh(' '); SOut->PutStr(ArgNm3); SOut->PutCh('='); SOut->PutCh('"'); SOut->PutStr(ArgVal3); SOut->PutCh('"'); } if (!ArgNm4.Empty()){ SOut->PutCh(' '); SOut->PutStr(ArgNm4); SOut->PutCh('='); SOut->PutCh('"'); SOut->PutStr(ArgVal4); SOut->PutCh('"'); } if (ETagP){ SOut->PutCh('/'); TagNm="";} SOut->PutCh('>'); } } TXmlObjSerTagNm::~TXmlObjSerTagNm(){ if (!TagNm.Empty()){ SOut->PutCh('<'); SOut->PutCh('/'); SOut->PutStr(TagNm); SOut->PutCh('>'); } } ///////////////////////////////////////////////// // Xml-Chars void TXmlChDef::SetChTy(TBSet& ChSet, const int& MnCh, const int& MxCh){ IAssert((0<=MnCh)&&((MxCh==-1)||((MnCh<=MxCh)&&(MxCh"); SetEntityVal("apos", "'"); SetEntityVal("quot", "\""); } ///////////////////////////////////////////////// // Xml-Lexical TXmlChDef TXmlLx::ChDef; uchar TXmlLx::GetCh(){ EAssert(Ch!=TCh::EofCh); PrevCh=Ch; if (ChStack.Empty()){Ch=(RSIn.Eof()) ? TCh::EofCh : RSIn.GetCh();} else {Ch=ChStack.Pop();} ChN++; if (Ch==TCh::LfCh){LnN++; LnChN=0;} else {LnChN++;} //putchar(Ch); return Ch; } void TXmlLx::ToNrSpacing(){ if (Spacing==xspIntact){ } else if (Spacing==xspPreserve){ int SrcChN=0; int DstChN=0; while (SrcChN0)&&(TxtChA[DstChN-1]==' ')){ SrcChN++; } else { TxtChA.PutCh(DstChN, ' '); SrcChN++; DstChN++; } } else { TxtChA.PutCh(DstChN, TxtChA[SrcChN]); SrcChN++; DstChN++; } } TxtChA.Trunc(DstChN); } else if (Spacing==xspTruncate){ // cut leading and trailing white-spaces and // squeeze series of white-spaces to single space int SrcChN=0; int DstChN=0; while (SrcChN0)&&(TxtChA[DstChN-1]==' ')){ SrcChN++; } else { TxtChA.PutCh(DstChN, ' '); SrcChN++; DstChN++; } } else { TxtChA.PutCh(DstChN, TxtChA[SrcChN]); SrcChN++; DstChN++; } } TxtChA.Trunc(DstChN); // delete trailing white-spaces while ((TxtChA.Len()>0)&&(ChDef.IsWs(TxtChA.LastCh()))){ TxtChA.Trunc(TxtChA.Len()-1);} } else { Fail; } } void TXmlLx::GetWs(const bool& IsRq){ // [3] S ::= (#x20 | #x9 | #xD | #xA)+ int WSpaces=0; TxtChA.Clr(); while (ChDef.IsWs(Ch)){ WSpaces++; TxtChA+=Ch; GetCh();} if (IsRq&&(WSpaces==0)){ EThrow("White-space required.");} } TStr TXmlLx::GetReference(){ // [67] Reference ::= EntityRef | CharRef if (Ch=='#'){ // [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' TChA RefChA; int RefCd=0; if (GetCh()=='x'){ // hex-decimal character code forever { GetCh(); if (TCh::IsHex(Ch)){ RefChA+=Ch; RefCd=RefCd*16+TCh::GetHex(Ch); } else { break; } } } else { // decimal character code forever { if (TCh::IsNum(Ch)){ RefChA+=Ch; RefCd=RefCd*10+TCh::GetNum(Ch); } else { break; } GetCh(); } } if ((!RefChA.Empty())&&(Ch==';')){ GetCh(); uchar RefCh=uchar(RefCd); return TStr(RefCh); } else { EThrow("Invalid Char-Reference."); Fail; return TStr(); } } else { // [68] EntityRef ::= '&' Name ';' TStr EntityNm=GetName(); if ((!EntityNm.Empty())&&(Ch==';')){ GetCh(); TStr EntityVal; if (IsEntityNm(EntityNm, EntityVal)){/*intentionaly empty*/} else if (ChDef.IsEntityNm(EntityNm, EntityVal)){/*intentionaly empty*/} else {EThrow(TStr("Entity-Reference (")+EntityNm+") does not exist.");} return EntityVal; } else { EThrow("Invalid Entity-Reference."); Fail; return TStr(); } } } TStr TXmlLx::GetPEReference(){ // [69] PEReference ::= '%' Name ';' TStr EntityNm=GetName(); if ((EntityNm.Empty())||(Ch!=';')){EThrow("Invalid PEntity-Reference.");} GetCh(); TStr EntityVal; if (IsPEntityNm(EntityNm, EntityVal)){/*intentionaly empty*/} else {EThrow(TStr("PEntity-Reference (")+EntityNm+") does not exist.");} return EntityVal; } void TXmlLx::GetEq(){ // [25] Eq ::= S? '=' S? GetWs(false); if (Ch=='='){GetCh();} else {EThrow("Equality ('=') character expected.");} GetWs(false); } TStr TXmlLx::GetName(){ // [5] Name ::= (Letter | '_' | ':') (NameChar)* TChA NmChA; if (ChDef.IsFirstNameCh(Ch)){ do {NmChA+=Ch;} while (ChDef.IsName(GetCh())); } else { EThrow("Invalid first name character."); // EThrow(TStr::Fmt("Invalid first name character [%u:'%c%c%c%c%c'].", // uint(Ch), Ch, RSIn.GetCh(), RSIn.GetCh(), RSIn.GetCh(), RSIn.GetCh())); } return NmChA; } TStr TXmlLx::GetName(const TStr& RqNm){ TStr Nm=GetName(); // test if the name is equal to the required name if (Nm==RqNm){return RqNm;} else {EThrow(TStr("Name '")+RqNm+"' expected."); Fail; return TStr();} } void TXmlLx::GetComment(){ // [15] Comment ::= {{'' if (GetCh()!='-'){EThrow("Invalid comment start.");} TxtChA.Clr(); forever { GetCh(); if (!ChDef.IsChar(Ch)){EThrow("Invalid comment character.");} if (Ch=='-'){ if (GetCh()=='-'){ if (GetCh()=='>'){GetCh(); break;} // final bracket else {EThrow("Invalid comment end.");} } else { if (!ChDef.IsChar(Ch)){EThrow("Invalid comment character.");} TxtChA+='-'; TxtChA+=Ch; // special case if single '-' } } else { TxtChA+=Ch; // usual char } } } TStr TXmlLx::GetAttValue(){ // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' // | "'" ([^<&'] | Reference)* "'" uchar QCh=Ch; if ((QCh!='"')&&(QCh!='\'')){EThrow("Invalid attribute-value start.");} TChA ValChA; GetCh(); forever { if ((Ch=='<')||(!ChDef.IsChar(Ch))){ EThrow("Invalid attribute-value character.");} if (Ch==QCh){GetCh(); break;} // final quote else if (Ch=='&'){GetCh(); ValChA+=GetReference();} // reference else {ValChA+=Ch; GetCh();} // usual char } return ValChA; } TStr TXmlLx::GetVersionNum(){ // [24] VersionInfo ::= {{S 'version' Eq}} (' VersionNum ' | " VersionNum ") // [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+ char QCh=Ch; if ((Ch!='\'')&&(Ch!='"')){EThrow("Quote character (' or \") expected.");} TChA VerNumChA; GetCh(); do { if ((('a'<=Ch)&&(Ch<='z'))||(('A'<=Ch)&&(Ch<='Z'))|| (('0'<=Ch)&&(Ch<='9'))||(Ch=='_')||(Ch=='.')||(Ch==':')||(Ch=='-')){ VerNumChA+=Ch; } else { EThrow("Invalid version-number character."); } GetCh(); } while (Ch!=QCh); GetCh(); return VerNumChA; } TStr TXmlLx::GetEncName(){ // [80] EncodingDecl ::= {{S 'encoding' Eq}} ('"' EncName '"' | "'" EncName "'" ) // [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* char QCh=Ch; if ((Ch!='\'')&&(Ch!='"')){EThrow("Quote character (' or \") expected.");} TChA EncNmChA; GetCh(); if ((('a'<=Ch)&&(Ch<='z'))||(('A'<=Ch)&&(Ch<='Z'))){EncNmChA+=Ch;} else {EThrow("Invalid encoding-name character.");} GetCh(); while (Ch!=QCh){ if ((('a'<=Ch)&&(Ch<='z'))||(('A'<=Ch)&&(Ch<='Z'))|| (('0'<=Ch)&&(Ch<='9'))||(Ch=='.')||(Ch=='_')||(Ch=='-')){EncNmChA+=Ch;} else {EThrow("Invalid version-number character.");} GetCh(); } GetCh(); return EncNmChA; } TStr TXmlLx::GetStalVal(){ // [32] SDDecl ::= {{S 'standalone' Eq}} // (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) char QCh=Ch; if ((Ch!='\'')&&(Ch!='"')){EThrow("Quote character (' or \") expected.");} TChA StalChA; GetCh(); while (Ch!=QCh){ if (('a'<=Ch)&&(Ch<='z')){StalChA+=Ch;} else {EThrow("Invalid standalone-value character.");} GetCh(); } GetCh(); TStr StalVal=StalChA; if ((StalVal=="yes")||(StalVal=="no")){return StalVal;} else {EThrow("Invalid standalone-value."); Fail; return TStr();} } void TXmlLx::GetXmlDecl(){ // [23] XMLDecl ::= {{'' // [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ") GetWs(true); TStr VerNm=GetName("version"); GetEq(); TStr VerVal=GetVersionNum(); if (VerVal!="1.0"){EThrow("Invalid XML version.");} AddArg(VerNm, VerVal); GetWs(false); if (Ch!='?'){ // EncodingDecl ::= {{S}} 'encoding' Eq // ('"' EncName '"' | "'" EncName "'" ) TStr EncNm=GetName("encoding"); GetEq(); TStr EncVal=GetEncName(); AddArg(EncNm, EncVal); } GetWs(false); if (Ch!='?'){ // SDDecl ::= {{S}} 'standalone' Eq // (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) TStr StalNm=GetName("standalone"); GetEq(); TStr StalVal=GetStalVal(); AddArg(StalNm, StalVal); } GetWs(false); if (Ch=='?'){ GetCh(); if (Ch=='>'){GetCh();} else {EThrow("Invalid end-of-tag in XML-declaration.");} } else { EThrow("Invalid end-of-tag in XML-declaration."); } } void TXmlLx::GetPI(){ // [16] PI ::= {{'' Char*)))? '?>' // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) GetWs(false); TxtChA.Clr(); forever { if (!ChDef.IsChar(Ch)){EThrow("Invalid PI character.");} if (Ch=='?'){ if (GetCh()=='>'){ GetCh(); break; } else { if (!ChDef.IsChar(Ch)){EThrow("Invalid PI character.");} TxtChA+='?'; TxtChA+=Ch; // special case if single '?' } } else { TxtChA+=Ch; // usual char } GetCh(); } } TStr TXmlLx::GetSystemLiteral(){ // [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") char QCh=Ch; if ((Ch!='\'')&&(Ch!='"')){EThrow("Quote character (' or \") expected.");} TChA LitChA; GetCh(); while (Ch!=QCh){ if (!ChDef.IsChar(Ch)){EThrow("Invalid System-Literal character.");} LitChA+=Ch; GetCh(); } GetCh(); return LitChA; } TStr TXmlLx::GetPubidLiteral(){ // [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" char QCh=Ch; if ((Ch!='\'')&&(Ch!='"')){EThrow("Quote character (' or \") expected.");} TChA LitChA; GetCh(); while (Ch!=QCh){ if (!ChDef.IsPubid(Ch)){EThrow("Invalid Public-Id-Literal character.");} LitChA+=Ch; GetCh(); } GetCh(); return LitChA; } void TXmlLx::GetExternalId(){ // ExternalID ::= 'SYSTEM' S SystemLiteral // | 'PUBLIC' S PubidLiteral S SystemLiteral TStr ExtIdNm=GetName(); if (ExtIdNm=="SYSTEM"){ GetWs(true); GetSystemLiteral(); } else if (ExtIdNm=="PUBLIC"){ GetWs(true); GetPubidLiteral(); GetWs(true); GetSystemLiteral(); } else { EThrow("Invalid external-id ('SYSTEM' or 'PUBLIC' expected)."); } } void TXmlLx::GetNData(){ // [76] NDataDecl ::= S 'NDATA' S Name GetName("NDATA"); GetWs(true); GetName(); } void TXmlLx::GetDocTypeDecl(){ // [28] doctypedecl ::= {{'' GetWs(true); TStr DocTypeDeclNm=GetName(); GetWs(false); if (Ch=='>'){GetCh(); return;} if (Ch!='['){GetExternalId();} GetWs(false); if (Ch=='['){ GetCh(); // [28] (markupdecl | PEReference | S)* GetWs(false); while (Ch!=']'){ if (ChDef.IsWs(Ch)){GetWs(true);} else if (Ch=='%'){GetPEReference();} else { GetSym(); } } GetCh(); } GetWs(false); // '>' if (Ch=='>'){GetCh();} else {EThrow("Invalid end-of-tag in document-type-declaration.");} TagNm=DocTypeDeclNm; } void TXmlLx::GetElement(){ TxtChA.Clr(); while (Ch!='>'){ if (!ChDef.IsChar(Ch)){EThrow("Invalid Element character.");} TxtChA+=Ch; GetCh(); } GetCh(); } void TXmlLx::GetAttList(){ TxtChA.Clr(); while (Ch!='>'){ if (!ChDef.IsChar(Ch)){EThrow("Invalid Element character.");} TxtChA+=Ch; GetCh(); } GetCh(); } TStr TXmlLx::GetEntityValue(){ // [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' // | "'" ([^%&'] | PEReference | Reference)* "'" uchar QCh=Ch; if ((QCh!='"')&&(QCh!='\'')){EThrow("Invalid entity-value start.");} TChA ValChA; GetCh(); forever { if (!ChDef.IsChar(Ch)){EThrow("Invalid entity-value character.");} if (Ch==QCh){GetCh(); break;} // final quote else if (Ch=='&'){GetCh(); ValChA+=GetReference();} // reference else if (Ch=='%'){GetCh(); ValChA+=GetPEReference();} // pereference else {ValChA+=Ch; GetCh();} // usual char } return ValChA; } void TXmlLx::GetEntity(){ // [70] EntityDecl ::= GEDecl | PEDecl // [71] GEDecl ::= '' // [72] PEDecl ::= '' GetWs(true); TStr EntityNm; if (Ch=='%'){ GetCh(); GetWs(true); EntityNm=GetName(); GetWs(true); // [74] PEDef ::= EntityValue | ExternalID if ((Ch=='\"')||(Ch=='\'')){ TStr EntityVal=GetEntityValue(); PutPEntityVal(EntityNm, EntityVal); } else { GetExternalId(); GetWs(false); if (Ch!='>'){GetNData();} } } else { EntityNm=GetName(); GetWs(true); // [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) if ((Ch=='\"')||(Ch=='\'')){ TStr EntityVal=GetEntityValue(); PutEntityVal(EntityNm, EntityVal); } else { GetExternalId(); } } GetWs(false); if (Ch=='>'){GetCh();} else {EThrow("Invalid end-of-tag in entity-declaration.");} TagNm=EntityNm; } void TXmlLx::GetNotation(){ // [82] NotationDecl ::= '' // [83] PublicID ::= 'PUBLIC' S PubidLiteral TxtChA.Clr(); while (Ch!='>'){ if (!ChDef.IsChar(Ch)){EThrow("Invalid Element character.");} TxtChA+=Ch; GetCh(); } GetCh(); } void TXmlLx::GetCDSect(){ // [18] CDSect ::= CDStart CData CDEnd // [19] CDStart ::= '' Char*)) // [21] CDEnd ::= ']]>' if (Ch=='['){GetCh();} else {EThrow("Invalid start of CDATA section.");} TxtChA.Clr(); forever { if (!ChDef.IsChar(Ch)){EThrow("Invalid CDATA character.");} if ((Ch=='>')&&(TxtChA.Len()>=2)&& (TxtChA[TxtChA.Len()-2]==']')&&(TxtChA[TxtChA.Len()-1]==']')){ GetCh(); TxtChA.Trunc(TxtChA.Len()-2); break; } else { TxtChA+=Ch; GetCh(); } } } void TXmlLx::SkipWs(){ // [3] S ::= (#x20 | #x9 | #xD | #xA)+ while (ChDef.IsWs(Ch)){GetCh();} } TXmlLxSym TXmlLx::GetSym(){ if (Ch=='<'){ GetCh(); ClrArgV(); if (Ch=='?'){ GetCh(); TagNm=GetName(); if (TagNm.GetLc()=="xml"){Sym=xsyXmlDecl; GetXmlDecl();} else {Sym=xsyPI; GetPI();} } else if (Ch=='!'){ GetCh(); if (Ch=='['){ GetCh(); TagNm=GetName(); if (TagNm=="CDATA"){Sym=xsyQStr; GetCDSect();} else {EThrow(TStr("Invalid tag after ''){GetCh();} else {EThrow("Invalid End-Tag.");} } else { // xsySTag or xsySETag TagNm=GetName(); GetWs(false); while ((Ch!='>')&&(Ch!='/')){ TStr AttrNm=GetName(); GetEq(); TStr AttrVal=GetAttValue(); GetWs(false); AddArg(AttrNm, AttrVal); } if (Ch=='/'){ if (GetCh()=='>'){Sym=xsySETag; GetCh();} else {EThrow("Invalid Empty-Element-Tag.");} } else { Sym=xsySTag; GetCh(); } } if (Spacing==xspTruncate){SkipWs();} } else if (ChDef.IsWs(Ch)){ Sym=xsyWs; GetWs(true); ToNrSpacing(); if (Spacing==xspTruncate){GetSym();} } else if (Ch==TCh::EofCh){ Sym=xsyEof; } else { Sym=xsyStr; TxtChA.Clr(); // [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) forever { if (!ChDef.IsChar(Ch)){ EThrow(TUInt::GetStr(Ch, "Invalid character (%d)."));} // GetCh(); continue; // skip invalid characters if (Ch=='<'){break;} // tag if (Ch=='&'){GetCh(); TxtChA+=GetReference();} // reference else { if ((Ch=='>')&&(TxtChA.Len()>=2)&& (TxtChA[TxtChA.Len()-2]==']')&&(TxtChA[TxtChA.Len()-1]==']')){ EThrow("Forbidden substring ']]>' in character data.");} TxtChA+=Ch; GetCh(); // usual char } } ToNrSpacing(); } return Sym; } TStr TXmlLx::GetSymStr() const { TChA SymChA; switch (Sym){ case xsyUndef: SymChA="{Undef}"; break; case xsyWs: SymChA+="{Space:'"; SymChA+=TStr(TxtChA).GetHex(); SymChA+="'}"; break; case xsyComment: SymChA+=""; break; case xsyXmlDecl:{ SymChA+=""; break;} case xsyPI: SymChA+=""; break; case xsyDocTypeDecl: SymChA+=""; break; case xsySTag: case xsySETag:{ SymChA+="<"; SymChA+=TagNm; for (int ArgN=0; ArgN";} else if (Sym==xsySETag){SymChA+="/>";} else {Fail;} break;} case xsyETag: SymChA+=""; break; case xsyStr: SymChA="{String:'"; SymChA+=TxtChA; SymChA+="'}"; break; case xsyQStr: SymChA="{QString:'"; SymChA+=TxtChA; SymChA+="'}"; break; case xsyEof: SymChA="{Eof}"; break; default: Fail; } return SymChA; } void TXmlLx::EThrow(const TStr& MsgStr) const { TChA FPosChA; FPosChA+=" [File:"; FPosChA+=SIn->GetSNm(); FPosChA+=" Line:"; FPosChA+=TInt::GetStr(LnN); FPosChA+=" Char:"; FPosChA+=TInt::GetStr(LnChN); FPosChA+="]"; TStr FullMsgStr=MsgStr+FPosChA; TExcept::Throw(FullMsgStr); } TStr TXmlLx::GetFPosStr() const { TChA FPosChA; FPosChA+=" [File:"; FPosChA+=SIn->GetSNm(); FPosChA+=" Line:"; FPosChA+=TInt::GetStr(LnN); FPosChA+=" Char:"; FPosChA+=TInt::GetStr(LnChN); FPosChA+="]"; return FPosChA; } TStr TXmlLx::GetXmlLxSymStr(const TXmlLxSym& XmlLxSym){ switch (XmlLxSym){ case xsyUndef: return "Undef"; case xsyWs: return "White-Space"; case xsyComment: return "Comment"; case xsyXmlDecl: return "Declaration"; case xsyPI: return "PI"; case xsyDocTypeDecl: return "Document-Type"; case xsyElement: return "Element"; case xsyAttList: return "Attribute-List"; case xsyEntity: return "Entity"; case xsyNotation: return "Notation"; case xsyTag: return "Tag"; case xsySTag: return "Start-Tag"; case xsyETag: return "End-Tag"; case xsySETag: return "Start-End-Tag"; case xsyStr: return "String"; case xsyQStr: return "Quoted-String"; case xsyEof: return "Eon-Of-File"; default: return "Undef"; } } bool TXmlLx::IsTagNm(const TStr& Str){ TChA ChA=Str; if (ChA.Len()>0){ if (TXmlLx::ChDef.IsFirstNameCh(ChA[0])){ for (int ChN=1; ChN': XmlChA+=">"; break; default: XmlChA+=Ch; } } else if ((Ch=='\r')||(Ch=='\n')){ XmlChA+=Ch; } else { XmlChA+='&'; XmlChA+='#'; XmlChA+=TUInt::GetStr(Ch); XmlChA+=';'; } } return XmlChA; } TStr TXmlLx::GetXmlStrFromPlainStr(const TChA& PlainChA){ TChA XmlChA; for (int ChN=0; ChN': XmlChA+=">"; break; default: XmlChA+=Ch; } } else if ((Ch=='\r')||(Ch=='\n')){ XmlChA+=Ch; } else { XmlChA+='&'; XmlChA+='#'; XmlChA+=TUInt::GetStr(Ch); XmlChA+=';'; } } return XmlChA; } TStr TXmlLx::GetPlainStrFromXmlStr(const TStr& XmlStr){ TChA PlainChA; TChRet Ch(TStrIn::New(XmlStr)); Ch.GetCh(); while (!Ch.Eof()){ if (Ch()!='&'){ PlainChA+=Ch(); Ch.GetCh(); } else { // [67] Reference ::= EntityRef | CharRef if (Ch.GetCh()=='#'){ // [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' TChA RefChA; int RefCd=0; if (Ch.GetCh()=='x'){ // hex-decimal character code forever { Ch.GetCh(); if (TCh::IsHex(Ch())){ RefChA+=Ch(); RefCd=RefCd*16+TCh::GetHex(Ch()); } else { break; } } } else { // decimal character code forever { if (TCh::IsNum(Ch())){ RefChA+=Ch(); RefCd=RefCd*10+TCh::GetNum(Ch()); } else { break; } Ch.GetCh(); } } if ((!RefChA.Empty())&&(Ch()==';')){ Ch.GetCh(); uchar RefCh=uchar(RefCd); PlainChA+=RefCh; } } else { // [68] EntityRef ::= '&' Name ';' TChA EntityNm; while ((!Ch.Eof())&&(Ch()!=';')){ EntityNm+=Ch(); Ch.GetCh();} if ((!EntityNm.Empty())&&(Ch()==';')){ Ch.GetCh(); if (EntityNm=="quot"){PlainChA+='"';} else if (EntityNm=="amp"){PlainChA+='&';} else if (EntityNm=="apos"){PlainChA+='\'';} else if (EntityNm=="lt"){PlainChA+='<';} else if (EntityNm=="gt"){PlainChA+='>';} } } } } return PlainChA; } TStr TXmlLx::GetUsAsciiStrFromXmlStr(const TStr& XmlStr){ TStr UsAsciiStr=XmlStr; UsAsciiStr.ChangeStrAll("è", "c"); UsAsciiStr.ChangeStrAll("È", "C"); UsAsciiStr.ChangeStrAll("š", "s"); UsAsciiStr.ChangeStrAll("Š", "S"); UsAsciiStr.ChangeStrAll("ž", "z"); UsAsciiStr.ChangeStrAll("Ž", "Z"); TChA UsAsciiChA=TXmlLx::GetPlainStrFromXmlStr(UsAsciiStr); for (int ChN=0; ChNGetSym()==xsyTag)&&(SubTok->GetStr()==TagNm)){break;} else {SubTok=NULL;} } if ((SubTok.Empty())||(RestTagPath.Empty())){return SubTok;} else {return SubTok->GetTagTok(RestTagPath);} } } void TXmlTok::GetTagTokV(const TStr& TagPath, TXmlTokV& XmlTokV) const { XmlTokV.Clr(); TStr PreTagPath; TStr TagNm; TagPath.SplitOnLastCh(PreTagPath, '|', TagNm); PXmlTok Tok=GetTagTok(PreTagPath); if (!Tok.Empty()){ for (int SubTokN=0; SubTokNGetSubToks(); SubTokN++){ PXmlTok SubTok=Tok->GetSubTok(SubTokN); if ((SubTok->GetSym()==xsyTag)&&(SubTok->GetStr()==TagNm)){ XmlTokV.Add(SubTok);} } } } void TXmlTok::GetTagValV(const TStr& TagNm, const bool& XmlP, TStrV& ValV) const { if ((Sym==xsyTag)&&(Str==TagNm)){ ValV.Add(GetTokStr(XmlP)); } else { for (int SubTokN=0; SubTokNGetTagValV(TagNm, XmlP, ValV);} } } TStr TXmlTok::GetTagVal(const TStr& TagNm, const bool& XmlP) const { TStrV ValV; GetTagValV(TagNm, XmlP, ValV); if (ValV.Len()>0){return ValV[0];} else {return "";} } void TXmlTok::AddTokToChA(const bool& XmlP, TChA& ChA) const { switch (Sym){ case xsyWs: ChA+=Str; break; case xsyStr: if (XmlP){ChA+=TXmlLx::GetXmlStrFromPlainStr(Str);} else {ChA+=Str;} break; case xsyQStr: if (XmlP){ChA+="";} break; case xsyTag: if (XmlP){ ChA+='<'; ChA+=Str; for (int ArgN=0; ArgNAddTokToChA(XmlP, ChA);} if (XmlP){ChA+="';} } break; default: Fail; } } TStr TXmlTok::GetTokVStr(const TXmlTokV& TokV, const bool& XmlP){ TChA TokVChA; for (int TokN=0; TokN0){TokVChA+=' ';} TokVChA+=TokV[TokN]->GetTokStr(XmlP); } return TokVChA; } PXmlTok TXmlTok::GetTok(TXmlLx& Lx){ switch (Lx.Sym){ case xsyWs: case xsyStr: case xsyQStr: return TXmlTok::New(Lx.Sym, Lx.TxtChA); case xsySTag: case xsySETag: return TXmlTok::New(xsyTag, Lx.TagNm, Lx.ArgNmValKdV); default: Fail; return NULL; } } ///////////////////////////////////////////////// // Xml-Document void TXmlDoc::LoadTxtMiscStar(TXmlLx& Lx){ // [27] Misc ::= Comment | PI | S while ((Lx.Sym==xsyComment)||(Lx.Sym==xsyPI)||(Lx.Sym==xsyWs)){ Lx.GetSym();} } PXmlTok TXmlDoc::LoadTxtElement(TXmlLx& Lx){ // [39] element ::= EmptyElemTag | STag content ETag PXmlTok Tok; if (Lx.Sym==xsySETag){ Tok=TXmlTok::GetTok(Lx); } else if (Lx.Sym==xsySTag){ Tok=TXmlTok::GetTok(Lx); forever { Lx.GetSym(); if (Lx.Sym==xsyETag){ if (Tok->GetStr()==Lx.TagNm){ break; } else { TStr MsgStr=TStr("Invalid End-Tag '")+Lx.TagNm+ "' ('"+Tok->GetStr()+"' expected)."; Lx.EThrow(MsgStr); } } else { PXmlTok SubTok; switch (Lx.Sym){ case xsySTag: SubTok=LoadTxtElement(Lx); break; case xsySETag: case xsyStr: case xsyQStr: case xsyWs: SubTok=TXmlTok::GetTok(Lx); break; case xsyPI: case xsyComment: break; default: Lx.EThrow("Content or End-Tag expected."); } if (!SubTok.Empty()){ Tok->AddSubTok(SubTok);} } } } else if (Lx.Sym==xsyETag){ TStr MsgStr= TStr("Xml-Element (Start-Tag or Empty-Element-Tag) required.")+ TStr::GetStr(Lx.TagNm, " End-Tag encountered."); Lx.EThrow(MsgStr); } else { Lx.EThrow("Xml-Element (Start-Tag or Empty-Element-Tag) required."); } return Tok; } PXmlTok TXmlDoc::GetTagTok(const TStr& TagPath) const { if (TagPath.Empty()){ return Tok; } else { TStr TagNm; TStr RestTagPath; TagPath.SplitOnCh(TagNm, '|', RestTagPath); if ((Tok->GetSym()==xsyTag)&&(Tok->GetStr()==TagNm)){ if (RestTagPath.Empty()){return Tok;} else {return Tok->GetTagTok(RestTagPath);} } else { return NULL; } } } void TXmlDoc::PutTagTokStr(const TStr& TagPath, const TStr& TokStr) const { PXmlTok Tok=GetTagTok(TagPath); Tok->ClrSubTok(); PXmlTok StrTok=TXmlTok::New(xsyStr, TokStr); Tok->AddSubTok(StrTok); } void TXmlDoc::GetTagTokV(const TStr& TagPath, TXmlTokV& XmlTokV) const { XmlTokV.Clr(); TStr PreTagPath; TStr TagNm; TagPath.SplitOnLastCh(PreTagPath, '|', TagNm); PXmlTok Tok=GetTagTok(PreTagPath); if (!Tok.Empty()){ for (int SubTokN=0; SubTokNGetSubToks(); SubTokN++){ PXmlTok SubTok=Tok->GetSubTok(SubTokN); if ((SubTok->GetSym()==xsyTag)&&(SubTok->GetStr()==TagNm)){ XmlTokV.Add(SubTok);} } } } bool TXmlDoc::GetTagTokBoolArgVal( const TStr& TagPath, const TStr& ArgNm, const bool& DfVal) const { PXmlTok TagTok; if (IsTagTok(TagPath, TagTok)){ return TagTok->GetBoolArgVal(ArgNm, DfVal);} else {return DfVal;} } int TXmlDoc::GetTagTokIntArgVal( const TStr& TagPath, const TStr& ArgNm, const int& DfVal) const { PXmlTok TagTok; if (IsTagTok(TagPath, TagTok)){ return TagTok->GetIntArgVal(ArgNm, DfVal);} else {return DfVal;} } double TXmlDoc::GetTagTokFltArgVal( const TStr& TagPath, const TStr& ArgNm, const double& DfVal) const { PXmlTok TagTok; if (IsTagTok(TagPath, TagTok)){ return TagTok->GetFltArgVal(ArgNm, DfVal);} else {return DfVal;} } TStr TXmlDoc::GetTagTokStrArgVal( const TStr& TagPath, const TStr& ArgNm, const TStr& DfVal) const { PXmlTok TagTok; if (IsTagTok(TagPath, TagTok)){ return TagTok->GetStrArgVal(ArgNm, DfVal);} else {return DfVal;} } TStr TXmlDoc::GetXmlStr(const TStr& Str){ TChA ChA=Str; TChA XmlChA; for (int ChN=0; ChN'){XmlChA+="<";} else if (Ch=='<'){XmlChA+=">";} else if (Ch=='\''){XmlChA+="'";} else if (Ch=='\"'){XmlChA+=""";} else {XmlChA+=Ch;} } else { XmlChA+="&#"; XmlChA+=TUInt::GetStr(Ch); XmlChA+=";"; } } return XmlChA; } bool TXmlDoc::SkipTopTag(const PSIn& SIn){ bool Ok=true; TXmlLx Lx(SIn, xspIntact); try { Lx.GetSym(); // [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? if (Lx.Sym==xsyXmlDecl){Lx.GetSym();} LoadTxtMiscStar(Lx); if (Lx.Sym==xsyDocTypeDecl){Lx.GetSym();} LoadTxtMiscStar(Lx); Ok=true; } catch (PExcept Except){ Ok=false; } return Ok; } PXmlDoc TXmlDoc::LoadTxt(TXmlLx& Lx){ PXmlDoc Doc=TXmlDoc::New(); // [1] document ::= prolog element Misc* try { Lx.GetSym(); // [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? if (Lx.Sym==xsyXmlDecl){Lx.GetSym();} LoadTxtMiscStar(Lx); if (Lx.Sym==xsyDocTypeDecl){Lx.GetSym();} LoadTxtMiscStar(Lx); Doc->Tok=LoadTxtElement(Lx); LoadTxtMiscStar(Lx); Doc->Ok=true; Doc->MsgStr="Ok"; } catch (PExcept& Except){ Doc->Ok=false; Doc->MsgStr=Except->GetMsgStr(); } return Doc; } PXmlDoc TXmlDoc::LoadTxt(const PSIn& SIn, const TXmlSpacing& Spacing){ TXmlLx Lx(SIn, Spacing); return LoadTxt(Lx); } PXmlDoc TXmlDoc::LoadTxt(const TStr& FNm, const TXmlSpacing& Spacing){ PSIn SIn=TFIn::New(FNm); return LoadTxt(SIn, Spacing); } void TXmlDoc::LoadTxt( const TStr& FNm, TXmlDocV& XmlDocV, const TXmlSpacing& Spacing){ XmlDocV.Clr(); PSIn SIn=TFIn::New(FNm); TXmlLx Lx(SIn, Spacing); PXmlDoc XmlDoc; forever { Lx.SkipWs(); XmlDoc=LoadTxt(Lx); if (XmlDoc->IsOk()){XmlDocV.Add(XmlDoc);} else {break;} } } PXmlDoc TXmlDoc::LoadStr(const TStr& Str){ PSIn SIn=TStrIn::New(Str); return LoadTxt(SIn); } void TXmlDoc::SaveStr(TStr& Str){ PSOut SOut=TMOut::New(); TMOut& MOut=*(TMOut*)SOut(); SaveTxt(SOut); Str=MOut.GetAsStr(); } ///////////////////////////////////////////////// // Fast and dirty XML parser // very basic it does only string, no comments, no arguments TXmlLxSym TXmlParser::GetSym() { if (NextSym != xsyUndef) { Sym = NextSym; NextSym=xsyUndef; SymStr=NextSymStr; NextSymStr.Clr(); return Sym; } SymStr.Clr(); char Ch; while (TCh::IsWs(Ch=GetCh())) { } if (Ch == TCh::EofCh) { Sym = xsyEof; return xsyEof; } if (Ch == '<') { // load tag Ch = GetCh(); if (Ch == '/') { Sym = xsyETag; } else { Sym = xsySTag; SymStr.Push(Ch); } while((Ch=GetCh())!='>' && Ch!=TCh::EofCh) { SymStr.Push(Ch); } const int StrLen = SymStr.Len(); if (StrLen > 1 && SymStr[StrLen-1] == '/') { Sym = xsyETag; SymStr[StrLen-1] = 0; for (char *c = SymStr.CStr()+StrLen-2; TCh::IsWs(*c); c--) { *c=0; } } } else { // load string _SymStr.Clr(); _SymStr.Push(Ch); while (! RSIn.Eof() && RSIn.PeekCh() != '<') { _SymStr.Push(GetCh()); } GetPlainStrFromXmlStr(_SymStr, SymStr); Sym = xsyStr; } if (Ch == TCh::EofCh) { SymStr.Clr(); Sym = xsyEof; return xsyEof; } return Sym; } TXmlLxSym TXmlParser::GetSym(TChA& _SymStr) { GetSym(); _SymStr = SymStr; return Sym; } TXmlLxSym TXmlParser::PeekSym() { if (NextSym == xsyUndef) { const TXmlLxSym TmpSim=Sym; const TChA TmpSymStr=SymStr; NextSym=GetSym(NextSymStr); Sym=TmpSim; SymStr=TmpSymStr; } return NextSym; } TXmlLxSym TXmlParser::PeekSym(TChA& _SymStr) { PeekSym(); _SymStr = NextSymStr; return NextSym; } void TXmlParser::SkipTillTag(const TChA& _SymStr) { while(PeekSym() != xsyEof) { if (NextSymStr == _SymStr) { return; } GetSym(); } } // get value void TXmlParser::GetTagVal(const TChA& TagStr, TChA& TagVal) { EAssertR(GetTag(TagStr) == xsySTag, TStr::Fmt("Expected '<%s>'. Found '%s'", TagStr.CStr(), SymStr.CStr()).CStr()); EAssertR(GetSym(TagVal) == xsyStr, "Expected string tag."); EAssertR(GetTag(TagStr) == xsyETag, TStr::Fmt("Expected ''. Found '%s'", TagStr.CStr(), SymStr.CStr()).CStr()); } TXmlLxSym TXmlParser::GetTag(const TChA& TagStr) { GetSym(); EAssertR(TagStr==SymStr, TStr::Fmt("Expected xml symbol '%s'. Found '%s'", TagStr.CStr(), SymStr.CStr()).CStr()); return Sym; } void TXmlParser::GetPlainStrFromXmlStr(const TChA& XmlStr, TChA& PlainChA) { static TChA EntityNm; PlainChA.Clr(); const char *Ch = XmlStr.CStr(); while (*Ch){ if (*Ch!='&'){ PlainChA+=*Ch; Ch++; } else { if (*++Ch=='#'){ TChA RefChA; int RefCd=0; if (*++Ch=='x'){ forever { Ch++; if (TCh::IsHex(*Ch)){ RefChA+=*Ch; RefCd=RefCd*16+TCh::GetHex(*Ch); } else { break; } } } else { // decimal character code forever { if (TCh::IsNum(*Ch)){ RefChA+=*Ch; RefCd=RefCd*10+TCh::GetNum(*Ch); } else { break; } Ch++; } } if ((!RefChA.Empty())&&(*Ch==';')){ Ch++; const uchar RefCh=uchar(RefCd); PlainChA+=RefCh; } } else { EntityNm.Clr(); while ((*Ch)&&(*Ch!=';')){EntityNm+=*Ch; Ch++;} if ((!EntityNm.Empty())&&(*Ch==';')){ Ch++; if (EntityNm=="quot"){PlainChA+='"';} else if (EntityNm=="amp"){PlainChA+='&';} else if (EntityNm=="apos"){PlainChA+='\'';} else if (EntityNm=="lt"){PlainChA+='<';} else if (EntityNm=="gt"){PlainChA+='>';} } } } } }