📄 markup.cpp
字号:
while ( *pChar && *pChar != c )
pChar += _tclen( pChar );
nChar = pChar - szDoc;
if ( ! *pChar )
return false;
/*
while ( szDoc[nChar] && szDoc[nChar] != c )
nChar += _tclen( &szDoc[nChar] );
if ( ! szDoc[nChar] )
return false;
*/
return true;
}
bool CMarkup::x_FindToken( CMarkup::TokenPos& token )
{
// Starting at token.nNext, bypass whitespace and find the next token
// returns true on success, members of token point to token
// returns false on end of document, members point to end of document
LPCTSTR szDoc = token.szDoc;
int nChar = token.nNext;
token.bIsString = false;
// By-pass leading whitespace
while ( szDoc[nChar] && _tcschr(_T(" \t\n\r"),szDoc[nChar]) )
++nChar;
if ( ! szDoc[nChar] )
{
// No token was found before end of document
token.nL = nChar;
token.nR = nChar;
token.nNext = nChar;
return false;
}
// Is it an opening quote?
_TCHAR cFirstChar = szDoc[nChar];
if ( cFirstChar == _T('\"') || cFirstChar == _T('\'') )
{
token.bIsString = true;
// Move past opening quote
++nChar;
token.nL = nChar;
// Look for closing quote
x_FindChar( token.szDoc, nChar, cFirstChar );
// Set right to before closing quote
token.nR = nChar - 1;
// Set nChar past closing quote unless at end of document
if ( szDoc[nChar] )
++nChar;
}
else
{
// Go until special char or whitespace
token.nL = nChar;
while ( szDoc[nChar] && ! _tcschr(_T(" \t\n\r<>=\\/?!"),szDoc[nChar]) )
nChar += _tclen(&szDoc[nChar]);
// Adjust end position if it is one special char
if ( nChar == token.nL )
++nChar; // it is a special char
token.nR = nChar - 1;
}
// nNext points to one past last char of token
token.nNext = nChar;
return true;
}
CString CMarkup::x_GetToken( const CMarkup::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.nL > token.nR )
return _T("");
return m_csDoc.Mid( token.nL,
token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) );
}
int CMarkup::x_FindElem( int iPosParent, int iPos, LPCTSTR szPath )
{
// If szPath is NULL or empty, go to next sibling element
// Otherwise go to next sibling element with matching path
//
if ( iPos )
iPos = m_aPos[iPos].iElemNext;
else
iPos = m_aPos[iPosParent].iElemChild;
// Finished here if szPath not specified
if ( szPath == NULL || !szPath[0] )
return iPos;
// Search
TokenPos token( m_csDoc );
while ( iPos )
{
// Compare tag name
token.nNext = m_aPos[iPos].nStartL + 1;
x_FindToken( token ); // Locate tag name
if ( token.Match(szPath) )
return iPos;
iPos = m_aPos[iPos].iElemNext;
}
return 0;
}
int CMarkup::x_ParseNode( CMarkup::TokenPos& token )
{
// Call this with token.nNext set to the start of the node
// This returns the node type and token.nNext set to the char after the node
// If the node is not found or an element, token.nR is not determined
// White space between elements is a text node
int nTypeFound = 0;
LPCTSTR szDoc = token.szDoc;
token.nL = token.nNext;
if ( szDoc[token.nL] == '<' )
{
// Started with <, could be:
// <!--...--> comment
// <!DOCTYPE ...> dtd
// <?target ...?> processing instruction
// <![CDATA[...]]> cdata section
// <NAME ...> element
//
if ( ! szDoc[token.nL+1] || ! szDoc[token.nL+2] )
return 0;
_TCHAR cFirstChar = szDoc[token.nL+1];
LPCTSTR szEndOfNode = NULL;
if ( cFirstChar == _T('?') )
{
nTypeFound = MNT_PROCESSING_INSTRUCTION; // processing instruction
szEndOfNode = _T("?>");
}
else if ( cFirstChar == _T('!') )
{
_TCHAR cSecondChar = szDoc[token.nL+2];
if ( cSecondChar == _T('[') )
{
nTypeFound = MNT_CDATA_SECTION;
szEndOfNode = _T("]]>");
}
else if ( cSecondChar == _T('-') )
{
nTypeFound = MNT_COMMENT;
szEndOfNode = _T("-->");
}
else
{
// Document type requires tokenizing because of strings and brackets
nTypeFound = 0;
int nBrackets = 0;
while ( x_FindToken(token) )
{
if ( ! token.bIsString )
{
_TCHAR cChar = szDoc[token.nL];
if ( cChar == _T('[') )
++nBrackets;
else if ( cChar == _T(']') )
--nBrackets;
else if ( nBrackets == 0 && cChar == _T('>') )
{
nTypeFound = MNT_DOCUMENT_TYPE;
break;
}
}
}
if ( ! nTypeFound )
return 0;
}
}
else if ( cFirstChar == _T('/') )
{
// End tag means no node found within parent element
return 0;
}
else
{
nTypeFound = MNT_ELEMENT;
}
// Search for end of node if not found yet
if ( szEndOfNode )
{
LPCTSTR pEnd = _tcsstr( &szDoc[token.nNext], szEndOfNode );
if ( ! pEnd )
return 0; // not well-formed
token.nNext = (pEnd - szDoc) + _tcslen(szEndOfNode);
}
}
else if ( szDoc[token.nL] )
{
// It is text or whitespace because it did not start with <
nTypeFound = MNT_WHITESPACE;
if ( x_FindToken(token) )
{
if ( szDoc[token.nL] == _T('<') )
token.nNext = token.nL;
else
{
nTypeFound = MNT_TEXT;
x_FindChar( token.szDoc, token.nNext, _T('<') );
}
}
}
return nTypeFound;
}
CString CMarkup::x_GetTagName( int iPos ) const
{
// Return the tag name at specified element
TokenPos token( m_csDoc );
token.nNext = m_aPos[iPos].nStartL + 1;
if ( ! iPos || ! x_FindToken( token ) )
return _T("");
// Return substring of document
return x_GetToken( token );
}
bool CMarkup::x_FindAttr( CMarkup::TokenPos& token, LPCTSTR szAttr ) const
{
// If szAttr is NULL find next Attr, otherwise find named Attr
// Return true if found
int nAttr = 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; // Attr not found
// Equal sign
if ( m_csDoc[token.nL] == _T('=') )
continue;
// Potential Attrute
if ( ! nAttr && nCount )
{
// Attrute name search?
if ( ! szAttr || ! szAttr[0] )
return true; // return with token at Attr name
// Compare szAttr
if ( token.Match(szAttr) )
nAttr = nCount;
}
}
else if ( nAttr && nCount == nAttr + 2 )
{
return true;
}
}
// Not found
return false;
}
CString CMarkup::x_GetAttr( int iPos, LPCTSTR szAttr ) const
{
// Return the value of the Attr at specified element
if ( ! iPos || m_nNodeType != MNT_ELEMENT )
return _T("");
TokenPos token( m_csDoc );
token.nNext = m_aPos[iPos].nStartL + 1;
if ( szAttr && x_FindAttr( token, szAttr ) )
return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) );
return _T("");
}
bool CMarkup::x_SetAttr( int iPos, LPCTSTR szAttr, int nValue )
{
// Convert integer to string and call SetChildAttr
_TCHAR szVal[25];
_stprintf( szVal, _T("%d"), nValue );
return x_SetAttr( iPos, szAttr, szVal );
}
bool CMarkup::x_SetAttr( int iPos, LPCTSTR szAttr, LPCTSTR szValue )
{
// Set Attrute in iPos element
if ( ! iPos || m_nNodeType != MNT_ELEMENT )
return false;
TokenPos token( m_csDoc );
token.nNext = m_aPos[iPos].nStartL + 1;
int nInsertAt, nReplace = 0;
CString csInsert;
if ( x_FindAttr( token, szAttr ) )
{
// Decision: for empty value leaving Attr="" instead of removing Attr
// Replace value only
csInsert = x_TextToDoc( szValue, true );
nInsertAt = token.nL;
nReplace = token.nR-token.nL+1;
}
else
{
// Insert string name value pair
CString csFormat;
csFormat = _T(" ");
csFormat += szAttr;
csFormat += _T("=\"");
csFormat += x_TextToDoc( szValue, true );
csFormat += _T("\"");
csInsert = csFormat;
// take into account whether it is an empty element
nInsertAt = m_aPos[iPos].nStartR - (m_aPos[iPos].IsEmptyElement()?1:0);
}
x_DocChange( nInsertAt, nReplace, csInsert );
int nAdjust = csInsert.GetLength() - nReplace;
m_aPos[iPos].nStartR += nAdjust;
m_aPos[iPos].AdjustEnd( nAdjust );
x_Adjust( iPos, nAdjust );
MARKUP_SETDEBUGSTATE;
return true;
}
bool CMarkup::x_CreateNode( CString& csNode, int nNodeType, LPCTSTR szText )
{
// Set csNode based on nNodeType and szData
// Return false if szData would jeopardize well-formed document
//
switch ( nNodeType )
{
case MNT_CDATA_SECTION:
if ( _tcsstr(szText,_T("]]>")) != NULL )
return false;
csNode = "<![CDATA[";
csNode += szText;
csNode += "]]>";
break;
}
return true;
}
bool CMarkup::x_SetData( int iPos, LPCTSTR szData, int nCDATA )
{
// Set data at specified position
// if nCDATA==1, set content of element to a CDATA Section
CString csInsert;
// Set data in iPos element
if ( ! iPos || m_aPos[iPos].iElemChild )
return false;
// Build csInsert from szData based on nCDATA
// If CDATA section not valid, use parsed text (PCDATA) instead
if ( nCDATA != 0 )
if ( ! x_CreateNode(csInsert, MNT_CDATA_SECTION, szData) )
nCDATA = 0;
if ( nCDATA == 0 )
csInsert = x_TextToDoc( szData );
// Decide where to insert
int nInsertAt, nReplace;
if ( m_aPos[iPos].IsEmptyElement() )
{
nInsertAt = m_aPos[iPos].nEndL;
nReplace = 1;
// Pre-adjust since <NAME/> becomes <NAME>data</NAME>
CString csTagName = x_GetTagName( iPos );
m_aPos[iPos].nStartR -= 1;
m_aPos[iPos].nEndL -= (1 + csTagName.GetLength());
CString csFormat;
csFormat = _T(">");
csFormat += csInsert;
csFormat += _T("</");
csFormat += csTagName;
csInsert = csFormat;
}
else
{
nInsertAt = m_aPos[iPos].nStartR+1;
nReplace = m_aPos[iPos].nEndL - m_aPos[iPos].nStartR - 1;
}
x_DocChange( nInsertAt, nReplace, csInsert );
int nAdjust = csInsert.GetLength() - nReplace;
x_Adjust( iPos, nAdjust );
m_aPos[iPos].AdjustEnd( nAdjust );
MARKUP_SETDEBUGSTATE;
return true;
}
CString CMarkup::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( m_csDoc );
token.nNext = m_aPos[iPos].nStartR+1;
if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<')
&& token.nL + 11 < m_aPos[iPos].nEndL
&& _tcsncmp( &token.szDoc[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 _T("");
}
CString CMarkup::x_TextToDoc( LPCTSTR szText, bool bAttr ) 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 Attrutes:
//
// ' apostrophe or single quote
// " double quote
//
static _TCHAR* szaReplace[] = { _T("<"),_T("&"),_T(">"),_T("'"),_T(""") };
const _TCHAR* pFind = bAttr?_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 CMarkup::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
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -