Skip to content

Commit 1477d8f

Browse files
committed
Use XPath with element identifiers instead of node indexes
1 parent 49fccf5 commit 1477d8f

File tree

4 files changed

+190
-31
lines changed

4 files changed

+190
-31
lines changed

crengine/include/lvtinydom.h

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,9 +1483,12 @@ class ldomXPointer
14831483
lvPoint toPoint( bool extended=false ) const;
14841484
//#endif
14851485
/// converts to string
1486-
lString16 toString( XPointerMode mode = XPATH_USE_INDEXES) {
1487-
if( XPATH_USE_NAMES==mode )
1488-
return toStringUsingNames();
1486+
lString16 toString( XPointerMode mode = XPATH_USE_NAMES) {
1487+
if( XPATH_USE_NAMES==mode ) {
1488+
if( gDOMVersionRequested >= 20180528)
1489+
return toStringUsingNames();
1490+
return toStringUsingNamesOld();
1491+
}
14891492
return toStringUsingIndexes();
14901493
}
14911494

@@ -1501,11 +1504,11 @@ class ldomXPointer
15011504
lString16 getHRef();
15021505
/// returns href attribute of <A> element, plus xpointer of <A> element itself
15031506
lString16 getHRef(ldomXPointer & a_xpointer);
1504-
/// create a copy of pointer data
1505-
ldomXPointer * clone()
1506-
{
1507-
return new ldomXPointer( _data );
1508-
}
1507+
/// create a copy of pointer data
1508+
ldomXPointer * clone()
1509+
{
1510+
return new ldomXPointer( _data );
1511+
}
15091512
/// returns true if current node is element
15101513
inline bool isElement() const { return !isNull() && getNode()->isElement(); }
15111514
/// returns true if current node is element
@@ -1516,6 +1519,7 @@ class ldomXPointer
15161519
lString16Collection cssFiles; return getHtml(cssFiles, wflags);
15171520
}
15181521
lString16 toStringUsingNames();
1522+
lString16 toStringUsingNamesOld();
15191523
lString16 toStringUsingIndexes();
15201524
};
15211525

@@ -2223,6 +2227,8 @@ class ldomDocument : public lxmlDocBase
22232227
virtual ContinuousOperationResult saveChanges( CRTimerUtil & maxTime, LVDocViewCallback * progressCallback=NULL );
22242228
#endif
22252229

2230+
ldomXPointer createXPointerV1( ldomNode * baseNode, const lString16 & xPointerStr );
2231+
ldomXPointer createXPointerV2( ldomNode * baseNode, const lString16 & xPointerStr );
22262232
protected:
22272233

22282234
#if BUILD_LITE!=1
@@ -2353,7 +2359,13 @@ class ldomDocument : public lxmlDocBase
23532359
}
23542360

23552361
/// create xpointer from relative pointer string
2356-
ldomXPointer createXPointer( ldomNode * baseNode, const lString16 & xPointerStr );
2362+
ldomXPointer createXPointer( ldomNode * baseNode, const lString16 & xPointerStr )
2363+
{
2364+
if( gDOMVersionRequested >= 20180528)
2365+
return createXPointerV2(baseNode, xPointerStr);
2366+
return createXPointerV1(baseNode, xPointerStr);
2367+
}
2368+
23572369
#if BUILD_LITE!=1
23582370
/// create xpointer from doc point
23592371
ldomXPointer createXPointer( lvPoint pt, int direction=0, bool strictBounds=false, ldomNode * from_node=NULL );

crengine/src/hist.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,19 +461,24 @@ void CRFileHistRecord::setLastPos( CRBookmark * bmk )
461461

462462
void CRFileHistRecord::convertBookmarks(ldomDocument *doc)
463463
{
464+
int saveVersion = gDOMVersionRequested;
464465
for ( int i=0; i< getBookmarks().length(); i++) {
465466
CRBookmark * bmk = getBookmarks()[i];
466467

467468
if( bmk->isValid() ) {
468469
if (bmk->getType() != bmkt_lastpos) {
470+
gDOMVersionRequested = saveVersion;
469471
ldomXPointer p = doc->createXPointer(bmk->getStartPos());
470472
if ( !p.isNull() ) {
473+
gDOMVersionRequested = gDOMVersionCurrent;
471474
bmk->setStartPos(p.toString());
472475
}
473476
lString16 endPos = bmk->getEndPos();
474477
if( !endPos.empty() ) {
478+
gDOMVersionRequested = saveVersion;
475479
p = doc->createXPointer(endPos);
476480
if( !p.isNull() ) {
481+
gDOMVersionRequested = gDOMVersionCurrent;
477482
bmk->setEndPos(p.toString());
478483
}
479484
}

crengine/src/lvdocview.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3754,8 +3754,7 @@ static bool needToConvertBookmarks(CRFileHistRecord* historyRecord)
37543754
if(historyRecord) {
37553755
gDOMVersionRequested = historyRecord->getDOMversion();
37563756
if(gDOMVersionRequested < 20180528) {
3757-
convertBookmarks = gDOMVersionRequested < 20180528 &&
3758-
historyRecord->getBookmarks().length() > 1;
3757+
convertBookmarks = historyRecord->getBookmarks().length() > 1;
37593758
}
37603759
} else
37613760
gDOMVersionRequested = gDOMVersionCurrent;
@@ -3825,6 +3824,7 @@ bool LVDocView::LoadDocument(const lChar16 * fname, bool metadataOnly) {
38253824
if(convertBookmarks) {
38263825
record->convertBookmarks(m_doc);
38273826
record->setDOMversion(gDOMVersionCurrent);
3827+
gDOMVersionRequested = gDOMVersionCurrent;
38283828
m_doc_props->setInt(PROP_RENDER_BLOCK_RENDERING_FLAGS, DEF_RENDER_BLOCK_RENDERING_FLAGS);
38293829
//FIXME: need to reload file after this
38303830
}
@@ -3879,9 +3879,11 @@ bool LVDocView::LoadDocument(const lChar16 * fname, bool metadataOnly) {
38793879
if (LoadDocument(stream, metadataOnly)) {
38803880
m_filename = lString16(fname);
38813881
m_stream.Clear();
3882+
38823883
if(convertBookmarks) {
38833884
record->convertBookmarks(m_doc);
38843885
record->setDOMversion(gDOMVersionCurrent);
3886+
gDOMVersionRequested = gDOMVersionCurrent;
38853887
m_doc_props->setIntDef(PROP_RENDER_BLOCK_RENDERING_FLAGS, DEF_RENDER_BLOCK_RENDERING_FLAGS);
38863888
//FIXME: need to reload file after this
38873889
}

crengine/src/lvtinydom.cpp

Lines changed: 160 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7438,27 +7438,50 @@ static bool isBoxingNode(ldomNode * pNode)
74387438
return false;
74397439
}
74407440

7441-
static ldomNode * getNodebyIndex(ldomNode *parent, int index, int& count)
7441+
static bool isTextNode(ldomNode * node)
7442+
{
7443+
return (node && node->isText());
7444+
}
7445+
7446+
struct ldomNodeIdPredicate
7447+
{
7448+
lUInt16 m_id;
7449+
ldomNodeIdPredicate(lUInt16 id) : m_id(id) {}
7450+
bool operator() (ldomNode * node) {
7451+
return (node && node->getNodeId() == m_id);
7452+
}
7453+
};
7454+
7455+
static bool notNull(ldomNode * node)
7456+
{
7457+
return (NULL != node);
7458+
}
7459+
7460+
template<typename T>
7461+
static ldomNode * getNodebyIndex(ldomNode *parent, int index, T predicat, int& count)
74427462
{
74437463
ldomNode *foundNode = NULL;
74447464

74457465
for( int i=0; i < (int)parent->getChildCount(); i++) {
74467466
ldomNode * p = parent->getChildNode(i);
7447-
if( isBoxingNode(p) )
7448-
foundNode = getNodebyIndex(p, index, count);
7449-
else
7467+
if( isBoxingNode(p) ) {
7468+
foundNode = getNodebyIndex(p, index, predicat, count);
7469+
if( foundNode )
7470+
return foundNode;
7471+
} else if(predicat(p)) {
74507472
count++;
7451-
if(count == index) {
7452-
if( !foundNode )
7453-
foundNode = p;
7454-
break;
7473+
if(index == -1 || count == index) {
7474+
if( !foundNode )
7475+
foundNode = p;
7476+
return foundNode;
7477+
}
74557478
}
74567479
}
7457-
return foundNode;
7480+
return NULL;
74587481
}
74597482

74607483
/// create xpointer from relative pointer string
7461-
ldomXPointer ldomDocument::createXPointer( ldomNode * baseNode, const lString16 & xPointerStr )
7484+
ldomXPointer ldomDocument::createXPointerV1( ldomNode * baseNode, const lString16 & xPointerStr )
74627485
{
74637486
//CRLog::trace( "ldomDocument::createXPointer(%s)", UnicodeToUtf8(xPointerStr).c_str() );
74647487
if ( xPointerStr.empty() || !baseNode )
@@ -7535,14 +7558,86 @@ ldomXPointer ldomDocument::createXPointer( ldomNode * baseNode, const lString16
75357558
break;
75367559
case xpath_step_nodeindex:
75377560
// node index /N/
7561+
if ( index<=0 || index>(int)currNode->getChildCount() )
7562+
return ldomXPointer(); // node not found: invalid index
7563+
currNode = currNode->getChildNode( index-1 );
7564+
break;
7565+
case xpath_step_point:
7566+
// point index .N
7567+
if (*str)
7568+
return ldomXPointer(); // not at end of string
7569+
if ( currNode->isElement() ) {
7570+
// element point
7571+
if ( index<0 || index>(int)currNode->getChildCount() )
7572+
return ldomXPointer();
7573+
return ldomXPointer(currNode, index);
7574+
} else {
7575+
// text point
7576+
if ( index<0 || index>(int)currNode->getText().length() )
7577+
return ldomXPointer();
7578+
return ldomXPointer(currNode, index);
7579+
}
7580+
break;
7581+
}
7582+
}
7583+
return ldomXPointer( currNode, -1 ); // XPath: index==-1
7584+
}
7585+
7586+
ldomXPointer ldomDocument::createXPointerV2( ldomNode * baseNode, const lString16 & xPointerStr )
7587+
{
7588+
//CRLog::trace( "ldomDocument::createXPointer(%s)", UnicodeToUtf8(xPointerStr).c_str() );
7589+
if ( xPointerStr.empty() || !baseNode )
7590+
return ldomXPointer();
7591+
const lChar16 * str = xPointerStr.c_str();
7592+
int index = -1;
7593+
int count;
7594+
ldomNode * currNode = baseNode;
7595+
ldomNode * foundNode;
7596+
lString16 name;
7597+
xpath_step_t step_type;
7598+
7599+
while ( *str ) {
7600+
//CRLog::trace( " %s", UnicodeToUtf8(lString16(str)).c_str() );
7601+
step_type = ParseXPathStep( str, name, index );
7602+
//CRLog::trace( " name=%s index=%d", UnicodeToUtf8(lString16(name)).c_str(), index );
7603+
switch (step_type ) {
7604+
case xpath_step_error:
7605+
// error
7606+
//CRLog::trace(" xpath_step_error");
7607+
return ldomXPointer();
7608+
case xpath_step_element:
7609+
// element of type 'name' with 'index' /elemname[N]/
75387610
{
7539-
int count = 0;
7540-
ldomNode * foundItem = getNodebyIndex(currNode, index, count);
7541-
if ( foundItem == NULL )
7542-
return ldomXPointer(); // node not found: invalid index
7543-
currNode = foundItem;
7611+
ldomNodeIdPredicate predicat(getElementNameIndex( name.c_str() ));
7612+
count = 0;
7613+
foundNode = getNodebyIndex(currNode, index, predicat, count);
7614+
if (foundNode == NULL) {
7615+
//CRLog::trace(" Element %d is not found. foundCount=%d", id, foundCount);
7616+
return ldomXPointer(); // node not found
7617+
}
7618+
// found element node
7619+
currNode = foundNode;
7620+
lString16 nm = currNode->getNodeName();
7621+
CRLog::trace("%d -> %s", index, LCSTR(nm));
75447622
}
75457623
break;
7624+
case xpath_step_text:
7625+
//
7626+
count = 0;
7627+
foundNode = getNodebyIndex(currNode, index, isTextNode, count);
7628+
7629+
if ( foundNode==NULL )
7630+
return ldomXPointer(); // node not found
7631+
// found text node
7632+
currNode = foundNode;
7633+
break;
7634+
case xpath_step_nodeindex:
7635+
// node index /N/
7636+
foundNode = getNodebyIndex(currNode, index, notNull, count);
7637+
if ( foundNode == NULL )
7638+
return ldomXPointer(); // node not found: invalid index
7639+
currNode = foundNode;
7640+
break;
75467641
case xpath_step_point:
75477642
// point index .N
75487643
if (*str)
@@ -7595,7 +7690,7 @@ lString16 ldomNode::getXPathSegment()
75957690
return lString16::empty_str;
75967691
}
75977692

7598-
lString16 ldomXPointer::toStringUsingNames()
7693+
lString16 ldomXPointer::toStringUsingNamesOld()
75997694
{
76007695
lString16 path;
76017696
if ( isNull() )
@@ -7653,22 +7748,67 @@ lString16 ldomXPointer::toStringUsingNames()
76537748
return path;
76547749
}
76557750

7656-
static int getNodeIndex(ldomNode* parent, ldomNode *targetNode, int& count)
7751+
template<typename T>
7752+
static int getElementIndex(ldomNode* parent, ldomNode *targetNode, T predicat, int& count)
76577753
{
76587754
for ( int i=0; i<parent->getChildCount(); i++ ) {
76597755
ldomNode * node = parent->getChildNode( i );
76607756
if( isBoxingNode(node) && targetNode != node ) {
7661-
int index = getNodeIndex(node, targetNode, count);
7757+
int index = getElementIndex(node, targetNode, predicat, count);
76627758
if(index > 0)
76637759
return index;
7664-
} else
7760+
} else if (predicat(node))
76657761
count++;
76667762
if ( node==targetNode )
76677763
return count;
76687764
}
76697765
return -1;
76707766
}
76717767

7768+
lString16 ldomXPointer::toStringUsingNames()
7769+
{
7770+
lString16 path;
7771+
if ( isNull() )
7772+
return path;
7773+
ldomNode * node = getNode();
7774+
int offset = getOffset();
7775+
if ( offset >= 0 ) {
7776+
path << "." << fmt::decimal(offset);
7777+
}
7778+
ldomNode * p = node;
7779+
ldomNode * mainNode = node->getDocument()->getRootNode();
7780+
while (p && p!=mainNode) {
7781+
ldomNode * parent = p->getParentNode();
7782+
while( isBoxingNode(parent) )
7783+
parent = parent->getParentNode();
7784+
if ( p->isElement() ) {
7785+
// element
7786+
lString16 name = p->getNodeName();
7787+
if ( !parent )
7788+
return "/" + name + path;
7789+
int count = 0;
7790+
ldomNodeIdPredicate predicat(p->getNodeId());
7791+
int index = getElementIndex(parent, p, predicat, count);
7792+
if ( count>1 )
7793+
path = cs16("/") + name + "[" + fmt::decimal(index) + "]" + path;
7794+
else
7795+
path = cs16("/") + name + path;
7796+
} else {
7797+
// text
7798+
if ( !parent )
7799+
return cs16("/text()") + path;
7800+
int count = 0;
7801+
int index = getElementIndex(parent, p, isTextNode, count);
7802+
if ( count>1 )
7803+
path = cs16("/text()") + "[" + fmt::decimal(index) + "]" + path;
7804+
else
7805+
path = "/text()" + path;
7806+
}
7807+
p = parent;
7808+
}
7809+
return path;
7810+
}
7811+
76727812
lString16 ldomXPointer::toStringUsingIndexes()
76737813
{
76747814
lString16 path;
@@ -7689,7 +7829,7 @@ lString16 ldomXPointer::toStringUsingIndexes()
76897829
parent = parent->getParentNode();
76907830

76917831
int count = 0;
7692-
int index = getNodeIndex(parent, p, count);
7832+
int index = getElementIndex(parent, p, notNull, count);
76937833

76947834
if( index>0 ) {
76957835
path = cs16("/") + fmt::decimal(index) + path;

0 commit comments

Comments
 (0)