Markup.cpp
上传用户:yokoluohf
上传日期:2013-02-25
资源大小:769k
文件大小:37k
源码类别:

GIS编程

开发平台:

Visual C++

  1. // Markup.cpp: implementation of the CMarkup class.
  2. //
  3. // Markup Release 6.3
  4. // Copyright (C) 1999-2002 First Objective Software, Inc. All rights reserved
  5. // Go to www.firstobject.com for the latest CMarkup and EDOM documentation
  6. // Use in commercial applications requires written permission
  7. // This software is provided "as is", with no warranty.
  8. #include "stdafx.h"
  9. #include "afxconv.h"
  10. #include "Markup.h"
  11. #ifdef _DEBUG
  12. #undef THIS_FILE
  13. static char THIS_FILE[]=__FILE__;
  14. #define new DEBUG_NEW
  15. #endif
  16. #ifdef _MBCS
  17. #pragma message( "Note: MBCS build (not UTF-8)" )
  18. // For UTF-8, remove _MBCS from project settings C/C++ preprocessor definitions
  19. #endif
  20. // Defines for Windows CE
  21. #ifndef _tclen
  22. #define _tclen(p) 1
  23. #define _tccpy(p1,p2) *(p1)=*(p2)
  24. #endif
  25. void CMarkup::operator=( const CMarkup& markup )
  26. {
  27. m_iPosParent = markup.m_iPosParent;
  28. m_iPos = markup.m_iPos;
  29. m_iPosChild = markup.m_iPosChild;
  30. m_iPosFree = markup.m_iPosFree;
  31. m_nNodeType = markup.m_nNodeType;
  32. m_aPos.RemoveAll();
  33. m_aPos.Append( markup.m_aPos );
  34. m_csDoc = markup.m_csDoc;
  35. MARKUP_SETDEBUGSTATE;
  36. }
  37. bool CMarkup::SetDoc( LPCTSTR szDoc )
  38. {
  39. // Reset indexes
  40. m_iPosFree = 1;
  41. ResetPos();
  42. m_mapSavedPos.RemoveAll();
  43. // Set document text
  44. if ( szDoc )
  45. m_csDoc = szDoc;
  46. else
  47. m_csDoc.Empty();
  48. // Starting size of position array: 1 element per 64 bytes of document
  49. // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc
  50. // Start at 8 when creating new document
  51. int nStartSize = m_csDoc.GetLength() / 64 + 8;
  52. if ( m_aPos.GetSize() < nStartSize )
  53. m_aPos.SetSize( nStartSize );
  54. // Parse document
  55. bool bWellFormed = false;
  56. if ( m_csDoc.GetLength() )
  57. {
  58. m_aPos[0].Clear();
  59. int iPos = x_ParseElem( 0 );
  60. if ( iPos > 0 )
  61. {
  62. m_aPos[0].iElemChild = iPos;
  63. bWellFormed = true;
  64. }
  65. }
  66. // Clear indexes if parse failed or empty document
  67. if ( ! bWellFormed )
  68. {
  69. m_aPos[0].Clear();
  70. m_iPosFree = 1;
  71. }
  72. ResetPos();
  73. return bWellFormed;
  74. };
  75. bool CMarkup::IsWellFormed()
  76. {
  77. if ( m_aPos.GetSize() && m_aPos[0].iElemChild )
  78. return true;
  79. return false;
  80. }
  81. bool CMarkup::Load( LPCTSTR szFileName )
  82. {
  83. CString csDoc;
  84. CFile file;
  85. if ( ! file.Open(szFileName,CFile::modeRead) )
  86. return false;
  87. int nLength = file.GetLength();
  88. #if defined(_UNICODE)
  89. // Allocate Buffer for UTF-8 file data
  90. unsigned char* pBuffer = new unsigned char[nLength + 1];
  91. nLength = file.Read( pBuffer, nLength );
  92. pBuffer[nLength] = '';
  93. // Convert file from UTF-8 to Windows UNICODE (AKA UCS-2)
  94. int nWideLength = MultiByteToWideChar(CP_UTF8,0,(const char*)pBuffer,nLength,NULL,0);
  95. nLength = MultiByteToWideChar(CP_UTF8,0,(const char*)pBuffer,nLength,
  96. csDoc.GetBuffer(nWideLength),nWideLength);
  97. ASSERT( nLength == nWideLength );
  98. delete [] pBuffer;
  99. #else
  100. nLength = file.Read( csDoc.GetBuffer(nLength), nLength );
  101. #endif
  102. csDoc.ReleaseBuffer(nLength);
  103. file.Close();
  104. return SetDoc( csDoc );
  105. }
  106. bool CMarkup::Save( LPCTSTR szFileName )
  107. {
  108. int nLength = m_csDoc.GetLength();
  109. CFile file;
  110. if ( ! file.Open(szFileName,CFile::modeWrite|CFile::modeCreate) )
  111. return false;
  112. #if defined( _UNICODE )
  113. int nUTF8Len = WideCharToMultiByte(CP_UTF8,0,m_csDoc,nLength,NULL,0,NULL,NULL);
  114. char* pBuffer = new char[nUTF8Len+1];
  115. nLength = WideCharToMultiByte(CP_UTF8,0,m_csDoc,nLength,pBuffer,nUTF8Len+1,NULL,NULL);
  116. file.Write( pBuffer, nLength );
  117. delete pBuffer;
  118. #else
  119. file.Write( (LPCTSTR)m_csDoc, nLength );
  120. #endif
  121. file.Close();
  122. return true;
  123. }
  124. bool CMarkup::FindElem( LPCTSTR szName )
  125. {
  126. // Change current position only if found
  127. //
  128. if ( m_aPos.GetSize() )
  129. {
  130. int iPos = x_FindElem( m_iPosParent, m_iPos, szName );
  131. if ( iPos )
  132. {
  133. // Assign new position
  134. x_SetPos( m_aPos[iPos].iElemParent, iPos, 0 );
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. bool CMarkup::FindChildElem( LPCTSTR szName )
  141. {
  142. // Change current child position only if found
  143. //
  144. // Shorthand: call this with no current main position
  145. // means find child under root element
  146. if ( ! m_iPos )
  147. FindElem();
  148. int iPosChild = x_FindElem( m_iPos, m_iPosChild, szName );
  149. if ( iPosChild )
  150. {
  151. // Assign new position
  152. int iPos = m_aPos[iPosChild].iElemParent;
  153. x_SetPos( m_aPos[iPos].iElemParent, iPos, iPosChild );
  154. return true;
  155. }
  156. return false;
  157. }
  158. CString CMarkup::GetTagName() const
  159. {
  160. // Return the tag name at the current main position
  161. CString csTagName;
  162. if ( m_iPos )
  163. csTagName = x_GetTagName( m_iPos );
  164. return csTagName;
  165. }
  166. bool CMarkup::IntoElem()
  167. {
  168. // If there is no child position and IntoElem is called it will succeed in release 6.3
  169. // (A subsequent call to FindElem will find the first element)
  170. // The following short-hand behavior was never part of EDOM and was misleading
  171. // It would find a child element if there was no current child element position and go into it
  172. // It is removed in release 6.3, this change is NOT backwards compatible!
  173. // if ( ! m_iPosChild )
  174. // FindChildElem();
  175. if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  176. {
  177. x_SetPos( m_iPos, m_iPosChild, 0 );
  178. return true;
  179. }
  180. return false;
  181. }
  182. bool CMarkup::OutOfElem()
  183. {
  184. // Go to parent element
  185. if ( m_iPosParent )
  186. {
  187. x_SetPos( m_aPos[m_iPosParent].iElemParent, m_iPosParent, m_iPos );
  188. return true;
  189. }
  190. return false;
  191. }
  192. CString CMarkup::GetAttrName( int n ) const
  193. {
  194. // Return nth Attrute name of main position
  195. if ( ! m_iPos || m_nNodeType != MNT_ELEMENT )
  196. return _T("");
  197. TokenPos token( m_csDoc );
  198. token.nNext = m_aPos[m_iPos].nStartL + 1;
  199. for ( int nAttr=0; nAttr<=n; ++nAttr )
  200. if ( ! x_FindAttr(token) )
  201. return _T("");
  202. // Return substring of document
  203. return x_GetToken( token );
  204. }
  205. bool CMarkup::SavePos( LPCTSTR szPosName )
  206. {
  207. // Save current element position in saved position map
  208. if ( szPosName )
  209. {
  210. SavedPos savedpos;
  211. savedpos.iPosParent = m_iPosParent;
  212. savedpos.iPos = m_iPos;
  213. savedpos.iPosChild = m_iPosChild;
  214. m_mapSavedPos.SetAt( szPosName, savedpos );
  215. return true;
  216. }
  217. return false;
  218. }
  219. bool CMarkup::RestorePos( LPCTSTR szPosName )
  220. {
  221. // Restore element position if found in saved position map
  222. SavedPos savedpos;
  223. if ( szPosName && m_mapSavedPos.Lookup( szPosName, savedpos ) )
  224. {
  225. x_SetPos( savedpos.iPosParent, savedpos.iPos, savedpos.iPosChild );
  226. return true;
  227. }
  228. return false;
  229. }
  230. bool CMarkup::GetOffsets( int& nStart, int& nEnd ) const
  231. {
  232. // Return document offsets of current main position element
  233. // This is not part of EDOM but is used by the Markup project
  234. if ( m_iPos )
  235. {
  236. nStart = m_aPos[m_iPos].nStartL;
  237. nEnd = m_aPos[m_iPos].nEndR;
  238. return true;
  239. }
  240. return false;
  241. }
  242. CString CMarkup::GetChildSubDoc() const
  243. {
  244. if ( m_iPosChild )
  245. {
  246. int nL = m_aPos[m_iPosChild].nStartL;
  247. int nR = m_aPos[m_iPosChild].nEndR + 1;
  248. TokenPos token( m_csDoc );
  249. token.nNext = nR;
  250. if ( ! x_FindToken(token) || m_csDoc[token.nL] == _T('<') )
  251. nR = token.nL;
  252. return m_csDoc.Mid( nL, nR - nL );
  253. }
  254. return _T("");
  255. }
  256. bool CMarkup::RemoveElem()
  257. {
  258. // Remove current main position element
  259. if ( m_iPos && m_nNodeType == MNT_ELEMENT )
  260. {
  261. int iPos = x_RemoveElem( m_iPos );
  262. x_SetPos( m_iPosParent, iPos, 0 );
  263. return true;
  264. }
  265. return false;
  266. }
  267. bool CMarkup::RemoveChildElem()
  268. {
  269. // Remove current child position element
  270. if ( m_iPosChild )
  271. {
  272. int iPosChild = x_RemoveElem( m_iPosChild );
  273. x_SetPos( m_iPosParent, m_iPos, iPosChild );
  274. return true;
  275. }
  276. return false;
  277. }
  278. //////////////////////////////////////////////////////////////////////
  279. // Private Methods
  280. //////////////////////////////////////////////////////////////////////
  281. int CMarkup::x_GetFreePos()
  282. {
  283. //
  284. // This returns the index of the next unused ElemPos in the array
  285. //
  286. if ( m_iPosFree == m_aPos.GetSize() )
  287. m_aPos.SetSize( m_iPosFree + m_iPosFree / 2 );
  288. ++m_iPosFree;
  289. return m_iPosFree - 1;
  290. }
  291. int CMarkup::x_ReleasePos()
  292. {
  293. //
  294. // This decrements the index of the next unused ElemPos in the array
  295. // allowing the element index returned by GetFreePos() to be reused
  296. //
  297. --m_iPosFree;
  298. return 0;
  299. }
  300. int CMarkup::x_ParseError( LPCTSTR szError, LPCTSTR szName )
  301. {
  302. if ( szName )
  303. m_csError.Format( szError, szName );
  304. else
  305. m_csError = szError;
  306. x_ReleasePos();
  307. return -1;
  308. }
  309. int CMarkup::x_ParseElem( int iPosParent )
  310. {
  311. // This is either called by SetDoc, x_AddSubDoc, or itself recursively
  312. // m_aPos[iPosParent].nEndL is where to start parsing for the child element
  313. // This returns the new position if a tag is found, otherwise zero
  314. // In all cases we need to get a new ElemPos, but release it if unused
  315. //
  316. int iPos = x_GetFreePos();
  317. m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL;
  318. m_aPos[iPos].iElemParent = iPosParent;
  319. m_aPos[iPos].iElemChild = 0;
  320. m_aPos[iPos].iElemNext = 0;
  321. // Start Tag
  322. // A loop is used to ignore all remarks tags and special tags
  323. // i.e. <?xml version="1.0"?>, and <!-- comment here -->
  324. // So any tag beginning with ? or ! is ignored
  325. // Loop past ignored tags
  326. TokenPos token( m_csDoc );
  327. token.nNext = m_aPos[iPosParent].nEndL;
  328. CString csName;
  329. while ( csName.IsEmpty() )
  330. {
  331. // Look for left angle bracket of start tag
  332. m_aPos[iPos].nStartL = token.nNext;
  333. if ( ! x_FindChar( token.szDoc, m_aPos[iPos].nStartL, _T('<') ) )
  334. return x_ParseError( _T("Element tag not found") );
  335. // Set parent's End tag to start looking from here (or later)
  336. m_aPos[iPosParent].nEndL = m_aPos[iPos].nStartL;
  337. // Determine whether this is an element, or bypass other type of node
  338. token.nNext = m_aPos[iPos].nStartL + 1;
  339. if ( x_FindToken( token ) )
  340. {
  341. if ( token.bIsString )
  342. return x_ParseError( _T("Tag starts with quote") );
  343. _TCHAR cFirstChar = m_csDoc[token.nL];
  344. if ( cFirstChar == _T('?') || cFirstChar == _T('!') )
  345. {
  346. token.nNext = m_aPos[iPos].nStartL;
  347. if ( ! x_ParseNode(token) )
  348. return x_ParseError( _T("Invalid node") );
  349. }
  350. else if ( cFirstChar != _T('/') )
  351. {
  352. csName = x_GetToken( token );
  353. // Look for end of tag
  354. if ( ! x_FindChar(token.szDoc, token.nNext, _T('>')) )
  355. return x_ParseError( _T("End of tag not found") );
  356. }
  357. else
  358. return x_ReleasePos(); // probably end tag of parent
  359. }
  360. else
  361. return x_ParseError( _T("Abrupt end within tag") );
  362. }
  363. m_aPos[iPos].nStartR = token.nNext;
  364. // Is ending mark within start tag, i.e. empty element?
  365. if ( m_csDoc[m_aPos[iPos].nStartR-1] == _T('/') )
  366. {
  367. // Empty element
  368. // Close tag left is set to ending mark, and right to open tag right
  369. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR-1;
  370. m_aPos[iPos].nEndR = m_aPos[iPos].nStartR;
  371. }
  372. else // look for end tag
  373. {
  374. // Element probably has contents
  375. // Determine where to start looking for left angle bracket of end tag
  376. // This is done by recursively parsing the contents of this element
  377. int iInner, iInnerPrev = 0;
  378. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + 1;
  379. while ( (iInner = x_ParseElem( iPos )) > 0 )
  380. {
  381. // Set links to iInner
  382. if ( iInnerPrev )
  383. m_aPos[iInnerPrev].iElemNext = iInner;
  384. else
  385. m_aPos[iPos].iElemChild = iInner;
  386. iInnerPrev = iInner;
  387. // Set offset to reflect child
  388. m_aPos[iPos].nEndL = m_aPos[iInner].nEndR + 1;
  389. }
  390. if ( iInner == -1 )
  391. return -1;
  392. // Look for left angle bracket of end tag
  393. if ( ! x_FindChar( token.szDoc, m_aPos[iPos].nEndL, _T('<') ) )
  394. return x_ParseError( _T("End tag of %s element not found"), csName );
  395. // Look through tokens of end tag
  396. token.nNext = m_aPos[iPos].nEndL + 1;
  397. int nTokenCount = 0;
  398. while ( x_FindToken( token ) )
  399. {
  400. ++nTokenCount;
  401. if ( ! token.bIsString )
  402. {
  403. // Is first token not an end slash mark?
  404. if ( nTokenCount == 1 && m_csDoc[token.nL] != _T('/') )
  405. return x_ParseError( _T("Expecting end tag of element %s"), csName );
  406. else if ( nTokenCount == 2 && ! token.Match(csName) )
  407. return x_ParseError( _T("End tag does not correspond to %s"), csName );
  408. // Else is it a right angle bracket?
  409. else if ( m_csDoc[token.nL] == _T('>') )
  410. break;
  411. }
  412. }
  413. // Was a right angle bracket not found?
  414. if ( ! token.szDoc[token.nL] || nTokenCount < 2 )
  415. return x_ParseError( _T("End tag not completed for element %s"), csName );
  416. m_aPos[iPos].nEndR = token.nL;
  417. }
  418. // Successfully parsed element (and contained elements)
  419. return iPos;
  420. }
  421. bool CMarkup::x_FindChar( LPCTSTR szDoc, int& nChar, _TCHAR c )
  422. {
  423. // static function
  424. LPCTSTR pChar = &szDoc[nChar];
  425. while ( *pChar && *pChar != c )
  426. pChar += _tclen( pChar );
  427. nChar = pChar - szDoc;
  428. if ( ! *pChar )
  429. return false;
  430. /*
  431. while ( szDoc[nChar] && szDoc[nChar] != c )
  432. nChar += _tclen( &szDoc[nChar] );
  433. if ( ! szDoc[nChar] )
  434. return false;
  435. */
  436. return true;
  437. }
  438. bool CMarkup::x_FindToken( CMarkup::TokenPos& token )
  439. {
  440. // Starting at token.nNext, bypass whitespace and find the next token
  441. // returns true on success, members of token point to token
  442. // returns false on end of document, members point to end of document
  443. LPCTSTR szDoc = token.szDoc;
  444. int nChar = token.nNext;
  445. token.bIsString = false;
  446. // By-pass leading whitespace
  447. while ( szDoc[nChar] && _tcschr(_T(" tnr"),szDoc[nChar]) )
  448. ++nChar;
  449. if ( ! szDoc[nChar] )
  450. {
  451. // No token was found before end of document
  452. token.nL = nChar;
  453. token.nR = nChar;
  454. token.nNext = nChar;
  455. return false;
  456. }
  457. // Is it an opening quote?
  458. _TCHAR cFirstChar = szDoc[nChar];
  459. if ( cFirstChar == _T('"') || cFirstChar == _T(''') )
  460. {
  461. token.bIsString = true;
  462. // Move past opening quote
  463. ++nChar;
  464. token.nL = nChar;
  465. // Look for closing quote
  466. x_FindChar( token.szDoc, nChar, cFirstChar );
  467. // Set right to before closing quote
  468. token.nR = nChar - 1;
  469. // Set nChar past closing quote unless at end of document
  470. if ( szDoc[nChar] )
  471. ++nChar;
  472. }
  473. else
  474. {
  475. // Go until special char or whitespace
  476. token.nL = nChar;
  477. while ( szDoc[nChar] && ! _tcschr(_T(" tnr<>=\/?!"),szDoc[nChar]) )
  478. nChar += _tclen(&szDoc[nChar]);
  479. // Adjust end position if it is one special char
  480. if ( nChar == token.nL )
  481. ++nChar; // it is a special char
  482. token.nR = nChar - 1;
  483. }
  484. // nNext points to one past last char of token
  485. token.nNext = nChar;
  486. return true;
  487. }
  488. CString CMarkup::x_GetToken( const CMarkup::TokenPos& token ) const
  489. {
  490. // The token contains indexes into the document identifying a small substring
  491. // Build the substring from those indexes and return it
  492. if ( token.nL > token.nR )
  493. return _T("");
  494. return m_csDoc.Mid( token.nL,
  495. token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) );
  496. }
  497. int CMarkup::x_FindElem( int iPosParent, int iPos, LPCTSTR szPath )
  498. {
  499. // If szPath is NULL or empty, go to next sibling element
  500. // Otherwise go to next sibling element with matching path
  501. //
  502. if ( iPos )
  503. iPos = m_aPos[iPos].iElemNext;
  504. else
  505. iPos = m_aPos[iPosParent].iElemChild;
  506. // Finished here if szPath not specified
  507. if ( szPath == NULL || !szPath[0] )
  508. return iPos;
  509. // Search
  510. TokenPos token( m_csDoc );
  511. while ( iPos )
  512. {
  513. // Compare tag name
  514. token.nNext = m_aPos[iPos].nStartL + 1;
  515. x_FindToken( token ); // Locate tag name
  516. if ( token.Match(szPath) )
  517. return iPos;
  518. iPos = m_aPos[iPos].iElemNext;
  519. }
  520. return 0;
  521. }
  522. int CMarkup::x_ParseNode( CMarkup::TokenPos& token )
  523. {
  524. // Call this with token.nNext set to the start of the node
  525. // This returns the node type and token.nNext set to the char after the node
  526. // If the node is not found or an element, token.nR is not determined
  527. // White space between elements is a text node
  528. int nTypeFound = 0;
  529. LPCTSTR szDoc = token.szDoc;
  530. token.nL = token.nNext;
  531. if ( szDoc[token.nL] == '<' )
  532. {
  533. // Started with <, could be:
  534. // <!--...--> comment
  535. // <!DOCTYPE ...> dtd
  536. // <?target ...?> processing instruction
  537. // <![CDATA[...]]> cdata section
  538. // <NAME ...> element
  539. //
  540. if ( ! szDoc[token.nL+1] || ! szDoc[token.nL+2] )
  541. return 0;
  542. _TCHAR cFirstChar = szDoc[token.nL+1];
  543. LPCTSTR szEndOfNode = NULL;
  544. if ( cFirstChar == _T('?') )
  545. {
  546. nTypeFound = MNT_PROCESSING_INSTRUCTION; // processing instruction
  547. szEndOfNode = _T("?>");
  548. }
  549. else if ( cFirstChar == _T('!') )
  550. {
  551. _TCHAR cSecondChar = szDoc[token.nL+2];
  552. if ( cSecondChar == _T('[') )
  553. {
  554. nTypeFound = MNT_CDATA_SECTION;
  555. szEndOfNode = _T("]]>");
  556. }
  557. else if ( cSecondChar == _T('-') )
  558. {
  559. nTypeFound = MNT_COMMENT;
  560. szEndOfNode = _T("-->");
  561. }
  562. else
  563. {
  564. // Document type requires tokenizing because of strings and brackets
  565. nTypeFound = 0;
  566. int nBrackets = 0;
  567. while ( x_FindToken(token) )
  568. {
  569. if ( ! token.bIsString )
  570. {
  571. _TCHAR cChar = szDoc[token.nL];
  572. if ( cChar == _T('[') )
  573. ++nBrackets;
  574. else if ( cChar == _T(']') )
  575. --nBrackets;
  576. else if ( nBrackets == 0 && cChar == _T('>') )
  577. {
  578. nTypeFound = MNT_DOCUMENT_TYPE;
  579. break;
  580. }
  581. }
  582. }
  583. if ( ! nTypeFound )
  584. return 0;
  585. }
  586. }
  587. else if ( cFirstChar == _T('/') )
  588. {
  589. // End tag means no node found within parent element
  590. return 0;
  591. }
  592. else
  593. {
  594. nTypeFound = MNT_ELEMENT;
  595. }
  596. // Search for end of node if not found yet
  597. if ( szEndOfNode )
  598. {
  599. LPCTSTR pEnd = _tcsstr( &szDoc[token.nNext], szEndOfNode );
  600. if ( ! pEnd )
  601. return 0; // not well-formed
  602. token.nNext = (pEnd - szDoc) + _tcslen(szEndOfNode);
  603. }
  604. }
  605. else if ( szDoc[token.nL] )
  606. {
  607. // It is text or whitespace because it did not start with <
  608. nTypeFound = MNT_WHITESPACE;
  609. if ( x_FindToken(token) )
  610. {
  611. if ( szDoc[token.nL] == _T('<') )
  612. token.nNext = token.nL;
  613. else
  614. {
  615. nTypeFound = MNT_TEXT;
  616. x_FindChar( token.szDoc, token.nNext, _T('<') );
  617. }
  618. }
  619. }
  620. return nTypeFound;
  621. }
  622. CString CMarkup::x_GetTagName( int iPos ) const
  623. {
  624. // Return the tag name at specified element
  625. TokenPos token( m_csDoc );
  626. token.nNext = m_aPos[iPos].nStartL + 1;
  627. if ( ! iPos || ! x_FindToken( token ) )
  628. return _T("");
  629. // Return substring of document
  630. return x_GetToken( token );
  631. }
  632. bool CMarkup::x_FindAttr( CMarkup::TokenPos& token, LPCTSTR szAttr ) const
  633. {
  634. // If szAttr is NULL find next Attr, otherwise find named Attr
  635. // Return true if found
  636. int nAttr = 0;
  637. for ( int nCount = 0; x_FindToken(token); ++nCount )
  638. {
  639. if ( ! token.bIsString )
  640. {
  641. // Is it the right angle bracket?
  642. if ( m_csDoc[token.nL] == _T('>') || m_csDoc[token.nL] == _T('/') )
  643. break; // Attr not found
  644. // Equal sign
  645. if ( m_csDoc[token.nL] == _T('=') )
  646. continue;
  647. // Potential Attrute
  648. if ( ! nAttr && nCount )
  649. {
  650. // Attrute name search?
  651. if ( ! szAttr || ! szAttr[0] )
  652. return true; // return with token at Attr name
  653. // Compare szAttr
  654. if ( token.Match(szAttr) )
  655. nAttr = nCount;
  656. }
  657. }
  658. else if ( nAttr && nCount == nAttr + 2 )
  659. {
  660. return true;
  661. }
  662. }
  663. // Not found
  664. return false;
  665. }
  666. CString CMarkup::x_GetAttr( int iPos, LPCTSTR szAttr ) const
  667. {
  668. // Return the value of the Attr at specified element
  669. if ( ! iPos || m_nNodeType != MNT_ELEMENT )
  670. return _T("");
  671. TokenPos token( m_csDoc );
  672. token.nNext = m_aPos[iPos].nStartL + 1;
  673. if ( szAttr && x_FindAttr( token, szAttr ) )
  674. return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) );
  675. return _T("");
  676. }
  677. bool CMarkup::x_SetAttr( int iPos, LPCTSTR szAttr, int nValue )
  678. {
  679. // Convert integer to string and call SetChildAttr
  680. _TCHAR szVal[25];
  681. _stprintf( szVal, _T("%d"), nValue );
  682. return x_SetAttr( iPos, szAttr, szVal );
  683. }
  684. bool CMarkup::x_SetAttr( int iPos, LPCTSTR szAttr, LPCTSTR szValue )
  685. {
  686. // Set Attrute in iPos element
  687. if ( ! iPos || m_nNodeType != MNT_ELEMENT )
  688. return false;
  689. TokenPos token( m_csDoc );
  690. token.nNext = m_aPos[iPos].nStartL + 1;
  691. int nInsertAt, nReplace = 0;
  692. CString csInsert;
  693. if ( x_FindAttr( token, szAttr ) )
  694. {
  695. // Decision: for empty value leaving Attr="" instead of removing Attr
  696. // Replace value only
  697. csInsert = x_TextToDoc( szValue, true );
  698. nInsertAt = token.nL;
  699. nReplace = token.nR-token.nL+1;
  700. }
  701. else
  702. {
  703. // Insert string name value pair
  704. CString csFormat;
  705. csFormat = _T(" ");
  706. csFormat += szAttr;
  707. csFormat += _T("="");
  708. csFormat += x_TextToDoc( szValue, true );
  709. csFormat += _T(""");
  710. csInsert = csFormat;
  711. // take into account whether it is an empty element
  712. nInsertAt = m_aPos[iPos].nStartR - (m_aPos[iPos].IsEmptyElement()?1:0);
  713. }
  714. x_DocChange( nInsertAt, nReplace, csInsert );
  715. int nAdjust = csInsert.GetLength() - nReplace;
  716. m_aPos[iPos].nStartR += nAdjust;
  717. m_aPos[iPos].AdjustEnd( nAdjust );
  718. x_Adjust( iPos, nAdjust );
  719. MARKUP_SETDEBUGSTATE;
  720. return true;
  721. }
  722. bool CMarkup::x_CreateNode( CString& csNode, int nNodeType, LPCTSTR szText )
  723. {
  724. // Set csNode based on nNodeType and szData
  725. // Return false if szData would jeopardize well-formed document
  726. //
  727. switch ( nNodeType )
  728. {
  729. case MNT_CDATA_SECTION:
  730. if ( _tcsstr(szText,_T("]]>")) != NULL )
  731. return false;
  732. csNode = "<![CDATA[";
  733. csNode += szText;
  734. csNode += "]]>";
  735. break;
  736. }
  737. return true;
  738. }
  739. bool CMarkup::x_SetData( int iPos, LPCTSTR szData, int nCDATA )
  740. {
  741. // Set data at specified position
  742. // if nCDATA==1, set content of element to a CDATA Section
  743. CString csInsert;
  744. // Set data in iPos element
  745. if ( ! iPos || m_aPos[iPos].iElemChild )
  746. return false;
  747. // Build csInsert from szData based on nCDATA
  748. // If CDATA section not valid, use parsed text (PCDATA) instead
  749. if ( nCDATA != 0 )
  750. if ( ! x_CreateNode(csInsert, MNT_CDATA_SECTION, szData) )
  751. nCDATA = 0;
  752. if ( nCDATA == 0 )
  753. csInsert = x_TextToDoc( szData );
  754. // Decide where to insert
  755. int nInsertAt, nReplace;
  756. if ( m_aPos[iPos].IsEmptyElement() )
  757. {
  758. nInsertAt = m_aPos[iPos].nEndL;
  759. nReplace = 1;
  760. // Pre-adjust since <NAME/> becomes <NAME>data</NAME>
  761. CString csTagName = x_GetTagName( iPos );
  762. m_aPos[iPos].nStartR -= 1;
  763. m_aPos[iPos].nEndL -= (1 + csTagName.GetLength());
  764. CString csFormat;
  765. csFormat = _T(">");
  766. csFormat += csInsert;
  767. csFormat += _T("</");
  768. csFormat += csTagName;
  769. csInsert = csFormat;
  770. }
  771. else
  772. {
  773. nInsertAt = m_aPos[iPos].nStartR+1;
  774. nReplace = m_aPos[iPos].nEndL - m_aPos[iPos].nStartR - 1;
  775. }
  776. x_DocChange( nInsertAt, nReplace, csInsert );
  777. int nAdjust = csInsert.GetLength() - nReplace;
  778. x_Adjust( iPos, nAdjust );
  779. m_aPos[iPos].AdjustEnd( nAdjust );
  780. MARKUP_SETDEBUGSTATE;
  781. return true;
  782. }
  783. CString CMarkup::x_GetData( int iPos ) const
  784. {
  785. // Return a string representing data between start and end tag
  786. // Return empty string if there are any children elements
  787. if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() )
  788. {
  789. // See if it is a CDATA section
  790. TokenPos token( m_csDoc );
  791. token.nNext = m_aPos[iPos].nStartR+1;
  792. if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<')
  793. && token.nL + 11 < m_aPos[iPos].nEndL
  794. && _tcsncmp( &token.szDoc[token.nL+1], _T("![CDATA["), 8 ) == 0 )
  795. {
  796. int nEndCDATA = m_csDoc.Find( _T("]]>"), token.nNext );
  797. if ( nEndCDATA != -1 && nEndCDATA < m_aPos[iPos].nEndL )
  798. {
  799. return m_csDoc.Mid( token.nL+9, nEndCDATA-token.nL-9 );
  800. }
  801. }
  802. return x_TextFromDoc( m_aPos[iPos].nStartR+1, m_aPos[iPos].nEndL-1 );
  803. }
  804. return _T("");
  805. }
  806. CString CMarkup::x_TextToDoc( LPCTSTR szText, bool bAttr ) const
  807. {
  808. // Convert text as seen outside XML document to XML friendly
  809. // replacing special characters with ampersand escape codes
  810. // E.g. convert "6>7" to "6&gt;7"
  811. //
  812. // &lt;   less than
  813. // &amp;  ampersand
  814. // &gt;   greater than
  815. //
  816. // and for Attrutes:
  817. //
  818. // &apos; apostrophe or single quote
  819. // &quot; double quote
  820. //
  821. static _TCHAR* szaReplace[] = { _T("&lt;"),_T("&amp;"),_T("&gt;"),_T("&apos;"),_T("&quot;") };
  822. const _TCHAR* pFind = bAttr?_T("<&>'""):_T("<&>");
  823. CString csText;
  824. const _TCHAR* pSource = szText;
  825. int nDestSize = _tcslen(pSource);
  826. nDestSize += nDestSize / 10 + 7;
  827. _TCHAR* pDest = csText.GetBuffer(nDestSize);
  828. int nLen = 0;
  829. _TCHAR cSource = *pSource;
  830. _TCHAR* pFound;
  831. while ( cSource )
  832. {
  833. if ( nLen > nDestSize - 6 )
  834. {
  835. csText.ReleaseBuffer(nLen);
  836. nDestSize *= 2;
  837. pDest = csText.GetBuffer(nDestSize);
  838. }
  839. if ( (pFound=_tcschr(pFind,cSource)) != NULL )
  840. {
  841. pFound = szaReplace[pFound-pFind];
  842. _tcscpy(&pDest[nLen],pFound);
  843. nLen += _tcslen(pFound);
  844. }
  845. else
  846. {
  847. _tccpy( &pDest[nLen], pSource );
  848. ++nLen;
  849. }
  850. pSource += _tclen( pSource );
  851. cSource = *pSource;
  852. }
  853. csText.ReleaseBuffer(nLen);
  854. return csText;
  855. }
  856. CString CMarkup::x_TextFromDoc( int nLeft, int nRight ) const
  857. {
  858. // Convert XML friendly text to text as seen outside XML document
  859. // replacing ampersand escape codes with special characters
  860. // E.g. convert "6&gt;7" to "6>7"
  861. //
  862. // Conveniently the result is always the same or shorter in length
  863. //
  864. static _TCHAR* szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") };
  865. static int anCodeLen[] = { 3,4,3,5,5 };
  866. static _TCHAR* szSymbol = _T("<&>'"");
  867. CString csText;
  868. const _TCHAR* pSource = m_csDoc;
  869. int nDestSize = nRight - nLeft + 1;
  870. _TCHAR* pDest = csText.GetBuffer(nDestSize);
  871. int nLen = 0;
  872. int nCharLen;
  873. int nChar = nLeft;
  874. while ( nChar <= nRight )
  875. {
  876. if ( pSource[nChar] == _T('&') )
  877. {
  878. // Look for matching &code;
  879. for ( int nMatch = 0; nMatch < 5; ++nMatch )
  880. {
  881. if ( nChar <= nRight - anCodeLen[nMatch]
  882. && _tcsncmp(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 )
  883. {
  884. pDest[nLen++] = szSymbol[nMatch];
  885. nChar += anCodeLen[nMatch] + 1;
  886. break;
  887. }
  888. }
  889. // If no match is found it means XML doc is invalid
  890. // no devastating harm done, ampersand code will just be left in result
  891. if ( nMatch == 5 )
  892. {
  893. pDest[nLen++] = _T('&');
  894. ++nChar;
  895. }
  896. }
  897. else
  898. {
  899. nCharLen = _tclen(&pSource[nChar]);
  900. _tccpy( &pDest[nLen], &pSource[nChar] );
  901. nLen += nCharLen;
  902. nChar += nCharLen;
  903. }
  904. }
  905. csText.ReleaseBuffer(nLen);
  906. return csText;
  907. }
  908. void CMarkup::x_DocChange( int nLeft, int nReplace, const CString& csInsert )
  909. {
  910. // Insert csInsert int m_csDoc at nLeft replacing nReplace chars
  911. // Do this with only one buffer reallocation if it grows
  912. //
  913. int nDocLength = m_csDoc.GetLength();
  914. int nInsLength = csInsert.GetLength();
  915. // Make sure nLeft and nReplace are within bounds
  916. nLeft = max( 0, min( nLeft, nDocLength ) );
  917. nReplace = max( 0, min( nReplace, nDocLength-nLeft ) );
  918. // Get pointer to buffer with enough room
  919. int nNewLength = nInsLength + nDocLength - nReplace;
  920. int nBufferLen = nNewLength;
  921. _TCHAR* pDoc = m_csDoc.GetBuffer( nBufferLen );
  922. // Move part of old doc that goes after insert
  923. if ( nLeft+nReplace < nDocLength )
  924. memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
  925. // Copy insert
  926. memcpy( &pDoc[nLeft], csInsert, nInsLength*sizeof(_TCHAR) );
  927. // Release
  928. m_csDoc.ReleaseBuffer( nNewLength );
  929. }
  930. void CMarkup::x_Adjust( int iPos, int nShift, bool bAfterPos )
  931. {
  932. // Loop through affected elements and adjust indexes
  933. // Algorithm:
  934. // 1. update children unless bAfterPos
  935. //    (if no children or bAfterPos is true, end tag of iPos not affected)
  936. // 2. update next siblings and their children
  937. // 3. go up until there is a next sibling of a parent and update end tags
  938. // 4. step 2
  939. int iPosTop = m_aPos[iPos].iElemParent;
  940. bool bPosFirst = bAfterPos; // mark as first to skip its children
  941. while ( iPos )
  942. {
  943. // Were we at containing parent of affected position?
  944. bool bPosTop = false;
  945. if ( iPos == iPosTop )
  946. {
  947. // Move iPosTop up one towards root
  948. iPosTop = m_aPos[iPos].iElemParent;
  949. bPosTop = true;
  950. }
  951. // Traverse to the next update position
  952. if ( ! bPosTop && ! bPosFirst && m_aPos[iPos].iElemChild )
  953. {
  954. // Depth first
  955. iPos = m_aPos[iPos].iElemChild;
  956. }
  957. else if ( m_aPos[iPos].iElemNext )
  958. {
  959. iPos = m_aPos[iPos].iElemNext;
  960. }
  961. else
  962. {
  963. // Look for next sibling of a parent of iPos
  964. // When going back up, parents have already been done except iPosTop
  965. while ( (iPos=m_aPos[iPos].iElemParent) != 0 && iPos != iPosTop )
  966. if ( m_aPos[iPos].iElemNext )
  967. {
  968. iPos = m_aPos[iPos].iElemNext;
  969. break;
  970. }
  971. }
  972. bPosFirst = false;
  973. // Shift indexes at iPos
  974. if ( iPos != iPosTop )
  975. m_aPos[iPos].AdjustStart( nShift );
  976. m_aPos[iPos].AdjustEnd( nShift );
  977. }
  978. }
  979. void CMarkup::x_LocateNew( int iPosParent, int& iPosRel, int& nOffset, int nLength, int nFlags )
  980. {
  981. // Determine where to insert new element or node
  982. //
  983. bool bInsert = (nFlags&1)?true:false;
  984. bool bHonorWhitespace = (nFlags&2)?true:false;
  985. int nStartL;
  986. if ( nLength )
  987. {
  988. // Located at a non-element node
  989. if ( bInsert )
  990. nStartL = nOffset;
  991. else
  992. nStartL = nOffset + nLength;
  993. }
  994. else if ( iPosRel )
  995. {
  996. // Located at an element
  997. if ( bInsert ) // precede iPosRel
  998. nStartL = m_aPos[iPosRel].nStartL;
  999. else // follow iPosRel
  1000. nStartL = m_aPos[iPosRel].nEndR + 1;
  1001. }
  1002. else if ( m_aPos[iPosParent].IsEmptyElement() )
  1003. {
  1004. // Parent has no separate end tag, so split empty element
  1005. nStartL = m_aPos[iPosParent].nStartR;
  1006. }
  1007. else
  1008. {
  1009. if ( bInsert ) // after start tag
  1010. nStartL = m_aPos[iPosParent].nStartR + 1;
  1011. else // before end tag
  1012. nStartL = m_aPos[iPosParent].nEndL;
  1013. }
  1014. // Go up to start of next node, unless its splitting an empty element
  1015. if ( ! bHonorWhitespace && ! m_aPos[iPosParent].IsEmptyElement() )
  1016. {
  1017. TokenPos token( m_csDoc );
  1018. token.nNext = nStartL;
  1019. if ( ! x_FindToken(token) || m_csDoc[token.nL] == _T('<') )
  1020. nStartL = token.nL;
  1021. }
  1022. // Determine iPosBefore
  1023. int iPosBefore = 0;
  1024. if ( iPosRel )
  1025. {
  1026. if ( bInsert )
  1027. {
  1028. // Is iPosRel past first sibling?
  1029. int iPosPrev = m_aPos[iPosParent].iElemChild;
  1030. if ( iPosPrev != iPosRel )
  1031. {
  1032. // Find previous sibling of iPosRel
  1033. while ( m_aPos[iPosPrev].iElemNext != iPosRel )
  1034. iPosPrev = m_aPos[iPosPrev].iElemNext;
  1035. iPosBefore = iPosPrev;
  1036. }
  1037. }
  1038. else
  1039. {
  1040. iPosBefore = iPosRel;
  1041. }
  1042. }
  1043. else if ( m_aPos[iPosParent].iElemChild )
  1044. {
  1045. if ( ! bInsert )
  1046. {
  1047. // Find last element under iPosParent
  1048. int iPosLast = m_aPos[iPosParent].iElemChild;
  1049. int iPosNext = iPosLast;
  1050. while ( iPosNext )
  1051. {
  1052. iPosLast = iPosNext;
  1053. iPosNext = m_aPos[iPosNext].iElemNext;
  1054. }
  1055. iPosBefore = iPosLast;
  1056. }
  1057. }
  1058. nOffset = nStartL;
  1059. iPosRel = iPosBefore;
  1060. }
  1061. bool CMarkup::x_AddElem( LPCTSTR szName, LPCTSTR szValue, bool bInsert, bool bAddChild )
  1062. {
  1063. if ( bAddChild )
  1064. {
  1065. // Adding a child element under main position
  1066. if ( ! m_iPos )
  1067. return false;
  1068. }
  1069. else if ( m_iPosParent == 0 )
  1070. {
  1071. // Adding root element
  1072. if ( IsWellFormed() )
  1073. return false;
  1074. // Locate after any version and DTD
  1075. m_aPos[0].nEndL = m_csDoc.GetLength();
  1076. }
  1077. // Locate where to add element relative to current node
  1078. int iPosParent, iPosBefore, nOffset = 0, nLength = 0;
  1079. if ( bAddChild )
  1080. {
  1081. iPosParent = m_iPos;
  1082. iPosBefore = m_iPosChild;
  1083. }
  1084. else
  1085. {
  1086. iPosParent = m_iPosParent;
  1087. iPosBefore = m_iPos;
  1088. }
  1089. int nFlags = bInsert?1:0;
  1090. x_LocateNew( iPosParent, iPosBefore, nOffset, nLength, nFlags );
  1091. bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
  1092. if ( bEmptyParent )
  1093. nOffset += 2; // include CRLF
  1094. // Create element and modify positions of affected elements
  1095. // If no szValue is specified, an empty element is created
  1096. // i.e. either <NAME>value</NAME> or <NAME/>
  1097. //
  1098. int iPos = x_GetFreePos();
  1099. m_aPos[iPos].nStartL = nOffset;
  1100. // Set links
  1101. m_aPos[iPos].iElemParent = iPosParent;
  1102. m_aPos[iPos].iElemChild = 0;
  1103. m_aPos[iPos].iElemNext = 0;
  1104. if ( iPosBefore )
  1105. {
  1106. // Link in after iPosBefore
  1107. m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
  1108. m_aPos[iPosBefore].iElemNext = iPos;
  1109. }
  1110. else
  1111. {
  1112. // First child
  1113. m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
  1114. m_aPos[iPosParent].iElemChild = iPos;
  1115. }
  1116. // Create string for insert
  1117. CString csInsert;
  1118. int nLenName = _tcslen(szName);
  1119. int nLenValue = szValue? _tcslen(szValue) : 0;
  1120. if ( ! nLenValue )
  1121. {
  1122. // <NAME/> empty element
  1123. csInsert = _T("<");
  1124. csInsert += szName;
  1125. csInsert += _T("/>rn");
  1126. m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 2;
  1127. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR - 1;
  1128. m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + 1;
  1129. }
  1130. else
  1131. {
  1132. // <NAME>value</NAME>
  1133. CString csValue = x_TextToDoc( szValue );
  1134. nLenValue = csValue.GetLength();
  1135. csInsert = _T("<");
  1136. csInsert += szName;
  1137. csInsert += _T(">");
  1138. csInsert += csValue;
  1139. csInsert += _T("</");
  1140. csInsert += szName;
  1141. csInsert += _T(">rn");
  1142. m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 1;
  1143. m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + nLenValue + 1;
  1144. m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + nLenName + 2;
  1145. }
  1146. // Insert
  1147. int nReplace = 0, nLeft = m_aPos[iPos].nStartL;
  1148. if ( bEmptyParent )
  1149. {
  1150. CString csParentTagName = x_GetTagName(iPosParent);
  1151. CString csFormat;
  1152. csFormat = _T(">rn");
  1153. csFormat += csInsert;
  1154. csFormat += _T("</");
  1155. csFormat += csParentTagName;
  1156. csInsert = csFormat;
  1157. nLeft -= 3;
  1158. nReplace = 1;
  1159. // x_Adjust is going to update all affected indexes by one amount
  1160. // This will satisfy all except the empty parent
  1161. // Here we pre-adjust for the empty parent
  1162. // The empty tag slash is removed
  1163. m_aPos[iPosParent].nStartR -= 1;
  1164. // For the newly created end tag, see the following example:
  1165. // <A/> (len 4) becomes <A><B/></A> (len 11)
  1166. // In x_Adjust everything will be adjusted 11 - 4 = 7
  1167. // But the nEndL of element A should only be adjusted 5
  1168. m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
  1169. }
  1170. x_DocChange( nLeft, nReplace, csInsert );
  1171. x_Adjust( iPos, csInsert.GetLength() - nReplace );
  1172. if ( bAddChild )
  1173. x_SetPos( m_iPosParent, iPosParent, iPos );
  1174. else
  1175. x_SetPos( iPosParent, iPos, 0 );
  1176. return true;
  1177. }
  1178. bool CMarkup::x_AddSubDoc( LPCTSTR szSubDoc, bool bInsert, bool bAddChild )
  1179. {
  1180. // Add subdocument, parse, and modify positions of affected elements
  1181. //
  1182. int nOffset = 0, iPosParent, iPosBefore;
  1183. if ( bAddChild )
  1184. {
  1185. // Add a subdocument under main position, after current child position
  1186. if ( ! m_iPos )
  1187. return false;
  1188. iPosParent = m_iPos;
  1189. iPosBefore = m_iPosChild;
  1190. }
  1191. else
  1192. {
  1193. iPosParent = m_iPosParent;
  1194. iPosBefore = m_iPos;
  1195. }
  1196. int nFlags = bInsert?1:0;
  1197. x_LocateNew( iPosParent, iPosBefore, nOffset, 0, nFlags );
  1198. bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
  1199. if ( bEmptyParent )
  1200. nOffset += 2; // include CRLF
  1201. // if iPosBefore is NULL, insert as first element under parent
  1202. int nParentEndLBeforeAdd = m_aPos[iPosParent].nEndL;
  1203. int iPosFreeBeforeAdd = m_iPosFree;
  1204. // Skip version tag or DTD at start of subdocument
  1205. TokenPos token( szSubDoc );
  1206. int nNodeType = x_ParseNode( token );
  1207. while ( nNodeType && nNodeType != MNT_ELEMENT )
  1208. {
  1209. token.szDoc = &szSubDoc[token.nNext];
  1210. token.nNext = 0;
  1211. nNodeType = x_ParseNode( token );
  1212. }
  1213. CString csInsert = token.szDoc;
  1214. // Insert subdocument
  1215. m_aPos[iPosParent].nEndL = nOffset;
  1216. int nReplace = 0, nLeft = nOffset;
  1217. CString csParentTagName;
  1218. if ( bEmptyParent )
  1219. {
  1220. csParentTagName = x_GetTagName(iPosParent);
  1221. CString csFormat;
  1222. csFormat = _T(">rn");
  1223. csFormat += csInsert;
  1224. csFormat += _T("</");
  1225. csFormat += csParentTagName;
  1226. csInsert = csFormat;
  1227. m_aPos[iPosParent].nEndL = m_aPos[iPosParent].nStartR + 2;
  1228. nLeft = m_aPos[iPosParent].nStartR - 1;
  1229. nReplace = 1;
  1230. }
  1231. x_DocChange( nLeft, nReplace, csInsert );
  1232. // Parse subdocument
  1233. int iPos = x_ParseElem(iPosParent);
  1234. m_aPos[iPosParent].nEndL = nParentEndLBeforeAdd;
  1235. if ( iPos <= 0 )
  1236. {
  1237. // Abort because not well-formed
  1238. CString csRevert = bEmptyParent?_T("/"):_T("");
  1239. x_DocChange( nLeft, csInsert.GetLength(), csRevert );
  1240. m_iPosFree = iPosFreeBeforeAdd;
  1241. return false;
  1242. }
  1243. else
  1244. {
  1245. // Link in parent and siblings
  1246. m_aPos[iPos].iElemParent = iPosParent;
  1247. if ( iPosBefore )
  1248. {
  1249. m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
  1250. m_aPos[iPosBefore].iElemNext = iPos;
  1251. }
  1252. else
  1253. {
  1254. m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
  1255. m_aPos[iPosParent].iElemChild = iPos;
  1256. }
  1257. // Make empty parent pre-adjustment
  1258. if ( bEmptyParent )
  1259. {
  1260. m_aPos[iPosParent].nStartR -= 1;
  1261. m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
  1262. }
  1263. // Adjust, but don't adjust children of iPos (bAfterPos=true)
  1264. x_Adjust( iPos, csInsert.GetLength() - nReplace, true );
  1265. }
  1266. // Set position to top element of subdocument
  1267. if ( bAddChild )
  1268. x_SetPos( m_iPosParent, iPosParent, iPos );
  1269. else // Main
  1270. x_SetPos( m_iPosParent, iPos, 0 );
  1271. return true;
  1272. }
  1273. int CMarkup::x_RemoveElem( int iPos )
  1274. {
  1275. // Remove element and all contained elements
  1276. // Return new position
  1277. //
  1278. int iPosParent = m_aPos[iPos].iElemParent;
  1279. // Find previous sibling and bypass removed element
  1280. // This leaves orphan positions in m_aPos array
  1281. int iPosLook = m_aPos[iPosParent].iElemChild;
  1282. int iPosPrev = 0;
  1283. while ( iPosLook != iPos )
  1284. {
  1285. iPosPrev = iPosLook;
  1286. iPosLook = m_aPos[iPosLook].iElemNext;
  1287. }
  1288. if ( iPosPrev )
  1289. m_aPos[iPosPrev].iElemNext = m_aPos[iPos].iElemNext;
  1290. else
  1291. m_aPos[iPosParent].iElemChild = m_aPos[iPos].iElemNext;
  1292. // Remove from document
  1293. // Links have been changed to go around removed element
  1294. // But element position and links are still valid
  1295. int nAfterEnd = m_aPos[iPos].nEndR + 1;
  1296. TokenPos token( m_csDoc );
  1297. token.nNext = nAfterEnd;
  1298. if ( ! x_FindToken(token) || token.szDoc[token.nL] == _T('<') )
  1299. nAfterEnd = token.nL;
  1300. int nLen = nAfterEnd - m_aPos[iPos].nStartL;
  1301. x_DocChange( m_aPos[iPos].nStartL, nLen, CString() );
  1302. x_Adjust( iPos, - nLen, true );
  1303. return iPosPrev;
  1304. }