Markup.cpp
上传用户:hawkcdm
上传日期:2013-02-10
资源大小:411k
文件大小:24k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. // Markup.cpp: implementation of the CMarkup class.
  2. //
  3. // Markup Release 6.1 Lite
  4. // Copyright (C) 1999-2001 First Objective Software, Inc. All rights reserved
  5. // This entire notice must be retained in this source code
  6. // Redistributing this source code requires written permission
  7. // This software is provided "as is", with no warranty.
  8. // Latest fixes enhancements and documentation at www.firstobject.com
  9. #include "stdafx.h"
  10. #include "afxconv.h"
  11. #include "Markup.h"
  12. #ifdef _DEBUG
  13. #undef THIS_FILE
  14. static char THIS_FILE[]=__FILE__;
  15. #define new DEBUG_NEW
  16. #endif
  17. void CMarkup::operator=( const CMarkup& markup )
  18. {
  19. m_iPos = markup.m_iPos;
  20. m_iPosChild = markup.m_iPosChild;
  21. m_iPosFree = markup.m_iPosFree;
  22. m_aPos.RemoveAll();
  23. m_aPos.Append( markup.m_aPos );
  24. m_nLevel = markup.m_nLevel;
  25. m_csDoc = markup.m_csDoc;
  26. }
  27. void CMarkup::ResetPos()
  28. {
  29. // Reset the main and child positions
  30. m_iPos = 0;
  31. m_iPosChild = 0;
  32. m_nLevel = 0;
  33. };
  34. bool CMarkup::SetDoc( LPCTSTR szDoc )
  35. {
  36. // Reset indexes
  37. m_iPosFree = 1;
  38. ResetPos();
  39. // Set document text
  40. if ( szDoc )
  41. m_csDoc = szDoc;
  42. else
  43. m_csDoc.Empty();
  44. // Starting size of position array: 1 element per 64 bytes of document
  45. // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc
  46. // Start at 8 when creating new document
  47. int nStartSize = m_csDoc.GetLength() / 64 + 8;
  48. if ( m_aPos.GetSize() < nStartSize )
  49. m_aPos.SetSize( nStartSize );
  50. // Parse document
  51. bool bWellFormed = false;
  52. if ( m_csDoc.GetLength() )
  53. {
  54. m_aPos[0].Clear();
  55. int iPos = x_ParseElem( 0 );
  56. if ( iPos > 0 )
  57. {
  58. m_aPos[0].iElemChild = iPos;
  59. bWellFormed = true;
  60. }
  61. }
  62. // Clear indexes if parse failed or empty document
  63. if ( ! bWellFormed )
  64. {
  65. m_aPos[0].Clear();
  66. m_iPosFree = 1;
  67. }
  68. ResetPos();
  69. return bWellFormed;
  70. };
  71. bool CMarkup::IsWellFormed()
  72. {
  73. if ( m_aPos.GetSize() && m_aPos[0].iElemChild )
  74. return TRUE;
  75. return FALSE;
  76. }
  77. bool CMarkup::FindElem( LPCTSTR szName )
  78. {
  79. // If szName is NULL or empty, go to next sibling element
  80. // Otherwise go to next sibling element with matching tag name
  81. // If the current position is valid, start looking from next
  82. // Change current position only if found
  83. //
  84. int iPos = m_iPos;
  85. if ( ! iPos )
  86. {
  87. if ( m_aPos.GetSize() )
  88. iPos = m_aPos[0].iElemChild;
  89. }
  90. else
  91. {
  92. iPos = m_aPos[iPos].iElemNext;
  93. }
  94. while ( iPos )
  95. {
  96. // Compare tag name unless szName is not specified
  97. if ( szName == NULL || !szName[0] || x_GetTagName(iPos) == szName )
  98. {
  99. // Assign new position
  100. m_iPos = iPos;
  101. m_iPosChild = 0;
  102. return true;
  103. }
  104. iPos = m_aPos[iPos].iElemNext;
  105. }
  106. return false;
  107. }
  108. bool CMarkup::FindChildElem( LPCTSTR szName )
  109. {
  110. // If szName is NULL or empty, go to next sibling child element
  111. // Otherwise go to next sibling child element with matching tag name
  112. // If the current child position is valid, start looking from next
  113. // Change current child position only if found
  114. //
  115. // Shorthand: call this with no current position means under root element
  116. if ( ! m_iPos )
  117. FindElem();
  118. // Is main position valid and not empty?
  119. if ( ! m_iPos || m_aPos[m_iPos].IsEmptyElement() )
  120. return false;
  121. // Is current child position valid?
  122. int iPosChild = m_iPosChild;
  123. if ( iPosChild )
  124. iPosChild = m_aPos[iPosChild].iElemNext;
  125. else
  126. iPosChild = m_aPos[m_iPos].iElemChild;
  127. // Search
  128. while ( iPosChild )
  129. {
  130. // Compare tag name unless szName is not specified
  131. if ( szName == NULL || !szName[0] || x_GetTagName(iPosChild) == szName )
  132. {
  133. // Assign new position
  134. m_iPosChild = iPosChild;
  135. return true;
  136. }
  137. iPosChild = m_aPos[iPosChild].iElemNext;
  138. }
  139. return false;
  140. }
  141. bool CMarkup::IntoElem()
  142. {
  143. // Find child element unless there is already a child element position
  144. if ( ! m_iPosChild )
  145. FindChildElem();
  146. if ( m_iPosChild )
  147. {
  148. m_iPos = m_iPosChild;
  149. m_iPosChild = 0;
  150. ++m_nLevel;
  151. return true;
  152. }
  153. return false;
  154. }
  155. bool CMarkup::OutOfElem()
  156. {
  157. // Go to parent element
  158. if ( m_iPos && m_nLevel > 0 )
  159. {
  160. m_iPosChild = m_iPos;
  161. m_iPos = m_aPos[m_iPos].iElemParent;
  162. --m_nLevel;
  163. return true;
  164. }
  165. return false;
  166. }
  167. bool CMarkup::GetOffsets( int& nStart, int& nEnd ) const
  168. {
  169. // Return document offsets of current main position element
  170. // This is not part of EDOM but is used by the Markup project
  171. if ( m_iPos )
  172. {
  173. nStart = m_aPos[m_iPos].nStartL;
  174. nEnd = m_aPos[m_iPos].nEndR;
  175. return true;
  176. }
  177. return false;
  178. }
  179. bool CMarkup::AddElem( LPCTSTR szName, LPCTSTR szValue )
  180. {
  181. // Add an element after current main position
  182. int iPosParent = m_iPos? m_aPos[m_iPos].iElemParent : 0;
  183. m_iPosChild = 0;
  184. // Setting root element?
  185. if ( iPosParent == 0 )
  186. {
  187. if ( IsWellFormed() )
  188. return false;
  189. m_csDoc.Empty();
  190. }
  191. m_iPos = x_Add( iPosParent, m_iPos, szName, szValue );
  192. return true;
  193. }
  194. bool CMarkup::AddChildElem( LPCTSTR szName, LPCTSTR szValue )
  195. {
  196. // Add a child element under main position, after current child position
  197. if ( ! m_iPos )
  198. return false;
  199. // If no child position, add after last sibling
  200. int iPosLast = m_aPos[m_iPos].iElemChild;
  201. if ( ! m_iPosChild && iPosLast )
  202. {
  203. m_iPosChild = iPosLast;
  204. while ( (iPosLast=m_aPos[iPosLast].iElemNext) != 0 )
  205. m_iPosChild = iPosLast;
  206. }
  207. m_iPosChild = x_Add( m_iPos, m_iPosChild, szName, szValue );
  208. return true;
  209. }
  210. bool CMarkup::AddAttrib( LPCTSTR szAttrib, LPCTSTR szValue )
  211. {
  212. // Add attribute to current main position element
  213. if ( m_iPos )
  214. {
  215. x_AddAttrib( m_iPos, szAttrib, szValue );
  216. return true;
  217. }
  218. return false;
  219. }
  220. bool CMarkup::AddChildAttrib( LPCTSTR szAttrib, LPCTSTR szValue )
  221. {
  222. // Add attribute to current child position element
  223. if ( m_iPosChild )
  224. {
  225. x_AddAttrib( m_iPosChild, szAttrib, szValue );
  226. return true;
  227. }
  228. return false;
  229. }
  230. //////////////////////////////////////////////////////////////////////
  231. // Private Methods
  232. //////////////////////////////////////////////////////////////////////
  233. int CMarkup::x_GetFreePos()
  234. {
  235. //
  236. // This returns the index of the next unused ElemPos in the array
  237. //
  238. if ( m_iPosFree == m_aPos.GetSize() )
  239. m_aPos.SetSize( m_iPosFree + m_iPosFree / 2 );
  240. ++m_iPosFree;
  241. return m_iPosFree - 1;
  242. }
  243. int CMarkup::x_ReleasePos()
  244. {
  245. //
  246. // This decrements the index of the next unused ElemPos in the array
  247. // allowing the element index returned by GetFreePos() to be reused
  248. //
  249. --m_iPosFree;
  250. return 0;
  251. }
  252. int CMarkup::x_ParseError( LPCTSTR szError, LPCTSTR szTag )
  253. {
  254. if ( szTag )
  255. m_csError.Format( szError, szTag );
  256. else
  257. m_csError = szError;
  258. x_ReleasePos();
  259. return -1;
  260. }
  261. int CMarkup::x_ParseElem( int iPosParent )
  262. {
  263. // This is either called by SetDoc, x_AddSubDoc, or itself recursively
  264. // m_aPos[iPosParent].nEndL is where to start parsing for the child element
  265. // This returns the new position if a tag is found, otherwise zero
  266. // In all cases we need to get a new ElemPos, but release it if unused
  267. //
  268. int iPos = x_GetFreePos();
  269. m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL;
  270. m_aPos[iPos].nNext = m_aPos[iPosParent].nStartR + 1;
  271. m_aPos[iPos].iElemParent = iPosParent;
  272. m_aPos[iPos].iElemChild = 0;
  273. m_aPos[iPos].iElemNext = 0;
  274. // Start Tag
  275. // A loop is used to ignore all remarks tags and special tags
  276. // i.e. <?xml version="1.0"?>, and <!-- comment here -->
  277. // So any tag beginning with ? or ! is ignored
  278. // Loop past ignored tags
  279. TokenPos token;
  280. token.nNext = m_aPos[iPosParent].nEndL;
  281. CString csName;
  282. while ( csName.IsEmpty() )
  283. {
  284. // Look for left angle bracket of start tag
  285. m_aPos[iPos].nStartL = token.nNext;
  286. if ( ! x_FindChar( m_aPos[iPos].nStartL, _T('<') ) )
  287. return x_ParseError( _T("Element tag not found") );
  288. // Set parent's End tag to start looking from here (or later)
  289. m_aPos[iPosParent].nEndL = m_aPos[iPos].nStartL;
  290. // Determine whether this is an element, comment or version tag
  291. LPCTSTR szEndOfTag = NULL;
  292. token.nNext = m_aPos[iPos].nStartL + 1;
  293. if ( x_FindToken( token ) )
  294. {
  295. if ( token.bIsString )
  296. return x_ParseError( _T("Tag starts with quote") );
  297. TCHAR cFirstChar = m_csDoc[token.nL];
  298. if ( cFirstChar == _T('?') )
  299. szEndOfTag = _T("?>"); // version
  300. else if ( cFirstChar == _T('!') )
  301. {
  302. TCHAR cSecondChar = 0;
  303. if ( token.nL+1 < m_csDoc.GetLength() )
  304. cSecondChar = m_csDoc[token.nL+1];
  305. if ( cSecondChar == _T('[') )
  306. szEndOfTag = _T("]]>"); // CDATA section
  307. else if ( cSecondChar == _T('-') )
  308. szEndOfTag = _T("-->"); // comment
  309. else
  310. szEndOfTag = _T(">"); // DTD
  311. }
  312. else if ( cFirstChar != _T('/') )
  313. {
  314. csName = x_GetToken( token );
  315. szEndOfTag = _T(">");
  316. }
  317. else
  318. return x_ReleasePos(); // probably end tag of parent
  319. }
  320. else
  321. return x_ParseError( _T("Abrupt end within tag") );
  322. // Look for end of tag
  323. token.nNext = m_csDoc.Find( szEndOfTag, token.nNext );
  324. if ( token.nNext == -1 )
  325. return x_ParseError( _T("End of tag not found") );
  326. }
  327. m_aPos[iPos].nStartR = token.nNext;
  328. // Is ending mark within start tag, i.e. empty element?
  329. if ( m_csDoc[m_aPos[iPos].nStartR-1] == _T('/') )
  330. {
  331. // Empty element
  332. // Close tag left is set to ending mark, and right to open tag right
  333. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR-1;
  334. m_aPos[iPos].nEndR = m_aPos[iPos].nStartR;
  335. }
  336. else // look for end tag
  337. {
  338. // Element probably has contents
  339. // Determine where to start looking for left angle bracket of end tag
  340. // This is done by recursively parsing the contents of this element
  341. int iInner, iInnerPrev = 0;
  342. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + 1;
  343. while ( (iInner = x_ParseElem( iPos )) > 0 )
  344. {
  345. // Set links to iInner
  346. if ( iInnerPrev )
  347. m_aPos[iInnerPrev].iElemNext = iInner;
  348. else
  349. m_aPos[iPos].iElemChild = iInner;
  350. iInnerPrev = iInner;
  351. // Set offset to reflect child
  352. m_aPos[iPos].nEndL = m_aPos[iInner].nEndR + 1;
  353. }
  354. if ( iInner == -1 )
  355. return -1;
  356. // Look for left angle bracket of end tag
  357. if ( ! x_FindChar( m_aPos[iPos].nEndL, _T('<') ) )
  358. return x_ParseError( _T("End tag of %s element not found"), csName );
  359. // Look through tokens of end tag
  360. token.nNext = m_aPos[iPos].nEndL + 1;
  361. int nTokenCount = 0;
  362. while ( x_FindToken( token ) )
  363. {
  364. ++nTokenCount;
  365. if ( ! token.bIsString )
  366. {
  367. // Is first token not an end slash mark?
  368. if ( nTokenCount == 1 && m_csDoc[token.nL] != _T('/') )
  369. return x_ParseError( _T("Expecting end tag of element %s"), csName );
  370. else if ( nTokenCount == 2 && csName != x_GetToken( token ) )
  371. return x_ParseError( _T("End tag does not correspond to %s"), csName );
  372. // Else is it a right angle bracket?
  373. else if ( m_csDoc[token.nL] == _T('>') )
  374. break;
  375. }
  376. }
  377. // Was a right angle bracket not found?
  378. if ( ! token.IsValid() || nTokenCount < 2 )
  379. return x_ParseError( _T("End tag not completed for element %s"), csName );
  380. m_aPos[iPos].nEndR = token.nL;
  381. }
  382. // Successfully found positions of angle brackets
  383. m_aPos[iPos].nNext = m_aPos[iPos].nEndR;
  384. x_FindChar( m_aPos[iPos].nNext, _T('<') );
  385. return iPos;
  386. }
  387. bool CMarkup::x_FindChar( int&n, _TCHAR c ) const
  388. {
  389. // Look for char c starting at n, and set n to point to it
  390. // c is always the first char of a multi-byte char
  391. // Return false if not found before end of document
  392. LPCTSTR szDoc = (LPCTSTR)m_csDoc;
  393. while ( szDoc[n] && szDoc[n] != c )
  394. n += _tclen( &szDoc[n] );
  395. if ( ! szDoc[n] )
  396. return false;
  397. return true;
  398. }
  399. bool CMarkup::x_FindToken( CMarkup::TokenPos& token ) const
  400. {
  401. // Starting at token.nNext, find the next token
  402. // upon successful return, token.nNext points after the retrieved token
  403. LPCTSTR szDoc = (LPCTSTR)m_csDoc;
  404. int n = token.nNext;
  405. // Statically defined CStrings for whitespace and special chars
  406. static CString csWhitespace = _T(" tnr");
  407. static CString csSpecial = _T("<>=\/?!");
  408. // By-pass leading whitespace
  409. while ( szDoc[n] && csWhitespace.Find(szDoc[n]) > -1 )
  410. ++n;
  411. // Are we still within the document?
  412. token.bIsString = false;
  413. if ( szDoc[n] )
  414. {
  415. // Is it an opening quote?
  416. if ( szDoc[n] == _T('"') )
  417. {
  418. // Move past opening quote
  419. ++n;
  420. token.nL = n;
  421. // Look for closing quote
  422. x_FindChar( n, _T('"') );
  423. // Set right to before closing quote
  424. token.nR = n-1;
  425. // Set n past closing quote unless at end of document
  426. if ( szDoc[n] )
  427. ++n;
  428. // Set flag
  429. token.bIsString = true;
  430. }
  431. else
  432. {
  433. // Go until special char or whitespace
  434. token.nL = n;
  435. while ( szDoc[n] &&
  436. csSpecial.Find(m_csDoc[n]) == -1 &&
  437. csWhitespace.Find(m_csDoc[n]) == -1
  438. )
  439. n += _tclen(&szDoc[n]);
  440. // Adjust end position if it is one special char
  441. if ( n == token.nL )
  442. ++n; // it is a special char
  443. token.nR = n-1;
  444. }
  445. }
  446. token.nNext = n;
  447. if ( ! szDoc[n] )
  448. return false;
  449. // nNext points to one past last char of token
  450. return true;
  451. }
  452. CString CMarkup::x_GetToken( const CMarkup::TokenPos& token ) const
  453. {
  454. // The token contains indexes into the document identifying a small substring
  455. // Build the substring from those indexes and return it
  456. if ( ! token.IsValid() )
  457. return _T("");
  458. return m_csDoc.Mid( token.nL,
  459. token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) );
  460. }
  461. CString CMarkup::x_GetTagName( int iPos ) const
  462. {
  463. // Return the tag name at specified element
  464. TokenPos token;
  465. token.nNext = m_aPos[iPos].nStartL + 1;
  466. if ( ! iPos || ! x_FindToken( token ) )
  467. return _T("");
  468. // Return substring of document
  469. return x_GetToken( token );
  470. }
  471. bool CMarkup::x_FindAttrib( CMarkup::TokenPos& token, LPCTSTR szAttrib ) const
  472. {
  473. // If szAttrib is NULL find next attrib, otherwise find named attrib
  474. // Return true if found
  475. int nAttrib = 0;
  476. for ( int nCount = 0; x_FindToken(token); ++nCount )
  477. {
  478. if ( ! token.bIsString )
  479. {
  480. // Is it the right angle bracket?
  481. if ( m_csDoc[token.nL] == _T('>') || m_csDoc[token.nL] == _T('/') )
  482. break; // attrib not found
  483. // Equal sign
  484. if ( m_csDoc[token.nL] == _T('=') )
  485. continue;
  486. // Potential attribute
  487. if ( ! nAttrib && nCount )
  488. {
  489. // Attribute name search?
  490. if ( ! szAttrib || ! szAttrib[0] )
  491. return true; // return with token at attrib name
  492. // Compare szAttrib
  493. if ( x_GetToken(token) == szAttrib )
  494. nAttrib = nCount;
  495. }
  496. }
  497. else if ( nAttrib && nCount == nAttrib + 2 )
  498. {
  499. return true;
  500. }
  501. }
  502. // Not found
  503. return false;
  504. }
  505. CString CMarkup::x_GetAttrib( int iPos, LPCTSTR szAttrib ) const
  506. {
  507. // Return the value of the attrib at specified element
  508. TokenPos token;
  509. token.nNext = m_aPos[iPos].nStartL + 1;
  510. if ( szAttrib && x_FindAttrib( token, szAttrib ) )
  511. return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) );
  512. return _T("");
  513. }
  514. CString CMarkup::x_GetData( int iPos ) const
  515. {
  516. // Return a string representing data between start and end tag
  517. // Return empty string if there are any children elements
  518. if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() )
  519. {
  520. // See if it is a CDATA section
  521. TokenPos token;
  522. token.nNext = m_aPos[iPos].nStartR+1;
  523. if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<')
  524. && token.nL + 11 < m_aPos[iPos].nEndL
  525. && _tcsncmp( &((LPCTSTR)m_csDoc)[token.nL+1], _T("![CDATA["), 8 ) == 0 )
  526. {
  527. int nEndCDATA = m_csDoc.Find( _T("]]>"), token.nNext );
  528. if ( nEndCDATA != -1 && nEndCDATA < m_aPos[iPos].nEndL )
  529. {
  530. return m_csDoc.Mid( token.nL+9, nEndCDATA-token.nL-9 );
  531. }
  532. }
  533. return x_TextFromDoc( m_aPos[iPos].nStartR+1, m_aPos[iPos].nEndL-1 );
  534. }
  535. return "";
  536. }
  537. CString CMarkup::x_TextToDoc( LPCTSTR szText, bool bAttrib ) const
  538. {
  539. // Convert text as seen outside XML document to XML friendly
  540. // replacing special characters with ampersand escape codes
  541. // E.g. convert "6>7" to "6&gt;7"
  542. //
  543. // &lt;   less than
  544. // &amp;  ampersand
  545. // &gt;   greater than
  546. //
  547. // and for attributes:
  548. //
  549. // &apos; apostrophe or single quote
  550. // &quot; double quote
  551. //
  552. static _TCHAR* szaReplace[] = { _T("&lt;"),_T("&amp;"),_T("&gt;"),_T("&apos;"),_T("&quot;") };
  553. const _TCHAR* pFind = bAttrib?_T("<&>'""):_T("<&>");
  554. CString csText;
  555. const _TCHAR* pSource = szText;
  556. int nDestSize = _tcslen(pSource);
  557. nDestSize += nDestSize / 10 + 7;
  558. _TCHAR* pDest = csText.GetBuffer(nDestSize);
  559. int nLen = 0;
  560. _TCHAR cSource = *pSource;
  561. _TCHAR* pFound;
  562. while ( cSource )
  563. {
  564. if ( nLen > nDestSize - 6 )
  565. {
  566. csText.ReleaseBuffer(nLen);
  567. nDestSize *= 2;
  568. pDest = csText.GetBuffer(nDestSize);
  569. }
  570. if ( (pFound=_tcschr(pFind,cSource)) != NULL )
  571. {
  572. pFound = szaReplace[pFound-pFind];
  573. _tcscpy(&pDest[nLen],pFound);
  574. nLen += _tcslen(pFound);
  575. }
  576. else
  577. {
  578. _tccpy( &pDest[nLen], pSource );
  579. ++nLen;
  580. }
  581. pSource += _tclen( pSource );
  582. cSource = *pSource;
  583. }
  584. csText.ReleaseBuffer(nLen);
  585. return csText;
  586. }
  587. CString CMarkup::x_TextFromDoc( int nLeft, int nRight ) const
  588. {
  589. // Convert XML friendly text to text as seen outside XML document
  590. // replacing ampersand escape codes with special characters
  591. // E.g. convert "6&gt;7" to "6>7"
  592. //
  593. // Conveniently the result is always the same or shorter in length
  594. //
  595. static _TCHAR* szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") };
  596. static int anCodeLen[] = { 3,4,3,5,5 };
  597. static _TCHAR* szSymbol = _T("<&>'"");
  598. CString csText;
  599. const _TCHAR* pSource = m_csDoc;
  600. int nDestSize = nRight - nLeft + 1;
  601. _TCHAR* pDest = csText.GetBuffer(nDestSize);
  602. int nLen = 0;
  603. int nCharLen;
  604. int nChar = nLeft;
  605. while ( nChar <= nRight )
  606. {
  607. if ( pSource[nChar] == _T('&') )
  608. {
  609. // Look for matching &code;
  610. for ( int nMatch = 0; nMatch < 5; ++nMatch )
  611. {
  612. if ( nChar <= nRight - anCodeLen[nMatch]
  613. && _tcsncmp(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 )
  614. {
  615. pDest[nLen++] = szSymbol[nMatch];
  616. nChar += anCodeLen[nMatch] + 1;
  617. break;
  618. }
  619. }
  620. // If no match is found it means XML doc is invalid
  621. // no devastating harm done, ampersand code will just be left in result
  622. if ( nMatch == 5 )
  623. {
  624. pDest[nLen++] = _T('&');
  625. ++nChar;
  626. }
  627. }
  628. else
  629. {
  630. nCharLen = _tclen(&pSource[nChar]);
  631. _tccpy( &pDest[nLen], &pSource[nChar] );
  632. nLen += nCharLen;
  633. nChar += nCharLen;
  634. }
  635. }
  636. csText.ReleaseBuffer(nLen);
  637. return csText;
  638. }
  639. void CMarkup::x_DocChange( int nLeft, int nReplace, const CString& csInsert )
  640. {
  641. // Insert csInsert int m_csDoc at nLeft replacing nReplace chars
  642. // Do this with only one buffer reallocation if it grows
  643. //
  644. int nDocLength = m_csDoc.GetLength();
  645. int nInsLength = csInsert.GetLength();
  646. // Make sure nLeft and nReplace are within bounds
  647. nLeft = max( 0, min( nLeft, nDocLength ) );
  648. nReplace = max( 0, min( nReplace, nDocLength-nLeft ) );
  649. // Get pointer to buffer with enough room
  650. int nNewLength = nInsLength + nDocLength - nReplace;
  651. int nBufferLen = nNewLength;
  652. _TCHAR* pDoc = m_csDoc.GetBuffer( nBufferLen );
  653. // Move part of old doc that goes after insert
  654. if ( nLeft+nReplace < nDocLength )
  655. memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
  656. // Copy insert
  657. memcpy( &pDoc[nLeft], csInsert, nInsLength*sizeof(_TCHAR) );
  658. // Release
  659. m_csDoc.ReleaseBuffer( nNewLength );
  660. }
  661. void CMarkup::x_Adjust( int iPos, int nShift )
  662. {
  663. // Loop through affected elements and adjust indexes
  664. // Does not affect iPos itself
  665. // Algorithm:
  666. // 1. update next siblings and all their children
  667. // 2. then go up a level update end points and to step 1
  668. int iPosTop = m_aPos[iPos].iElemParent;
  669. while ( iPos )
  670. {
  671. // Were we at containing parent of affected position?
  672. bool bPosTop = false;
  673. if ( iPos == iPosTop )
  674. {
  675. // Move iPosTop up one towards root
  676. iPosTop = m_aPos[iPos].iElemParent;
  677. bPosTop = true;
  678. }
  679. // Traverse to the next update position
  680. if ( ! bPosTop && m_aPos[iPos].iElemChild )
  681. {
  682. // Depth first
  683. iPos = m_aPos[iPos].iElemChild;
  684. }
  685. else if ( m_aPos[iPos].iElemNext )
  686. {
  687. iPos = m_aPos[iPos].iElemNext;
  688. }
  689. else
  690. {
  691. // Look for next sibling of a parent of iPos
  692. // When going back up, parents have already been done except iPosTop
  693. while ( (iPos=m_aPos[iPos].iElemParent) != 0 && iPos != iPosTop )
  694. if ( m_aPos[iPos].iElemNext )
  695. {
  696. iPos = m_aPos[iPos].iElemNext;
  697. break;
  698. }
  699. }
  700. // Shift indexes at iPos
  701. if ( iPos != iPosTop )
  702. {
  703. // Move the start tag indexes
  704. // Don't do this for containing parent tag
  705. m_aPos[iPos].nStartL += nShift;
  706. m_aPos[iPos].nStartR += nShift;
  707. }
  708. // Move end tag indexes
  709. m_aPos[iPos].nEndL += nShift;
  710. m_aPos[iPos].nEndR += nShift;
  711. m_aPos[iPos].nNext += nShift;
  712. }
  713. }
  714. int CMarkup::x_Add( int iPosParent, int iPosBefore, LPCTSTR szName, LPCTSTR szValue )
  715. {
  716. // Create element and modify positions of affected elements
  717. // if iPosBefore is NULL, insert as first element under parent
  718. // If no szValue is specified, an empty element is created
  719. // i.e. either <NAME>value</NAME> or <NAME/>
  720. //
  721. int iPos = x_GetFreePos();
  722. bool bEmptyParent = false;
  723. if ( iPosBefore )
  724. {
  725. // Follow iPosBefore
  726. m_aPos[iPos].nStartL = m_aPos[iPosBefore].nNext;
  727. }
  728. else if ( m_aPos[iPosParent].iElemChild )
  729. {
  730. // Insert before first child of parent
  731. m_aPos[iPos].nStartL = m_aPos[m_aPos[iPosParent].iElemChild].nStartL;
  732. }
  733. else if ( m_aPos[iPosParent].IsEmptyElement() )
  734. {
  735. // Parent has no separate end tag
  736. m_aPos[iPos].nStartL = m_aPos[iPosParent].nStartR + 2;
  737. bEmptyParent = true;
  738. }
  739. else
  740. {
  741. // Parent has content, but no children
  742. m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL;
  743. }
  744. // Set links
  745. m_aPos[iPos].iElemParent = iPosParent;
  746. m_aPos[iPos].iElemChild = 0;
  747. if ( iPosBefore )
  748. {
  749. m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
  750. m_aPos[iPosBefore].iElemNext = iPos;
  751. }
  752. else
  753. {
  754. m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
  755. m_aPos[iPosParent].iElemChild = iPos;
  756. }
  757. // Create string for insert
  758. CString csInsert;
  759. int nLenName = _tcslen(szName);
  760. int nLenValue = szValue? _tcslen(szValue) : 0;
  761. if ( ! nLenValue )
  762. {
  763. // <NAME/> empty element
  764. csInsert.Format( _T("<%s/>rn"), szName );
  765. m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 2;
  766. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR - 1;
  767. m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + 1;
  768. m_aPos[iPos].nNext = m_aPos[iPos].nEndR + 3;
  769. }
  770. else
  771. {
  772. // <NAME>value</NAME>
  773. CString csValue = x_TextToDoc( szValue );
  774. nLenValue = csValue.GetLength();
  775. csInsert.Format( _T("<%s>%s</%s>rn"), szName, csValue, szName );
  776. m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 1;
  777. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + nLenValue + 1;
  778. m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + nLenName + 2;
  779. m_aPos[iPos].nNext = m_aPos[iPos].nEndR + 3;
  780. }
  781. // Insert
  782. int nReplace = 0, nLeft = m_aPos[iPos].nStartL;
  783. if ( bEmptyParent )
  784. {
  785. CString csParentTagName = x_GetTagName(iPosParent);
  786. csInsert = _T(">rn") + csInsert + _T("</") + csParentTagName;
  787. nLeft -= 3;
  788. nReplace = 1;
  789. // x_Adjust is going to update all affected indexes by one amount
  790. // This will satisfy all except the empty parent
  791. // Here we pre-adjust for the empty parent
  792. // The empty tag slash is removed
  793. m_aPos[iPosParent].nStartR -= 1;
  794. // For the newly created end tag, see the following example:
  795. // <A/> (len 4) becomes <A><B/></A> (len 11)
  796. // In x_Adjust everything will be adjusted 11 - 4 = 7
  797. // But the nEndL of element A should only be adjusted 5
  798. m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
  799. }
  800. x_DocChange( nLeft, nReplace, csInsert );
  801. x_Adjust( iPos, csInsert.GetLength() - nReplace );
  802. // Return the index of the new element
  803. return iPos;
  804. }
  805. int CMarkup::x_AddAttrib( int iPos, LPCTSTR szAttrib, LPCTSTR szValue )
  806. {
  807. // Add attribute to iPos element
  808. CString csInsert;
  809. if ( iPos )
  810. {
  811. // Insert string taking into account whether it is a single tag
  812. CString csValue = x_TextToDoc( szValue, true );
  813. csInsert.Format( _T(" %s="%s""), szAttrib, csValue );
  814. int nL = m_aPos[iPos].nStartR - (m_aPos[iPos].IsEmptyElement()?1:0);
  815. x_DocChange( nL, 0, csInsert );
  816. int nLen = csInsert.GetLength();
  817. m_aPos[iPos].nStartR += nLen;
  818. m_aPos[iPos].nEndL += nLen;
  819. m_aPos[iPos].nEndR += nLen;
  820. m_aPos[iPos].nNext += nLen;
  821. x_Adjust( iPos, nLen );
  822. }
  823. return csInsert.GetLength();
  824. }