📄 xmlparse.cpp
字号:
int nContentLen = m_aPos[iPos].ContentLen();
int nStartContent = m_aPos[iPos].StartContent();
LPCTSTR pszContent = &((LPCTSTR)m_strDoc)[nStartContent];
LPCTSTR pszTag = _tcschr( pszContent, _T('<') );
if ( pszTag && ((int)(pszTag-pszContent) < nContentLen) )
{
// Concatenate all CDATA Sections and text nodes, ignore other nodes
TokenPos token( m_strDoc, m_nFlags );
token.nNext = nStartContent;
NodePos node;
while ( token.nNext < nStartContent + nContentLen )
{
x_ParseNode( token, node );
if ( node.nNodeType == MNT_TEXT )
strData += UnescapeText( &token.szDoc[node.nStart], node.nLength ).c_str();
else if ( node.nNodeType == MNT_CDATA_SECTION )
strData += m_strDoc.Mid( node.nStart+9, node.nLength-12 );
}
}
else // no tags
strData = UnescapeText( &((LPCTSTR)m_strDoc)[nStartContent], nContentLen ).c_str();
}
return string(strData);
}
string CXMLParse::x_GetElemContent( int iPos ) const
{
if ( iPos && m_aPos[iPos].ContentLen() )
return string(m_strDoc.Mid( m_aPos[iPos].StartContent(), m_aPos[iPos].ContentLen() ));
return _T("");
}
bool CXMLParse::x_SetElemContent( const char * szContent )
{
// Set data in iPos element only
if ( ! m_iPos )
return false;
if ( m_nNodeLength )
return false; // not an element
// Unlink all children
int iPos = m_iPos;
int iPosChild = m_aPos[iPos].iElemChild;
bool bHadChild = (iPosChild != 0);
while ( iPosChild )
iPosChild = x_ReleaseSubDoc( iPosChild );
if ( bHadChild )
x_CheckSavedPos();
// Parse content
bool bWellFormed = true;
TokenPos token( szContent, m_nFlags );
int iPosVirtual = x_GetFreePos();
m_aPos[iPosVirtual].ClearVirtualParent();
m_aPos[iPosVirtual].SetLevel( m_aPos[iPos].Level() + 1 );
iPosChild = x_ParseElem( iPosVirtual, token );
if ( m_aPos[iPosVirtual].nFlags & MNF_ILLFORMED )
bWellFormed = false;
m_aPos[iPos].nFlags = (m_aPos[iPos].nFlags & ~MNF_ILLDATA) | (m_aPos[iPosVirtual].nFlags & MNF_ILLDATA);
// Prepare insert and adjust offsets
NodePos node( MNF_WITHNOLINES|MNF_REPLACE );
node.strMeta = szContent;
int iPosBefore = 0;
int nReplace = x_InsertNew( iPos, iPosBefore, node );
// Adjust and link in the inserted elements
x_Adjust( iPosChild, node.nStart );
m_aPos[iPosChild].nStart += node.nStart;
m_aPos[iPos].iElemChild = iPosChild;
while ( iPosChild )
{
m_aPos[iPosChild].iElemParent = iPos;
iPosChild = m_aPos[iPosChild].iElemNext;
}
x_ReleasePos( iPosVirtual );
int nAdjust = node.strMeta.GetLength() - nReplace;
x_Adjust( iPos, nAdjust, true );
m_aPos[iPos].nLength += nAdjust;
x_SetPos( m_iPosParent, m_iPos, 0 );
return bWellFormed;
}
void CXMLParse::x_DocChange( int nLeft, int nReplace, const CString& strInsert )
{
// Insert strInsert int m_strDoc at nLeft replacing nReplace chars
// Do this with only one buffer reallocation if it grows
//
int nDocLength = m_strDoc.GetLength();
int nInsLength = strInsert.GetLength();
int nNewLength = nInsLength + nDocLength - nReplace;
// When creating a document, reduce reallocs by reserving string space
// Allow for 1.5 times the current allocation
int nBufferLen = nNewLength;
int nAllocLen = ((CStringData*)((LPCTSTR)m_strDoc)-1)->nAllocLength;
if ( nNewLength > nAllocLen )
{
nBufferLen += nBufferLen/2 + 128;
if ( nBufferLen < nNewLength )
nBufferLen = nNewLength;
}
_TCHAR* pDoc = m_strDoc.GetBuffer( nBufferLen );
// Move part of old doc that goes after insert, then copy insert into it
if ( nLeft+nReplace < nDocLength )
memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
memcpy( &pDoc[nLeft], strInsert, nInsLength*sizeof(_TCHAR) );
m_strDoc.ReleaseBuffer( nNewLength );
}
void CXMLParse::x_Adjust( int iPos, int nShift, bool bAfterPos /*=false*/ )
{
// Loop through affected elements and adjust indexes
// Algorithm:
// 1. update children unless bAfterPos
// (if no children or bAfterPos is true, length of iPos not affected)
// 2. update starts of next siblings and their children
// 3. go up until there is a next sibling of a parent and update starts
// 4. step 2
int iPosTop = m_aPos[iPos].iElemParent;
bool bPosFirst = bAfterPos; // mark as first to skip its children
// Stop when we've reached the virtual parent (which has no tags)
while ( m_aPos[iPos].StartTagLen() )
{
// 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 ( 1 )
{
iPos = m_aPos[iPos].iElemParent;
if ( iPos == iPosTop )
break;
if ( m_aPos[iPos].iElemNext )
{
iPos = m_aPos[iPos].iElemNext;
break;
}
}
}
bPosFirst = false;
// Shift indexes at iPos
if ( iPos != iPosTop )
m_aPos[iPos].nStart += nShift;
else
m_aPos[iPos].nLength += nShift;
}
}
int CXMLParse::x_InsertNew( int iPosParent, int& iPosRel, CXMLParse::NodePos& node )
{
// Parent empty tag or tags with no content?
bool bEmptyParentTag = iPosParent && m_aPos[iPosParent].IsEmptyElement();
bool bNoContentParentTags = iPosParent && ! m_aPos[iPosParent].ContentLen();
if ( node.nLength )
{
// Located at a non-element node
if ( ! (node.nFlags & MNF_INSERT) )
node.nStart += node.nLength;
}
else if ( iPosRel )
{
// Located at an element
node.nStart = m_aPos[iPosRel].nStart;
if ( ! (node.nFlags & MNF_INSERT) ) // follow iPosRel
node.nStart += m_aPos[iPosRel].nLength;
}
else if ( bEmptyParentTag )
{
// Parent has no separate end tag, so split empty element
if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
node.nStart = m_aPos[iPosParent].StartContent();
else
node.nStart = m_aPos[iPosParent].StartContent() - 1;
}
else
{
if ( node.nFlags & (MNF_INSERT|MNF_REPLACE) )
node.nStart = m_aPos[iPosParent].StartContent();
else // before end tag
node.nStart = m_aPos[iPosParent].StartAfter() - m_aPos[iPosParent].EndTagLen();
}
// Go up to start of next node, unless its splitting an empty element
if ( ! (node.nFlags&(MNF_WITHNOLINES|MNF_REPLACE)) && ! bEmptyParentTag )
{
LPCTSTR szDoc = (LPCTSTR)m_strDoc;
int nChar = node.nStart;
if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
node.nStart = nChar;
}
// Is insert relative to element position? (i.e. not other kind of node)
if ( ! node.nLength )
{
// Modify iPosRel to reflect position before
if ( iPosRel )
{
if ( node.nFlags & MNF_INSERT )
{
if ( ! (m_aPos[iPosRel].nFlags & MNF_FIRST) )
iPosRel = m_aPos[iPosRel].iElemPrev;
else
iPosRel = 0;
}
}
else if ( ! (node.nFlags & MNF_INSERT) )
{
// If parent has a child, add after last child
if ( m_aPos[iPosParent].iElemChild )
iPosRel = m_aPos[m_aPos[iPosParent].iElemChild].iElemPrev;
}
}
// Get node length (used only by x_AddNode)
node.nLength = node.strMeta.GetLength();
// Prepare end of lines
if ( (! (node.nFlags & MNF_WITHNOLINES)) && (bEmptyParentTag || bNoContentParentTags) )
node.nStart += x_EOLLEN;
if ( ! (node.nFlags & MNF_WITHNOLINES) )
node.strMeta += x_EOL;
// Calculate insert offset and replace length
int nReplace = 0;
int nInsertAt = node.nStart;
if ( bEmptyParentTag )
{
CString strTagName = x_GetTagName( iPosParent ).c_str();
CString strFormat;
if ( node.nFlags & MNF_WITHNOLINES )
strFormat = _T(">");
else
strFormat = _T(">") x_EOL;
strFormat += node.strMeta;
strFormat += _T("</");
strFormat += strTagName;
node.strMeta = strFormat;
if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
{
nInsertAt = m_aPos[iPosParent].StartAfter() - 1;
nReplace = 0;
m_aPos[iPosParent].nFlags ^= MNF_NONENDED;
}
else
{
nInsertAt = m_aPos[iPosParent].StartAfter() - 2;
nReplace = 1;
m_aPos[iPosParent].AdjustStartTagLen( -1 );
}
m_aPos[iPosParent].SetEndTagLen( 3 + strTagName.GetLength() );
}
else
{
if ( node.nFlags & MNF_REPLACE )
{
nInsertAt = m_aPos[iPosParent].StartContent();
nReplace = m_aPos[iPosParent].ContentLen();
}
else if ( bNoContentParentTags )
{
node.strMeta = x_EOL + node.strMeta;
nInsertAt = m_aPos[iPosParent].StartContent();
}
}
x_DocChange( nInsertAt, nReplace, node.strMeta );
return nReplace;
}
bool CXMLParse::x_AddElem( const char * szName, int nValue, int nFlags )
{
// Convert integer to string
_TCHAR szVal[25];
_stprintf( szVal, _T("%d"), nValue );
return x_AddElem( szName, szVal, nFlags );
}
bool CXMLParse::x_AddElem( const char * szName, const char * szValue, int nFlags )
{
if ( nFlags & MNF_CHILD )
{
// Adding a child element under main position
if ( ! m_iPos )
return false;
}
// Locate where to add element relative to current node
NodePos node( nFlags );
int iPosParent, iPosBefore;
if ( nFlags & MNF_CHILD )
{
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
node.nStart = m_nNodeOffset;
node.nLength = m_nNodeLength;
}
// Cannot have data in non-ended element
if ( (nFlags&MNF_WITHNOEND) && szValue && szValue[0] )
return false;
// Allocate ElemPos structure for this element
int iPos = x_GetFreePos();
// Create string for insert
// If no szValue is specified, an empty element is created
// i.e. either <NAME>value</NAME> or <NAME/>
//
ElemPos* pElem = &m_aPos[iPos];
int nLenName = (int)_tcslen(szName);
if ( ! szValue || ! szValue[0] )
{
// <NAME/> empty element
node.strMeta = _T("<");
node.strMeta += szName;
if ( nFlags & MNF_WITHNOEND )
{
node.strMeta += _T(">");
pElem->SetStartTagLen( nLenName + 2 );
pElem->nLength = nLenName + 2;
}
else
{
if ( nFlags & MNF_WITHXHTMLSPACE )
{
node.strMeta += _T(" />");
pElem->SetStartTagLen( nLenName + 4 );
pElem->nLength = nLenName + 4;
}
else
{
node.strMeta += _T("/>");
pElem->SetStartTagLen( nLenName + 3 );
pElem->nLength = nLenName + 3;
}
}
pElem->SetEndTagLen( 0 );
}
else
{
// <NAME>value</NAME>
CString strValue;
if ( nFlags & MNF_WITHCDATA )
strValue = x_EncodeCDATASection( szValue );
else
strValue = EscapeText( szValue, nFlags ).c_str();
int nLenValue = strValue.GetLength();
node.strMeta = _T("<");
node.strMeta += szName;
node.strMeta += _T(">");
node.strMeta += strValue;
node.strMeta += _T("</");
node.strMeta += szName;
node.strMeta += _T(">");
pElem->SetEndTagLen( nLenName + 3 );
pElem->nLength = nLenName * 2 + nLenValue + 5;
pElem->SetStartTagLen( nLenName + 2 );
}
// Insert
int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
pElem->nStart = node.nStart;
pElem->iElemChild = 0;
if ( nFlags & MNF_WITHNOEND )
pElem->nFlags = MNF_NONENDED;
else
pElem->nFlags = 0;
x_LinkElem( iPosParent, iPosBefore, iPos );
x_Adjust( iPos, node.strMeta.GetLength() - nReplace );
if ( nFlags & MNF_CHILD )
x_SetPos( m_iPosParent, iPosParent, iPos );
else
x_SetPos( iPosParent, iPos, 0 );
return true;
}
string CXMLParse::x_GetSubDoc( int iPos ) const
{
if ( iPos )
{
int nStart = m_aPos[iPos].nStart;
int nNext = nStart + m_aPos[iPos].nLength;
LPCTSTR szDoc = (LPCTSTR)m_strDoc;
int nChar = nNext;
if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
nNext = nChar;
return string(m_strDoc.Mid( nStart, nNext - nStart ));
}
return _T("");
}
string CXMLParse::GetError() const
{
string s = this->m_strError;
return s;
}
bool CXMLParse::x_AddSubDoc( const char * szSubDoc, int nFlags )
{
// Add subdocument, parse, and modify positions of affected elements
//
NodePos node( nFlags );
int iPosParent, iPosBefore;
if ( nFlags & MNF_CHILD )
{
// Add a subdocument under main position, before or after child
if ( ! m_iPos )
return false;
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
// Add a subdocument under parent position, before or after main
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
node.nStart = m_nNodeOffset;
node.nLength = m_nNodeLength;
}
// Parse subdocument
bool bWellFormed = true;
TokenPos token( szSubDoc, m_nFlags );
int iPosVirtual = x_GetFreePos();
m_aPos[iPosVirtual].ClearVirtualParent();
m_aPos[iPosVirtual].SetLevel( m_aPos[iPosParent].Level() + 1 );
int iPos = x_ParseElem( iPosVirtual, token );
if ( (!iPos) || m_aPos[iPosVirtual].nFlags & MNF_ILLFORMED )
bWellFormed = false;
if ( m_aPos[iPosVirtual].nFlags & MNF_ILLDATA )
m_aPos[iPosParent].nFlags |= MNF_ILLDATA;
// Extract subdocument without leading/trailing nodes
int nExtractStart = 0;
int iPosLast = m_aPos[iPos].iElemPrev;
if ( bWellFormed )
{
nExtractStart = m_aPos[iPos].nStart;
int nExtractLength = m_aPos[iPos].nLeng
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -