📄 markup.cpp
字号:
static _TCHAR* szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") };
static int anCodeLen[] = { 3,4,3,5,5 };
static _TCHAR* szSymbol = _T("<&>\'\"");
CString csText;
const _TCHAR* pSource = m_csDoc;
int nDestSize = nRight - nLeft + 1;
_TCHAR* pDest = csText.GetBuffer(nDestSize);
int nLen = 0;
int nCharLen;
int nChar = nLeft;
while ( nChar <= nRight )
{
if ( pSource[nChar] == _T('&') )
{
// Look for matching &code;
for ( int nMatch = 0; nMatch < 5; ++nMatch )
{
if ( nChar <= nRight - anCodeLen[nMatch]
&& _tcsncmp(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 )
{
pDest[nLen++] = szSymbol[nMatch];
nChar += anCodeLen[nMatch] + 1;
break;
}
}
// If no match is found it means XML doc is invalid
// no devastating harm done, ampersand code will just be left in result
if ( nMatch == 5 )
{
pDest[nLen++] = _T('&');
++nChar;
}
}
else
{
nCharLen = _tclen(&pSource[nChar]);
_tccpy( &pDest[nLen], &pSource[nChar] );
nLen += nCharLen;
nChar += nCharLen;
}
}
csText.ReleaseBuffer(nLen);
return csText;
}
void CMarkup::x_DocChange( int nLeft, int nReplace, const CString& csInsert )
{
// Insert csInsert int m_csDoc at nLeft replacing nReplace chars
// Do this with only one buffer reallocation if it grows
//
int nDocLength = m_csDoc.GetLength();
int nInsLength = csInsert.GetLength();
// Make sure nLeft and nReplace are within bounds
nLeft = max( 0, min( nLeft, nDocLength ) );
nReplace = max( 0, min( nReplace, nDocLength-nLeft ) );
// Get pointer to buffer with enough room
int nNewLength = nInsLength + nDocLength - nReplace;
int nBufferLen = nNewLength;
_TCHAR* pDoc = m_csDoc.GetBuffer( nBufferLen );
// Move part of old doc that goes after insert
if ( nLeft+nReplace < nDocLength )
memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
// Copy insert
memcpy( &pDoc[nLeft], csInsert, nInsLength*sizeof(_TCHAR) );
// Release
m_csDoc.ReleaseBuffer( nNewLength );
}
void CMarkup::x_Adjust( int iPos, int nShift, bool bAfterPos )
{
// Loop through affected elements and adjust indexes
// Algorithm:
// 1. update children unless bAfterPos
// (if no children or bAfterPos is true, end tag of iPos not affected)
// 2. update next siblings and their children
// 3. go up until there is a next sibling of a parent and update end tags
// 4. step 2
int iPosTop = m_aPos[iPos].iElemParent;
bool bPosFirst = bAfterPos; // mark as first to skip its children
while ( iPos )
{
// Were we at containing parent of affected position?
bool bPosTop = false;
if ( iPos == iPosTop )
{
// Move iPosTop up one towards root
iPosTop = m_aPos[iPos].iElemParent;
bPosTop = true;
}
// Traverse to the next update position
if ( ! bPosTop && ! bPosFirst && m_aPos[iPos].iElemChild )
{
// Depth first
iPos = m_aPos[iPos].iElemChild;
}
else if ( m_aPos[iPos].iElemNext )
{
iPos = m_aPos[iPos].iElemNext;
}
else
{
// Look for next sibling of a parent of iPos
// When going back up, parents have already been done except iPosTop
while ( (iPos=m_aPos[iPos].iElemParent) != 0 && iPos != iPosTop )
if ( m_aPos[iPos].iElemNext )
{
iPos = m_aPos[iPos].iElemNext;
break;
}
}
bPosFirst = false;
// Shift indexes at iPos
if ( iPos != iPosTop )
m_aPos[iPos].AdjustStart( nShift );
m_aPos[iPos].AdjustEnd( nShift );
}
}
void CMarkup::x_LocateNew( int iPosParent, int& iPosRel, int& nOffset, int nLength, int nFlags )
{
// Determine where to insert new element or node
//
bool bInsert = (nFlags&1)?true:false;
bool bHonorWhitespace = (nFlags&2)?true:false;
int nStartL;
if ( nLength )
{
// Located at a non-element node
if ( bInsert )
nStartL = nOffset;
else
nStartL = nOffset + nLength;
}
else if ( iPosRel )
{
// Located at an element
if ( bInsert ) // precede iPosRel
nStartL = m_aPos[iPosRel].nStartL;
else // follow iPosRel
nStartL = m_aPos[iPosRel].nEndR + 1;
}
else if ( m_aPos[iPosParent].IsEmptyElement() )
{
// Parent has no separate end tag, so split empty element
nStartL = m_aPos[iPosParent].nStartR;
}
else
{
if ( bInsert ) // after start tag
nStartL = m_aPos[iPosParent].nStartR + 1;
else // before end tag
nStartL = m_aPos[iPosParent].nEndL;
}
// Go up to start of next node, unless its splitting an empty element
if ( ! bHonorWhitespace && ! m_aPos[iPosParent].IsEmptyElement() )
{
TokenPos token( m_csDoc );
token.nNext = nStartL;
if ( ! x_FindToken(token) || m_csDoc[token.nL] == _T('<') )
nStartL = token.nL;
}
// Determine iPosBefore
int iPosBefore = 0;
if ( iPosRel )
{
if ( bInsert )
{
// Is iPosRel past first sibling?
int iPosPrev = m_aPos[iPosParent].iElemChild;
if ( iPosPrev != iPosRel )
{
// Find previous sibling of iPosRel
while ( m_aPos[iPosPrev].iElemNext != iPosRel )
iPosPrev = m_aPos[iPosPrev].iElemNext;
iPosBefore = iPosPrev;
}
}
else
{
iPosBefore = iPosRel;
}
}
else if ( m_aPos[iPosParent].iElemChild )
{
if ( ! bInsert )
{
// Find last element under iPosParent
int iPosLast = m_aPos[iPosParent].iElemChild;
int iPosNext = iPosLast;
while ( iPosNext )
{
iPosLast = iPosNext;
iPosNext = m_aPos[iPosNext].iElemNext;
}
iPosBefore = iPosLast;
}
}
nOffset = nStartL;
iPosRel = iPosBefore;
}
bool CMarkup::x_AddElem( LPCTSTR szName, LPCTSTR szValue, bool bInsert, bool bAddChild )
{
if ( bAddChild )
{
// Adding a child element under main position
if ( ! m_iPos )
return false;
}
else if ( m_iPosParent == 0 )
{
// Adding root element
if ( IsWellFormed() )
return false;
// Locate after any version and DTD
m_aPos[0].nEndL = m_csDoc.GetLength();
}
// Locate where to add element relative to current node
int iPosParent, iPosBefore, nOffset = 0, nLength = 0;
if ( bAddChild )
{
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
}
int nFlags = bInsert?1:0;
x_LocateNew( iPosParent, iPosBefore, nOffset, nLength, nFlags );
bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
if ( bEmptyParent )
nOffset += 2; // include CRLF
// Create element and modify positions of affected elements
// If no szValue is specified, an empty element is created
// i.e. either <NAME>value</NAME> or <NAME/>
//
int iPos = x_GetFreePos();
m_aPos[iPos].nStartL = nOffset;
// Set links
m_aPos[iPos].iElemParent = iPosParent;
m_aPos[iPos].iElemChild = 0;
m_aPos[iPos].iElemNext = 0;
if ( iPosBefore )
{
// Link in after iPosBefore
m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
m_aPos[iPosBefore].iElemNext = iPos;
}
else
{
// First child
m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
m_aPos[iPosParent].iElemChild = iPos;
}
// Create string for insert
CString csInsert;
int nLenName = _tcslen(szName);
int nLenValue = szValue? _tcslen(szValue) : 0;
if ( ! nLenValue )
{
// <NAME/> empty element
csInsert = _T("<");
csInsert += szName;
csInsert += _T("/>\r\n");
m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 2;
m_aPos[iPos].nEndL = m_aPos[iPos].nStartR - 1;
m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + 1;
}
else
{
// <NAME>value</NAME>
CString csValue = x_TextToDoc( szValue );
nLenValue = csValue.GetLength();
csInsert = _T("<");
csInsert += szName;
csInsert += _T(">");
csInsert += csValue;
csInsert += _T("</");
csInsert += szName;
csInsert += _T(">\r\n");
m_aPos[iPos].nStartR = m_aPos[iPos].nStartL + nLenName + 1;
m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + nLenValue + 1;
m_aPos[iPos].nEndR = m_aPos[iPos].nEndL + nLenName + 2;
}
// Insert
int nReplace = 0, nLeft = m_aPos[iPos].nStartL;
if ( bEmptyParent )
{
CString csParentTagName = x_GetTagName(iPosParent);
CString csFormat;
csFormat = _T(">\r\n");
csFormat += csInsert;
csFormat += _T("</");
csFormat += csParentTagName;
csInsert = csFormat;
nLeft -= 3;
nReplace = 1;
// x_Adjust is going to update all affected indexes by one amount
// This will satisfy all except the empty parent
// Here we pre-adjust for the empty parent
// The empty tag slash is removed
m_aPos[iPosParent].nStartR -= 1;
// For the newly created end tag, see the following example:
// <A/> (len 4) becomes <A><B/></A> (len 11)
// In x_Adjust everything will be adjusted 11 - 4 = 7
// But the nEndL of element A should only be adjusted 5
m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
}
x_DocChange( nLeft, nReplace, csInsert );
x_Adjust( iPos, csInsert.GetLength() - nReplace );
if ( bAddChild )
x_SetPos( m_iPosParent, iPosParent, iPos );
else
x_SetPos( iPosParent, iPos, 0 );
return true;
}
bool CMarkup::x_AddSubDoc( LPCTSTR szSubDoc, bool bInsert, bool bAddChild )
{
// Add subdocument, parse, and modify positions of affected elements
//
int nOffset = 0, iPosParent, iPosBefore;
if ( bAddChild )
{
// Add a subdocument under main position, after current child position
if ( ! m_iPos )
return false;
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
}
int nFlags = bInsert?1:0;
x_LocateNew( iPosParent, iPosBefore, nOffset, 0, nFlags );
bool bEmptyParent = m_aPos[iPosParent].IsEmptyElement();
if ( bEmptyParent )
nOffset += 2; // include CRLF
// if iPosBefore is NULL, insert as first element under parent
int nParentEndLBeforeAdd = m_aPos[iPosParent].nEndL;
int iPosFreeBeforeAdd = m_iPosFree;
// Skip version tag or DTD at start of subdocument
TokenPos token( szSubDoc );
int nNodeType = x_ParseNode( token );
while ( nNodeType && nNodeType != MNT_ELEMENT )
{
token.szDoc = &szSubDoc[token.nNext];
token.nNext = 0;
nNodeType = x_ParseNode( token );
}
CString csInsert = token.szDoc;
// Insert subdocument
m_aPos[iPosParent].nEndL = nOffset;
int nReplace = 0, nLeft = nOffset;
CString csParentTagName;
if ( bEmptyParent )
{
csParentTagName = x_GetTagName(iPosParent);
CString csFormat;
csFormat = _T(">\r\n");
csFormat += csInsert;
csFormat += _T("</");
csFormat += csParentTagName;
csInsert = csFormat;
m_aPos[iPosParent].nEndL = m_aPos[iPosParent].nStartR + 2;
nLeft = m_aPos[iPosParent].nStartR - 1;
nReplace = 1;
}
x_DocChange( nLeft, nReplace, csInsert );
// Parse subdocument
int iPos = x_ParseElem(iPosParent);
m_aPos[iPosParent].nEndL = nParentEndLBeforeAdd;
if ( iPos <= 0 )
{
// Abort because not well-formed
CString csRevert = bEmptyParent?_T("/"):_T("");
x_DocChange( nLeft, csInsert.GetLength(), csRevert );
m_iPosFree = iPosFreeBeforeAdd;
return false;
}
else
{
// Link in parent and siblings
m_aPos[iPos].iElemParent = iPosParent;
if ( iPosBefore )
{
m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext;
m_aPos[iPosBefore].iElemNext = iPos;
}
else
{
m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild;
m_aPos[iPosParent].iElemChild = iPos;
}
// Make empty parent pre-adjustment
if ( bEmptyParent )
{
m_aPos[iPosParent].nStartR -= 1;
m_aPos[iPosParent].nEndL -= (csParentTagName.GetLength() + 1);
}
// Adjust, but don't adjust children of iPos (bAfterPos=true)
x_Adjust( iPos, csInsert.GetLength() - nReplace, true );
}
// Set position to top element of subdocument
if ( bAddChild )
x_SetPos( m_iPosParent, iPosParent, iPos );
else // Main
x_SetPos( m_iPosParent, iPos, 0 );
return true;
}
int CMarkup::x_RemoveElem( int iPos )
{
// Remove element and all contained elements
// Return new position
//
int iPosParent = m_aPos[iPos].iElemParent;
// Find previous sibling and bypass removed element
// This leaves orphan positions in m_aPos array
int iPosLook = m_aPos[iPosParent].iElemChild;
int iPosPrev = 0;
while ( iPosLook != iPos )
{
iPosPrev = iPosLook;
iPosLook = m_aPos[iPosLook].iElemNext;
}
if ( iPosPrev )
m_aPos[iPosPrev].iElemNext = m_aPos[iPos].iElemNext;
else
m_aPos[iPosParent].iElemChild = m_aPos[iPos].iElemNext;
// Remove from document
// Links have been changed to go around removed element
// But element position and links are still valid
int nAfterEnd = m_aPos[iPos].nEndR + 1;
TokenPos token( m_csDoc );
token.nNext = nAfterEnd;
if ( ! x_FindToken(token) || token.szDoc[token.nL] == _T('<') )
nAfterEnd = token.nL;
int nLen = nAfterEnd - m_aPos[iPos].nStartL;
x_DocChange( m_aPos[iPos].nStartL, nLen, CString() );
x_Adjust( iPos, - nLen, true );
return iPosPrev;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -