Skip to content

Commit 8aa4695

Browse files
[test] Fix failing fn:element-with-id tests
1 parent 08feb75 commit 8aa4695

5 files changed

Lines changed: 143 additions & 30 deletions

File tree

exist-core/src/main/java/org/exist/Namespaces.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ public interface Namespaces {
7474
/** QName representing xml:space */
7575
QName XML_SPACE_QNAME = new QName("space", XML_NS, "xml");
7676

77+
/** QName representing xs:id */
78+
QName XS_ID_QNAME = new QName("ID", XML_NS, "xs");
79+
80+
/** QName representing xsi:type */
81+
QName XSI_TYPE_QNAME = new QName("type", XML_NS, "xsi");
82+
7783
String SOAP_ENVELOPE = "http://schemas.xmlsoap.org/soap/envelope/";
7884

7985
//SAXfeatures / properties : move toadedicated package

exist-core/src/main/java/org/exist/dom/memtree/DocumentImpl.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -705,22 +705,28 @@ public NodeImpl selectById(final String id) {
705705
return selectById(id, false);
706706
}
707707

708-
public NodeImpl selectById(final String id, boolean rootConsidered) {
708+
public NodeImpl selectById(final String id, boolean typeConsidered) {
709709
if(size == 1) {
710710
return null;
711711
}
712712
expand();
713713
final ElementImpl root = (ElementImpl) getDocumentElement();
714-
if ((rootConsidered && Objects.equals(root.getAttributeValue("xsi:type"), "xs:ID")) ||
714+
if ( // (rootConsidered && Objects.equals(root.getAttributeValue("xsi:type"), "xs:ID")) ||
715715
hasIdAttribute(root.getNodeNumber(), id)) {
716716
return root;
717717
}
718718
final int treeLevel = this.treeLevel[root.getNodeNumber()];
719719
int nextNode = root.getNodeNumber();
720720
while((++nextNode < document.size) && (document.treeLevel[nextNode] > treeLevel)) {
721-
if((document.nodeKind[nextNode] == Node.ELEMENT_NODE) &&
722-
hasIdAttribute(nextNode, id)) {
723-
return getNode(nextNode);
721+
if (document.nodeKind[nextNode] == Node.ELEMENT_NODE) {
722+
if (hasIdAttribute(nextNode, id)) {
723+
return getNode(nextNode);
724+
} else if (hasIdTypeAttribute(nextNode, id)) {
725+
return typeConsidered ? (NodeImpl) getNode(nextNode).getParentNode() : getNode(nextNode);
726+
} else if (getNode(nextNode).getNodeName().equalsIgnoreCase("id") &&
727+
getNode(nextNode).getStringValue().equals(id)) {
728+
return typeConsidered ? (NodeImpl) getNode(nextNode).getParentNode() : getNode(nextNode);
729+
}
724730
}
725731
}
726732
return null;
@@ -754,7 +760,25 @@ private boolean hasIdAttribute(final int nodeNumber, final String id) {
754760
if(-1 < attr) {
755761
while((attr < document.nextAttr) && (document.attrParent[attr] == nodeNumber)) {
756762
if((document.attrType[attr] == AttrImpl.ATTR_ID_TYPE) &&
757-
id.equals(document.attrValue[attr])) {
763+
id.equals(document.attrValue[attr])) {
764+
return true;
765+
} else if (document.attrName[attr].getLocalPart().equals("id") &&
766+
Objects.equals(document.attrValue[attr], id)) {
767+
return true;
768+
}
769+
++attr;
770+
}
771+
}
772+
return false;
773+
}
774+
775+
private boolean hasIdTypeAttribute(final int nodeNumber, final String id) {
776+
int attr = document.alpha[nodeNumber];
777+
if(-1 < attr) {
778+
while((attr < document.nextAttr) && (document.attrParent[attr] == nodeNumber)) {
779+
if (document.attrName[attr].getStringValue().equals(Namespaces.XSI_TYPE_QNAME.getStringValue()) &&
780+
document.attrValue[attr].equals(Namespaces.XS_ID_QNAME.getStringValue()) &&
781+
document.getNode(nodeNumber).getStringValue().equals(id)) {
758782
return true;
759783
}
760784
++attr;

exist-core/src/main/java/org/exist/xquery/functions/fn/FunElementWithId.java

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import static org.exist.xquery.FunctionDSL.*;
3838
import static org.exist.xquery.functions.fn.FnModule.functionSignatures;
3939

40-
public class FunElementWithId extends Function {
40+
public class FunElementWithId extends BasicFunction {
4141
private static final String FN_NAME = "element-with-id";
4242
private static final String FN_DESCRIPTION =
4343
"Returns the sequence of element nodes that have an ID value " +
@@ -60,39 +60,35 @@ public FunElementWithId(final XQueryContext context, final FunctionSignature sig
6060
}
6161

6262
@Override
63-
public Sequence eval(Sequence contextSequence, final Item contextItem) throws XPathException {
63+
public Sequence eval(final Sequence[] args, Sequence contextSequence) throws XPathException {
6464
if (context.getProfiler().isEnabled()) {
6565
context.getProfiler().start(this);
6666
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
67-
if (contextSequence != null)
68-
{context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);}
69-
if (contextItem != null)
70-
{context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());}
67+
if (contextSequence != null) {
68+
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
69+
}
7170
}
7271

7372
if (getArgumentCount() < 1) {
74-
throw new XPathException(this, ErrorCodes.XPST0017, "function element-with-id requires one argument");
75-
}
76-
if (contextItem != null) {
77-
contextSequence = contextItem.toSequence();
73+
throw new XPathException(this, ErrorCodes.XPST0017, "function element-with-id requires one argument");
7874
}
7975

8076
final Sequence result;
8177
boolean processInMem = false;
8278
final Expression arg = getArgument(0);
83-
final Sequence idval = arg.eval(contextSequence);
79+
final Sequence idval = arg.eval(contextSequence);
8480

85-
if (idval.isEmpty() || (getArgumentCount() == 1 && contextSequence != null && contextSequence.isEmpty())) {
81+
if (idval.isEmpty() || (getArgumentCount() == 1 && contextSequence != null && contextSequence.isEmpty())) {
8682
result = Sequence.EMPTY_SEQUENCE;
8783
} else {
88-
String nextId;
89-
DocumentSet docs = null;
84+
String nextId;
85+
DocumentSet docs = null;
9086
if (getArgumentCount() == 2) {
9187
final Sequence nodes = getArgument(1).eval(contextSequence);
9288
if (nodes.isEmpty()) {
9389
throw new XPathException(this, ErrorCodes.XPDY0002, "XPDY0002: no node or context item for fn:id", nodes);
9490
} else if (!Type.subTypeOf(nodes.itemAt(0).getType(), Type.NODE)) {
95-
throw new XPathException(this, ErrorCodes.XPTY0004, "XPTY0004: fn:id() argument is not a node", nodes);
91+
throw new XPathException(this, ErrorCodes.XPTY0004, "XPTY0004: fn:id() argument is not a node", nodes);
9692
}
9793
NodeValue node = (NodeValue)nodes.itemAt(0);
9894
if (node.getImplementationType() == NodeValue.IN_MEMORY_NODE) {
@@ -105,10 +101,10 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
105101
contextSequence = node;
106102
} else if (contextSequence == null) {
107103
throw new XPathException(this, ErrorCodes.XPDY0002, "No context item specified");
108-
} else if(!Type.subTypeOf(contextSequence.getItemType(), Type.NODE)) {
109-
throw new XPathException(this, ErrorCodes.XPTY0004, "Context item is not a node", contextSequence);
110-
} else {
111-
if (contextSequence.isPersistentSet()) {
104+
} else if(!Type.subTypeOf(contextSequence.getItemType(), Type.NODE)) {
105+
throw new XPathException(this, ErrorCodes.XPTY0004, "Context item is not a node", contextSequence);
106+
} else {
107+
if (contextSequence.isPersistentSet()) {
112108
docs = contextSequence.toNodeSet().getDocumentSet();
113109
} else {
114110
processInMem = true;
@@ -122,7 +118,7 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
122118
}
123119

124120
for(final SequenceIterator i = idval.iterate(); i.hasNext(); ) {
125-
nextId = i.nextItem().getStringValue();
121+
nextId = i.nextItem().getStringValue();
126122
if (!nextId.isEmpty()) {
127123
if (nextId.indexOf(' ') != Constants.STRING_NOT_FOUND) {
128124
final StringTokenizer tok = new StringTokenizer(nextId, " ");
@@ -146,15 +142,15 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
146142
}
147143
}
148144
}
149-
}
145+
}
150146
}
151-
result.removeDuplicates();
147+
result.removeDuplicates();
152148
if (context.getProfiler().isEnabled()) {
153149
context.getProfiler().end(this, "", result);
154150
}
155151

156152
return result;
157-
}
153+
}
158154

159155
private void getId(final NodeSet result, final DocumentSet docs, final String id) throws XPathException {
160156
final NodeSet attribs = context.getBroker().getValueIndex().find(context.getWatchDog(), Comparison.EQ, docs, null, -1, null, new StringValue(id, Type.ID));

exist-core/src/main/java/org/exist/xquery/functions/fn/FunId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathExc
127127
contextSequence = node;
128128
} else if (contextSequence == null) {
129129
logger.error("{} No context item specified", ErrorCodes.XPDY0002);
130-
throw new XPathException(this, ErrorCodes.XPDY0002, "No context item specified");
130+
throw new XPathException(this, ErrorCodes.XPTY0004, "No context item specified");
131131
} else if(!Type.subTypeOf(contextSequence.getItemType(), Type.NODE)) {
132132
logger.error("{} Context item is not a node", ErrorCodes.XPTY0004);
133133
throw new XPathException(this, ErrorCodes.XPTY0004, "Context item is not a node", contextSequence);

exist-core/src/test/xquery/xquery3/fnElementWithId.xqm

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,63 @@ declare variable $fnewi:data :=
3636
</employee>
3737
};
3838

39+
declare variable $fnewi:ids2 :=
40+
<IDS2 xmlns = "http://www.w3.org/XQueryTest/ididrefs"
41+
xmlns:i = "http://www.w3.org/XQueryTest/ididrefs"
42+
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
43+
xsi:schemaLocation = "http://www.w3.org/XQueryTest/ididrefs id.xsd">
44+
45+
<Element-with-ID-attribute id="alpha"><data>derived from Phoenician Aleph</data></Element-with-ID-attribute>
46+
<Element-with-ID-attribute id="beta"><data>derived from Phoenician Beth</data></Element-with-ID-attribute>
47+
<Element-with-Restricted-ID-attribute id="gamma"><data>derived from Phoenician Gimel</data></Element-with-Restricted-ID-attribute>
48+
<Element-with-Restricted-ID-attribute id="delta"><data>derived from Phoenician Daleth</data></Element-with-Restricted-ID-attribute>
49+
<Element-as-ID>epsilon</Element-as-ID>
50+
<Element-as-ID>zeta</Element-as-ID>
51+
<Element-as-Restricted-ID>eta</Element-as-Restricted-ID>
52+
<Element-as-Restricted-ID>theta</Element-as-Restricted-ID>
53+
<Element-with-ID-child><id>iota</id><data>Derived from Phoenician Yodh</data></Element-with-ID-child>
54+
<Element-with-ID-child><id>kappa</id><data>Derived from Phoenician Kaph</data></Element-with-ID-child>
55+
<Element-with-Restricted-ID-child><id>lambda</id><data>Derived from Phoenician Lamedh</data></Element-with-Restricted-ID-child>
56+
<Element-with-Restricted-ID-child><id>mu</id><data>Derived from Phoenician Mem</data></Element-with-Restricted-ID-child>
57+
<Element-with-complex-ID-child><id charmed="false">nu</id><data>Derived from Phoenician Nun</data></Element-with-complex-ID-child>
58+
<Element-with-ID-list-child><id>xi</id><data>Derived from Phoenician Samekh</data></Element-with-ID-list-child>
59+
<Element-with-ID-list-child><id>ping pong</id><data>not an id, as not a singleton value</data></Element-with-ID-list-child>
60+
<Element-with-ID-union-child><id>omicron</id><data>Derived from Phoenician Ayin</data></Element-with-ID-union-child>
61+
<Element-with-ID-union-child><id>853</id><data>Not an id, wrong member of union</data></Element-with-ID-union-child>
62+
63+
<IDREF>zeta</IDREF>
64+
<IDREFS>gamma kappa</IDREFS>
65+
<IDREF-List>epsilon mu alpha</IDREF-List>
66+
<IDREF-Union>eta 234 delta</IDREF-Union>
67+
<Restricted-IDREF>iota</Restricted-IDREF>
68+
<List-of-Restricted-IDREF>lambda beta iota</List-of-Restricted-IDREF>
69+
70+
<IDREF-content>zeta</IDREF-content>
71+
<IDREFS-content>gamma kappa</IDREFS-content>
72+
<IDREF-List-content>epsilon mu alpha</IDREF-List-content>
73+
<IDREF-Union-content>eta 234 delta</IDREF-Union-content>
74+
<Restricted-IDREF-content>iota</Restricted-IDREF-content>
75+
<List-of-Restricted-IDREF-content>lambda beta iota</List-of-Restricted-IDREF-content>
76+
77+
<Nillable-IDREF>omicron</Nillable-IDREF>
78+
<Nillable-IDREF xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
79+
80+
<Nillable-ID xsi:nil="true"/>
81+
82+
<Restricted-NCName-or-IDREF-list>Q</Restricted-NCName-or-IDREF-list>
83+
<Restricted-NCName-or-IDREF-list>Q omicron</Restricted-NCName-or-IDREF-list>
84+
85+
<W i:IDREF="epsilon"/>
86+
<W i:IDREFS="delta eta"/>
87+
<W i:IDREF-List="zeta iota"/>
88+
<W i:IDREF-Union="gamma 976 delta alpha"/>
89+
<W i:Restricted-IDREF="beta"/>
90+
<W i:List-of-Restricted-IDREF="lambda kappa"/>
91+
<W i:Restricted-NCName-or-IDREF-list="Z"/>
92+
<W i:Restricted-NCName-or-IDREF-list="Z epsilon"/>
93+
94+
</IDS2>;
95+
3996
declare
4097
%test:assertEquals("employee")
4198
function fnewi:id-with-is-id-attribute() {
@@ -47,3 +104,33 @@ declare
47104
function fnewi:id-with-is-id-type() {
48105
$fnewi:data/element-with-id('E21256')/name()
49106
};
107+
108+
declare
109+
%test:assertEquals("Element-with-ID-attribute", "Element-with-ID-attribute", "Element-with-ID-child", "Element-with-ID-child")
110+
function fnewi:fn-element-with-id-1() {
111+
$fnewi:ids2/element-with-id('alpha beta iota kappa')/local-name()
112+
};
113+
114+
declare
115+
%test:assertEquals("Element-with-Restricted-ID-child", "Element-with-Restricted-ID-child")
116+
function fnewi:fn-element-with-id-2() {
117+
$fnewi:ids2/element-with-id('lambda mu')/local-name()
118+
};
119+
120+
declare
121+
%test:assertEquals("Element-with-complex-ID-child")
122+
function fnewi:fn-element-with-id-3() {
123+
$fnewi:ids2/element-with-id('nu')/local-name()
124+
};
125+
126+
declare
127+
%test:assertEquals("Element-with-ID-list-child", "0")
128+
function fnewi:fn-element-with-id-4() {
129+
$fnewi:ids2/element-with-id('xi')/local-name(), count($fnewi:ids2/element-with-id('ping'))
130+
};
131+
132+
declare
133+
%test:assertEquals("Element-with-ID-union-child", "0")
134+
function fnewi:fn-element-with-id-5() {
135+
$fnewi:ids2/element-with-id('omicron')/local-name(), count($fnewi:ids2/element-with-id('853'))
136+
};

0 commit comments

Comments
 (0)