in daffodil-lib/src/main/scala/org/apache/daffodil/lib/xml/XMLUtils.scala [912:1055]
def computeDiffOne(
an: Node,
bn: Node,
aParentScope: NamespaceBinding,
bParentScope: NamespaceBinding,
maybeIndex: Option[Int],
parentPathSteps: Seq[String],
ignoreProcInstr: Boolean,
checkPrefixes: Boolean,
checkNamespaces: Boolean,
maybeType: Option[String],
maybeFloatEpsilon: Option[Float],
maybeDoubleEpsilon: Option[Double]
): Seq[(String, String, String)] = {
lazy val zPath = parentPathSteps.reverse.mkString("/")
(an, bn) match {
case (a: Elem, b: Elem) => {
val Elem(prefixA, labelA, attribsA, nsbA, childrenA @ _*) = a
val Elem(prefixB, labelB, attribsB, nsbB, childrenB @ _*) = b
val typeA: Option[String] = getXSIType(a)
val typeB: Option[String] = getXSIType(b)
val maybeType: Option[String] = Option(typeA.getOrElse(typeB.getOrElse(null)))
val nilledA = a.attribute(XSI_NAMESPACE.toString, "nil")
val nilledB = b.attribute(XSI_NAMESPACE.toString, "nil")
val mappingsA = if (checkNamespaces) nsbA.buildString(aParentScope).trim else ""
val mappingsB = if (checkNamespaces) nsbB.buildString(bParentScope).trim else ""
if (labelA != labelB) {
// different label
List((zPath, labelA, labelB))
} else if (checkPrefixes && prefixA != prefixB) {
// different prefix
List((zPath + "/" + labelA + "@prefix", prefixA, prefixB))
} else if (checkNamespaces && mappingsA != mappingsB) {
// different namespace bindings
List((zPath + "/" + labelA + "@xmlns", mappingsA, mappingsB))
} else if (nilledA != nilledB) {
// different xsi:nil
List(
(
zPath + "/" + labelA + "@xsi:nil",
nilledA.map(_.toString).getOrElse(""),
nilledB.map(_.toString).getOrElse("")
)
)
} else if (typeA != typeB && typeA.isDefined && typeB.isDefined) {
// different xsi:type (if both suppplied)
List(
(
zPath + "/" + labelA + "@xsi:type",
typeA.map(_.toString).getOrElse(""),
typeA.map(_.toString).getOrElse("")
)
)
} else {
val pathLabel = labelA + maybeIndex.map("[" + _ + "]").getOrElse("")
val thisPathStep = pathLabel +: parentPathSteps
val (childrenACompare, childrenBCompare) =
if (ignoreProcInstr) {
val ca = childrenA.filterNot(_.isInstanceOf[ProcInstr])
val cb = childrenB.filterNot(_.isInstanceOf[ProcInstr])
(ca, cb)
} else {
(childrenA, childrenB)
}
// for elements with repeats we want to use an index in any diff
// outut. So for repeating children, we'll create a mutable map where
// the key is the label and the value is the count of how many
// children of that label we've seen
val repeatingChildrenLabels =
childrenA.groupBy(_.label).filter { case (k, v) => v.length > 1 }.keys
val labelsWithZeroCount = repeatingChildrenLabels.map { _ -> 0 }
val countMap = mutable.Map(labelsWithZeroCount.toSeq: _*)
val childrenDiffs = childrenACompare.zip(childrenBCompare).flatMap { case (ca, cb) =>
val maybeChildCount = countMap.get(ca.label)
val maybeChildIndex = maybeChildCount.map { count =>
countMap(ca.label) += 1
count + 1
}
computeDiffOne(
ca,
cb,
nsbA,
nsbB,
maybeChildIndex,
thisPathStep,
ignoreProcInstr,
checkPrefixes,
checkNamespaces,
maybeType,
maybeFloatEpsilon,
maybeDoubleEpsilon
)
}
// if childrenA and childrenB have different length, zip will drop an
// extra. This will report a diff if the lengths are off.
val childrenLengthDiff =
if (childrenA.length != childrenB.length) {
List(
(
zPath + "/" + labelA + "::child@count)",
childrenA.length.toString,
childrenB.length.toString
)
)
} else {
Nil
}
childrenDiffs ++ childrenLengthDiff
}
}
case (tA: Text, tB: Text) => {
val thisDiff =
computeTextDiff(zPath, tA, tB, maybeType, maybeFloatEpsilon, maybeDoubleEpsilon)
thisDiff
}
case (pA: ProcInstr, pB: ProcInstr) => {
val ProcInstr(tA1label, tA1content) = pA
val ProcInstr(tB1label, tB1content) = pB
val labelDiff = computeTextDiff(zPath, tA1label, tB1label, None, None, None)
//
// The content of a ProcInstr is technically a big string
// But our usage of them the content is XML-like so could be loaded and then compared
// as XML, if the label is in fact an indicator that this is our special
// PI with format info.
//
// Much of that XML-ish content is attributes however, so we need to be sure
// we're comparing those too.
//
// TODO: implement XML-comparison for our data format info PIs.
//
val contentDiff = computeTextDiff(zPath, tA1content, tB1content, maybeType, None, None)
labelDiff ++ contentDiff
}
case _ => {
List((zPath, an.toString, bn.toString))
}
}
}