XPath for uniqueness check in XForm binding constraint

147 Views Asked by At

I'm trying to enforce a uniqueness constraint in an XForm binding. The relevant XML instance snippet is:

<PortAssignments>
  <PortAssignment>
    <PortReference IDREF="A">
    <PortReference IDREF="1">
    <Value>FOO</Value>
  </PortAssignment>
  <PortAssignment>
    <PortReference IDREF="A">
    <PortReference IDREF="1">
    <Value>BAR</Value>
  </PortAssignment>
  <PortAssignment>
    <PortReference IDREF="B">
    <PortReference IDREF="2">
    <Value>FOO2</Value>
  </PortAssignment>
</PortAssignments>

The constraint is that each port can only be connected to one other unique port, so in this case, A must always be connected to 1, and B must always be connected to 2. But, because they don't have to be unique in total, I can't simply check that there are no repeats, so a binding like the following doesn't work.

<xf:bind ref="PortReference">
  <xf:bind ref="@IDREF" required="true" type="xs:string" constraint="not(. = ../../preceding-sibling::*/PortReference/@IDREF) and not(. = ../../following-sibling::*/PortReferenence/@IDREF)"/> 
</xf:bind>

The other thing I've tried is comparing the second PortReference in the binding to the second PortReference in the PortAssignment with the matching IDREF, but I can't figure out a way to include the context from the current node. An example of that type of lookup is:

<xf:bind ref="PortReference">
  <xf:bind ref="@IDREF" required="true" type="xs:string" constraint="not(../../../PortAssignment[PortReference/@IDREF = {binding node's IDREF}][rest of comparison])"/> 
</xf:bind>  
2

There are 2 best solutions below

0
On BEST ANSWER

After seeing Bill's answer talking about the context() function, I looked around and found this discussion and realized I could use the current() function for my {binding node's IDREF}. This lead to the following XPath, which worked for any number of matching PortReferences.

<xf:bind ref="PortAssignment/PortReference/@IDREF" constraint="not(boolean(../../preceding-sibling::*[PortReference[1]/@IDREF = current() and PortReference[2]/@IDREF != current()/../../PortReference[2]/@IDREF])) and not(boolean(../../following-sibling::*[PortReference[1]/@IDREF = current() and PortReference[2]/@IDREF != current()/../../PortReference[2]/@IDREF]))"/>
0
On

The equivalent of {binding node's IDREF} in your XPath is the context() function.

You could also ensure uniqueness counting the number of ocurrences of a value (the context() function references the value)

<xf:bind nodeset="/PortAssignments/PortAssignment/PortReference[1]/@IDREF"
  constraint="count( /PortAssignments/PortAssignment[PortReference[1]/@IDREF = context()] ) lt 2" />