📄 hzxml.cpp
字号:
++n;
// Are we still within the document?
token.bIsString = false;
if ( szDoc[n] )
{
// Is it an opening quote?
if ( szDoc[n] == _T('\"') )
{
// Move past opening quote
++n;
token.nL = n;
// Look for closing quote
x_FindChar( n, _T('\"') );
// Set right to before closing quote
token.nR = n-1;
// Set n past closing quote unless at end of document
if ( szDoc[n] )
++n;
// Set flag
token.bIsString = true;
}
else
{
// Go until special char or whitespace
token.nL = n;
while ( szDoc[n] &&
csSpecial.Find(m_csDoc[n]) == -1 &&
csWhitespace.Find(m_csDoc[n]) == -1
)
n += _tclen(&szDoc[n]);
// Adjust end position if it is one special char
if ( n == token.nL )
++n; // it is a special char
token.nR = n-1;
}
}
token.nNext = n;
if ( ! szDoc[n] )
return false;
// nNext points to one past last char of token
return true;
}
CString CHzXML::x_GetToken( const CHzXML::TokenPos& token ) const
{
// The token contains indexes into the document identifying a small substring
// Build the substring from those indexes and return it
if ( ! token.IsValid() )
return _T("");
return m_csDoc.Mid( token.nL,
token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) );
}
CString CHzXML::x_GetTagName( int iPos ) const
{
// Return the tag name at specified element
TokenPos token;
token.nNext = m_aPos[iPos].nStartL + 1;
if ( ! iPos || ! x_FindToken( token ) )
return _T("");
// Return substring of document
return x_GetToken( token );
}
bool CHzXML::x_FindAttrib( CHzXML::TokenPos& token, LPCTSTR szAttrib ) const
{
// If szAttrib is NULL find next attrib, otherwise find named attrib
// Return true if found
int nAttrib = 0;
for ( int nCount = 0; x_FindToken(token); ++nCount )
{
if ( ! token.bIsString )
{
// Is it the right angle bracket?
if ( m_csDoc[token.nL] == _T('>') || m_csDoc[token.nL] == _T('/') )
break; // attrib not found
// Equal sign
if ( m_csDoc[token.nL] == _T('=') )
continue;
// Potential attribute
if ( ! nAttrib && nCount )
{
// Attribute name search?
if ( ! szAttrib || ! szAttrib[0] )
return true; // return with token at attrib name
// Compare szAttrib
if ( x_GetToken(token) == szAttrib )
nAttrib = nCount;
}
}
else if ( nAttrib && nCount == nAttrib + 2 )
{
return true;
}
}
// Not found
return false;
}
CString CHzXML::x_GetAttrib( int iPos, LPCTSTR szAttrib ) const
{
// Return the value of the attrib at specified element
TokenPos token;
token.nNext = m_aPos[iPos].nStartL + 1;
if ( szAttrib && x_FindAttrib( token, szAttrib ) )
return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) );
return _T("");
}
CString CHzXML::x_GetData( int iPos ) const
{
// Return a string representing data between start and end tag
// Return empty string if there are any children elements
if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() )
{
// See if it is a CDATA section
TokenPos token;
token.nNext = m_aPos[iPos].nStartR+1;
if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<')
&& token.nL + 11 < m_aPos[iPos].nEndL
&& _tcsncmp( &((LPCTSTR)m_csDoc)[token.nL+1], _T("![CDATA["), 8 ) == 0 )
{
int nEndCDATA = m_csDoc.Find( _T("]]>"), token.nNext );
if ( nEndCDATA != -1 && nEndCDATA < m_aPos[iPos].nEndL )
{
return m_csDoc.Mid( token.nL+9, nEndCDATA-token.nL-9 );
}
}
return x_TextFromDoc( m_aPos[iPos].nStartR+1, m_aPos[iPos].nEndL-1 );
}
return "";
}
CString CHzXML::x_TextToDoc( LPCTSTR szText, bool bAttrib ) const
{
// Convert text as seen outside XML document to XML friendly
// replacing special characters with ampersand escape codes
// E.g. convert "6>7" to "6>7"
//
// < less than
// & ampersand
// > greater than
//
// and for attributes:
//
// ' apostrophe or single quote
// " double quote
//
static _TCHAR* szaReplace[] = { _T("<"),_T("&"),_T(">"),_T("'"),_T(""") };
const _TCHAR* pFind = bAttrib?_T("<&>\'\""):_T("<&>");
CString csText;
const _TCHAR* pSource = szText;
int nDestSize = _tcslen(pSource);
nDestSize += nDestSize / 10 + 7;
_TCHAR* pDest = csText.GetBuffer(nDestSize);
int nLen = 0;
_TCHAR cSource = *pSource;
_TCHAR* pFound;
while ( cSource )
{
if ( nLen > nDestSize - 6 )
{
csText.ReleaseBuffer(nLen);
nDestSize *= 2;
pDest = csText.GetBuffer(nDestSize);
}
if ( (pFound=_tcschr(pFind,cSource)) != NULL )
{
pFound = szaReplace[pFound-pFind];
_tcscpy(&pDest[nLen],pFound);
nLen += _tcslen(pFound);
}
else
{
_tccpy( &pDest[nLen], pSource );
++nLen;
}
pSource += _tclen( pSource );
cSource = *pSource;
}
csText.ReleaseBuffer(nLen);
return csText;
}
CString CHzXML::x_TextFromDoc( int nLeft, int nRight ) const
{
// Convert XML friendly text to text as seen outside XML document
// replacing ampersand escape codes with special characters
// E.g. convert "6>7" to "6>7"
//
// Conveniently the result is always the same or shorter in length
//
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 CHzXML::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 CHzXML::x_Adjust( int iPos, int nShift )
{
// Loop through affected elements and adjust indexes
// Does not affect iPos itself
// Algorithm:
// 1. update next siblings and all their children
// 2. then go up a level update end points and to step 1
int iPosTop = m_aPos[iPos].iElemParent;
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 && 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;
}
}
// Shift indexes at iPos
if ( iPos != iPosTop )
{
// Move the start tag indexes
// Don't do this for containing parent tag
m_aPos[iPos].nStartL += nShift;
m_aPos[iPos].nStartR += nShift;
}
// Move end tag indexes
m_aPos[iPos].nEndL += nShift;
m_aPos[iPos].nEndR += nShift;
m_aPos[iPos].nNext += nShift;
}
}
int CHzXML::x_Add( int iPosParent, int iPosBefore, LPCTSTR szName, LPCTSTR szValue )
{
// Create element and modify positions of affected elements
// if iPosBefore is NULL, insert as first element under parent
// If no szValue is specified, an empty element is created
// i.e. either <NAME>value</NAME> or <NAME/>
//
int iPos = x_GetFreePos();
bool bEmptyParent = false;
if ( iPosBefore )
{
// Follow iPosBefore
m_aPos[iPos].nStartL = m_aPos[iPosBefore].nNext;
}
else if ( m_aPos[iPosParent].iElemChild )
{
// Insert before first child of parent
m_aPos[iPos].nStartL = m_aPos[m_aPos[iPosParent].iElemChild].nStartL;
}
else if ( m_aPos[iPosParent].IsEmptyElement() )
{
// Parent has no separate end tag
m_aPos[iPos].nStartL = m_aPos[iPosParent].nStartR + 2;
bEmptyParent = true;
}
else
{
// Parent has content, but no children
m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL;
}
// Set links
m_aPos[iPos].iElemParent = iPosParent;
m_aPos[iPos].iElemChild = 0;
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;
}
// Create string for insert
CString csInsert;
int nLenName = _tcslen(szName);
int nLenValue = szValue? _tcslen(szValue) : 0;
if ( ! nLenValue )
{
// <NAME/> empty element
csInsert.Format( _T("<%s/>\r\n"), szName );
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;
m_aPos[iPos].nNext = m_aPos[iPos].nEndR + 3;
}
else
{
// <NAME>value</NAME>
CString csValue = x_TextToDoc( szValue );
nLenValue = csValue.GetLength();
csInsert.Format( _T("<%s>%s</%s>\r\n"), szName, csValue, szName );
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;
m_aPos[iPos].nNext = m_aPos[iPos].nEndR + 3;
}
// Insert
int nReplace = 0, nLeft = m_aPos[iPos].nStartL;
if ( bEmptyParent )
{
CString csParentTagName = x_GetTagName(iPosParent);
csInsert = _T(">\r\n") + csInsert + _T("</") + csParentTagName;
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 );
// Return the index of the new element
return iPos;
}
int CHzXML::x_AddAttrib( int iPos, LPCTSTR szAttrib, LPCTSTR szValue )
{
// Add attribute to iPos element
CString csInsert;
if ( iPos )
{
// Insert string taking into account whether it is a single tag
CString csValue = x_TextToDoc( szValue, true );
csInsert.Format( _T(" %s=\"%s\""), szAttrib, csValue );
int nL = m_aPos[iPos].nStartR - (m_aPos[iPos].IsEmptyElement()?1:0);
x_DocChange( nL, 0, csInsert );
int nLen = csInsert.GetLength();
m_aPos[iPos].nStartR += nLen;
m_aPos[iPos].nEndL += nLen;
m_aPos[iPos].nEndR += nLen;
m_aPos[iPos].nNext += nLen;
x_Adjust( iPos, nLen );
}
return csInsert.GetLength();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -