📄 markupstl.cpp
字号:
{
if ( iPosNew )
iPosNew = m_aPos[iPosNew].iElemNext;
else
iPosNew = m_aPos[m_iPosParent].iElemChild;
if ( ! iPosNew )
return 0;
if ( ! nType || (nType & nTypeFound) )
{
// Found element node, move position to this element
x_SetPos( m_iPosParent, iPosNew, 0 );
return m_nNodeType;
}
token.nNext = m_aPos[iPosNew].StartAfter();
}
}
while ( nType && ! (nType & nTypeFound) );
m_iPos = iPosNew;
m_iPosChild = 0;
m_nNodeOffset = nNodeOffset;
m_nNodeLength = token.nNext - nNodeOffset;
m_nNodeType = nTypeFound;
MARKUP_SETDEBUGSTATE;
return m_nNodeType;
}
bool CMarkupSTL::RemoveNode()
{
if ( m_iPos || m_nNodeLength )
{
x_RemoveNode( m_iPosParent, m_iPos, m_nNodeType, m_nNodeOffset, m_nNodeLength );
m_iPosChild = 0;
MARKUP_SETDEBUGSTATE;
return true;
}
return false;
}
string CMarkupSTL::GetTagName() const
{
// Return the tag name at the current main position
string strTagName;
// This method is primarily for elements, however
// it does return something for certain other nodes
if ( m_nNodeLength )
{
switch ( m_nNodeType )
{
case MNT_PROCESSING_INSTRUCTION:
case MNT_LONE_END_TAG:
{
// <?target or </tagname
TokenPos token( m_strDoc, m_nFlags );
token.nNext = m_nNodeOffset + 2;
if ( x_FindName(token) )
strTagName = x_GetToken( token );
}
break;
case MNT_COMMENT:
strTagName = "#comment";
break;
case MNT_CDATA_SECTION:
strTagName = "#cdata-section";
break;
case MNT_DOCUMENT_TYPE:
{
// <!DOCTYPE name
TokenPos token( m_strDoc, m_nFlags );
token.nNext = m_nNodeOffset + 2;
if ( x_FindName(token) && x_FindName(token) )
strTagName = x_GetToken( token );
}
break;
case MNT_TEXT:
case MNT_WHITESPACE:
strTagName = "#text";
break;
}
return strTagName;
}
if ( m_iPos )
strTagName = x_GetTagName( m_iPos );
return strTagName;
}
bool CMarkupSTL::IntoElem()
{
// If there is no child position and IntoElem is called it will succeed in release 6.3
// (A subsequent call to FindElem will find the first element)
// The following short-hand behavior was never part of EDOM and was misleading
// It would find a child element if there was no current child element position and go into it
// It is removed in release 6.3, this change is NOT backwards compatible!
// if ( ! m_iPosChild )
// FindChildElem();
if ( m_iPos && m_nNodeType == MNT_ELEMENT )
{
x_SetPos( m_iPos, m_iPosChild, 0 );
return true;
}
return false;
}
bool CMarkupSTL::OutOfElem()
{
// Go to parent element
if ( m_iPosParent )
{
x_SetPos( m_aPos[m_iPosParent].iElemParent, m_iPosParent, m_iPos );
return true;
}
return false;
}
string CMarkupSTL::GetAttribName( int n ) const
{
// Return nth attribute name of main position
TokenPos token( m_strDoc, m_nFlags );
if ( m_iPos && m_nNodeType == MNT_ELEMENT )
token.nNext = m_aPos[m_iPos].nStart + 1;
else if ( m_nNodeLength && m_nNodeType == MNT_PROCESSING_INSTRUCTION )
token.nNext = m_nNodeOffset + 2;
else
return "";
if ( x_FindAttrib(token,NULL,n) )
return x_GetToken( token );
return "";
}
bool CMarkupSTL::SavePos( const char* szPosName )
{
// Save current element position in saved position map
if ( szPosName )
{
SavedPos savedpos;
if ( szPosName )
savedpos.strName = szPosName;
if ( m_iPosChild )
{
savedpos.iPos = m_iPosChild;
savedpos.nSavedPosFlags |= SavedPosMap::SPM_CHILD;
}
else if ( m_iPos )
{
savedpos.iPos = m_iPos;
savedpos.nSavedPosFlags |= SavedPosMap::SPM_MAIN;
}
else
{
savedpos.iPos = m_iPosParent;
}
savedpos.nSavedPosFlags |= SavedPosMap::SPM_USED;
if ( ! m_mapSavedPos.pTable )
m_mapSavedPos.AllocMapTable();
int nSlot = m_mapSavedPos.Hash( szPosName );
SavedPos* pSavedPos = m_mapSavedPos.pTable[nSlot];
int nOffset = 0;
if ( ! pSavedPos )
{
pSavedPos = new SavedPos[2];
pSavedPos[1].nSavedPosFlags = SavedPosMap::SPM_LAST;
m_mapSavedPos.pTable[nSlot] = pSavedPos;
}
else
{
while ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_USED )
{
if ( pSavedPos[nOffset].strName == szPosName )
break;
if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
{
int nNewSize = (nOffset + 6) * 2;
SavedPos* pNewSavedPos = new SavedPos[nNewSize];
for ( int nCopy=0; nCopy<=nOffset; ++nCopy )
pNewSavedPos[nCopy] = pSavedPos[nCopy];
pNewSavedPos[nOffset].nSavedPosFlags ^= SavedPosMap::SPM_LAST;
pNewSavedPos[nNewSize-1].nSavedPosFlags = SavedPosMap::SPM_LAST;
delete [] pSavedPos;
pSavedPos = pNewSavedPos;
m_mapSavedPos.pTable[nSlot] = pSavedPos;
++nOffset;
break;
}
++nOffset;
}
}
if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
savedpos.nSavedPosFlags |= SavedPosMap::SPM_LAST;
pSavedPos[nOffset] = savedpos;
/*
// To review hash table balance, uncomment and watch strBalance
string strBalance;
char szSlot[20];
for ( nSlot=0; nSlot < SavedPosMap::SPM_SIZE; ++nSlot )
{
pSavedPos = m_mapSavedPos.pTable[nSlot];
int nCount = 0;
while ( pSavedPos && pSavedPos->nSavedPosFlags & SavedPosMap::SPM_USED )
{
++nCount;
if ( pSavedPos->nSavedPosFlags & SavedPosMap::SPM_LAST )
break;
++pSavedPos;
}
sprintf( szSlot, "%d ", nCount );
strBalance += szSlot;
}
*/
return true;
}
return false;
}
bool CMarkupSTL::RestorePos( const char* szPosName )
{
// Restore element position if found in saved position map
if ( szPosName && m_mapSavedPos.pTable )
{
int nSlot = m_mapSavedPos.Hash( szPosName );
SavedPos* pSavedPos = m_mapSavedPos.pTable[nSlot];
if ( pSavedPos )
{
int nOffset = 0;
while ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_USED )
{
if ( pSavedPos[nOffset].strName == szPosName )
{
int i = pSavedPos[nOffset].iPos;
if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_CHILD )
x_SetPos( m_aPos[m_aPos[i].iElemParent].iElemParent, m_aPos[i].iElemParent, i );
else if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_MAIN )
x_SetPos( m_aPos[i].iElemParent, i, 0 );
else
x_SetPos( i, 0, 0 );
return true;
}
if ( pSavedPos[nOffset].nSavedPosFlags & SavedPosMap::SPM_LAST )
break;
++nOffset;
}
}
}
return false;
}
bool CMarkupSTL::RemoveElem()
{
// Remove current main position element
if ( m_iPos && m_nNodeType == MNT_ELEMENT )
{
int iPos = x_RemoveElem( m_iPos );
x_SetPos( m_iPosParent, iPos, 0 );
return true;
}
return false;
}
bool CMarkupSTL::RemoveChildElem()
{
// Remove current child position element
if ( m_iPosChild )
{
int iPosChild = x_RemoveElem( m_iPosChild );
x_SetPos( m_iPosParent, m_iPos, iPosChild );
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Private Methods
//////////////////////////////////////////////////////////////////////
string CMarkupSTL::x_GetLastError()
{
string strError = strerror(errno);
for ( int nChar=0; nChar<(int)strError.size(); ++nChar )
if ( strError[nChar] == '\r' || strError[nChar] == '\n' )
{
strError.resize( nChar ); // no trailing newline
break;
}
return strError;
}
bool CMarkupSTL::x_AllocPosArray( int nNewSize /*=0*/ )
{
// Resize m_aPos when the document is created or the array is filled
// The PosArray class is implemented using segments to reduce contiguous memory requirements
// It reduces reallocations (copying of memory) since this only occurs within one segment
// The "Grow By" algorithm ensures there are no reallocations after 2 segments
//
if ( ! nNewSize )
nNewSize = m_iPosFree + (m_iPosFree>>1); // Grow By: multiply size by 1.5
if ( m_aPos.GetSize() < nNewSize )
{
// Grow By: new size can be at most one more complete segment
int nSeg = (m_aPos.GetSize()?m_aPos.GetSize()-1:0) >> m_aPos.PA_SEGBITS;
int nNewSeg = (nNewSize-1) >> m_aPos.PA_SEGBITS;
if ( nNewSeg > nSeg + 1 )
{
nNewSeg = nSeg + 1;
nNewSize = (nNewSeg+1) << m_aPos.PA_SEGBITS;
}
// Allocate array of segments
if ( m_aPos.nSegs <= nNewSeg )
{
int nNewSegments = 4 + nNewSeg * 2;
char* pNewSegments = new char[nNewSegments*sizeof(char*)];
if ( m_aPos.SegsUsed() )
memcpy( pNewSegments, m_aPos.pSegs, m_aPos.SegsUsed()*sizeof(char*) );
if ( m_aPos.pSegs )
delete[] (char*)m_aPos.pSegs;
m_aPos.pSegs = (ElemPos**)pNewSegments;
m_aPos.nSegs = nNewSegments;
}
// Calculate segment sizes
int nSegSize = m_aPos.GetSize() - (nSeg << m_aPos.PA_SEGBITS);
int nNewSegSize = nNewSize - (nNewSeg << m_aPos.PA_SEGBITS);
// Complete first segment
int nFullSegSize = 1 << m_aPos.PA_SEGBITS;
if ( nSeg < nNewSeg && nSegSize < nFullSegSize )
{
char* pNewFirstSeg = new char[ nFullSegSize * sizeof(ElemPos) ];
if ( nSegSize )
{
// Reallocate
memcpy( pNewFirstSeg, m_aPos.pSegs[nSeg], nSegSize * sizeof(ElemPos) );
delete[] (char*)m_aPos.pSegs[nSeg];
}
m_aPos.pSegs[nSeg] = (ElemPos*)pNewFirstSeg;
}
// New segment
char* pNewSeg = new char[ nNewSegSize * sizeof(ElemPos) ];
if ( nNewSeg == nSeg && nSegSize )
{
// Reallocate
memcpy( pNewSeg, m_aPos.pSegs[nSeg], nSegSize * sizeof(ElemPos) );
delete[] (char*)m_aPos.pSegs[nSeg];
}
m_aPos.pSegs[nNewSeg] = (ElemPos*)pNewSeg;
m_aPos.nSize = nNewSize;
}
return true;
}
bool CMarkupSTL::x_ParseDoc()
{
// Preserve pre-parse result
string strResult = m_strError;
// Reset indexes
ResetPos();
m_mapSavedPos.RemoveAll();
// Starting size of position array: 1 element per 64 bytes of document
// Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc
// Start at 8 when creating new document
m_iPosFree = 1;
x_AllocPosArray( (int)m_strDoc.size() / 64 + 8 );
m_iPosDeleted = 0;
// Parse document
m_aPos[0].ClearVirtualParent();
if ( m_strDoc.size() )
{
TokenPos token( m_strDoc, m_nFlags );
int iPos = x_ParseElem( 0, token );
m_aPos[0].nLength = (int)m_strDoc.size();
if ( iPos > 0 )
{
m_aPos[0].iElemChild = iPos;
if ( m_aPos[iPos].iElemNext )
m_strError = "Root element has sibling";
}
else
m_strError = "No root element";
}
else
m_strError = "Empty document";
ResetPos();
// Combine preserved result with parse error
if ( ! strResult.empty() )
{
if ( m_strError.empty() )
m_strError = strResult;
else
m_strError = strResult + ", " + m_strError;
}
return IsWellFormed();
};
int CMarkupSTL::x_ParseElem( int iPosParent, TokenPos& token )
{
// This is either called by x_ParseDoc or x_AddSubDoc or x_SetElemContent
// Returns index of the first element encountered or zero if no elements
//
int iElemRoot = 0;
int iPos = iPosParent;
int iVirtualParent = iPosParent;
int nRootDepth = m_aPos[iPos].Level();
token.nNext = 0;
m_strError.erase();
// Loop through the nodes of the document
NodeStack aNodes;
aNodes.Add();
int nDepth = 0;
int nMatchDepth;
int iPosChild;
int iPosMatch;
int nTypeFound = 0;
ElemPos* pElem;
int iElemFirst, iElemLast;
while ( 1 )
{
nTypeFound = x_ParseNode( token, aNodes.Top() );
nMatchDepth = 0;
if ( nTypeFound == MNT_ELEMENT ) // start tag
{
iPos = x_GetFreePos();
if ( ! iElemRoot )
iElemRoot = iPos;
pElem = &m_aPos[iPos];
pElem->iElemParent = iPosParent;
pElem->iElemNext = 0;
if ( m_aPos[iPosParent].iElemChild )
{
iElemFirst = m_aPos[iPosParent].iElemChild;
iElemLast = m_aPos[iElemFirst].iElemPrev;
m_aPos[iElemLast].iElemNext = iPos;
pElem->iElemPrev = iElemLast;
m_aPos[iElemFirst].iElemPrev = iPos;
pElem->nFlags = 0;
}
else
{
m_aPos[iPosParent].iElemChild = iPos;
pElem->iElemPrev = iPos;
pElem->nFlags = MNF_FIRST;
}
pElem->SetLevel( nRootDepth + nDepth );
pElem->iElemChild = 0;
pElem->nStart = aNodes.Top().nStart;
pElem->SetStartTagLen( aNodes.Top().nLength );
if ( aNodes.Top().nFlags & MNF_EMPTY )
{
iPos = iPosParent;
pElem->SetEndTagLen( 0 );
pElem->nLength = aNodes.Top().nLength;
}
else
{
iPosParent = iPos;
++nDepth;
aNodes.Add();
}
}
else if ( nTypeFound == 0 ) // end tag
{
nMatchDepth = nDepth;
iPosMatch = iPos;
while ( nMatchDepth && ! token.Match(aNodes.At(nMatchDepth-1).strMeta) )
{
/*
// Auto-switch case sensitivity
if ( ! (token.nTokenFlags & MDF_IGNORECASE ) )
{
token.nTokenFlags |= MDF_IGNORECASE;
if ( token.Match(aNodes.At(nMatchDepth-1).strMeta) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -