This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git
The following commit(s) were added to refs/heads/main by this push:
new 1e26e2999 Ensure we set elements as final only once and when they have
values
1e26e2999 is described below
commit 1e26e299965e56774a8e1c38ef5cbd5b4675c250
Author: Steve Lawrence <[email protected]>
AuthorDate: Wed Aug 13 13:35:44 2025 -0400
Ensure we set elements as final only once and when they have values
Our current logic to handle setting elements as final during parse is
pretty broken. The core issue is that we primarily call setFinal in the
sequence parser where it is difficult to know which elements are
actually final--we had to make assumptions based on the results of the
things actualy parsing elements. This usually worked, but in some cases
could lead to setting an element final multiple times, or setting an
element as final when it wasn't actually final.
A case where this flawed logic could lead to issues is with
InfosetInputters that aren't designed to receive invalid infosets, such
as JDOM or SAX inputters. Note that InfosetInputters aren't required to
handle invalid infosets, except for those that are used by the debugger.
This could lead to incomplete error diagnostics or misleading reasons
for an error.
To make the final logic more clear and avoid these kinds of errors,
DINode.isFinal is made private with new getFinal and setFinal method
used instead. These functions allow us to place a number of assertions
throughout the infoset logic to ensure we set things as final
appropriately.
This also moves where we set elements as final. DISimple and DIComplex
elements are set final in the ElementCombinatorParser and only when they
successfully parse. This avoids cases where an element could be set
final even though it failed to parse and removes any ambiguities about
what sholud be set final. DIArrays are set final in the sequence parser
that handles the ending of arrays, with special logic to handle arrays
in unordered sequences. Note that the sequence parser is still
responsible for attempting to walk the infoset to project the internal
infoset into the target infoset--keeping all the walk logic in one
place.
DAFFODIL-3006
---
.../org/apache/daffodil/cli/cli_schema_05.dfdl.xsd | 52 +++++++++++++++
.../daffodil/cli/cliTest/TestCLIParsing.scala | 25 +++++++
.../daffodil/runtime1/infoset/InfosetImpl.scala | 51 +++++++++++++--
.../runtime1/processors/DataProcessor.scala | 9 +--
.../processors/parsers/ElementCombinator1.scala | 7 ++
.../processors/parsers/ElementKindParsers.scala | 11 ----
.../runtime1/processors/parsers/Parser.scala | 13 ----
.../processors/parsers/SequenceChildBases.scala | 15 +++++
.../processors/parsers/SequenceParserBases.scala | 76 ++--------------------
.../unparsers/runtime1/ElementUnparser.scala | 18 +++--
10 files changed, 163 insertions(+), 114 deletions(-)
diff --git
a/daffodil-cli/src/test/resources/org/apache/daffodil/cli/cli_schema_05.dfdl.xsd
b/daffodil-cli/src/test/resources/org/apache/daffodil/cli/cli_schema_05.dfdl.xsd
new file mode 100644
index 000000000..fc286ac61
--- /dev/null
+++
b/daffodil-cli/src/test/resources/org/apache/daffodil/cli/cli_schema_05.dfdl.xsd
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<schema
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:ex="http://example.com"
+ targetNamespace="http://example.com" >
+
+ <include
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+ <annotation>
+ <appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:format ref="ex:GeneralFormat" representation="binary" />
+ </appinfo>
+ </annotation>
+
+ <element name="root">
+ <complexType>
+ <sequence>
+ <element name="dispatch" type="xs:int" />
+ <choice dfdl:choiceDispatchKey="{ xs:string(./dispatch) }">
+ <sequence dfdl:choiceBranchKey="1">
+ <element name="i1" type="xs:int" />
+ <element name="i2" type="xs:int" />
+ </sequence>
+ <sequence dfdl:choiceBranchKey="2">
+ <element name="l1" type="xs:long" />
+ <element name="l2" type="xs:long" />
+ </sequence>
+ </choice>
+ </sequence>
+ </complexType>
+ </element>
+
+</schema>
diff --git
a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala
b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala
index 4e66be222..eeb214881 100644
---
a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala
+++
b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala
@@ -1012,4 +1012,29 @@ class TestCLIParsing {
e.getMessage.contains("""Input Buffer: <?xml version="1.0"
encoding="UTF-8"?>""")
)
}
+
+ @Test def test_CLI_Parsing_parse_error_infoset_walker_jdom(): Unit = {
+ val schema = path(
+
"daffodil-cli/src/test/resources/org/apache/daffodil/cli/cli_schema_05.dfdl.xsd"
+ )
+
+ runCLI(args"parse -s $schema -I jdom -TinfosetWalkerSkipMin=0
-TinfosetWalkerSkipMax=0") {
+ cli =>
+ // this is not enough data for the scema, which leads to a parse error
about insufficient bits
+ cli.sendBytes(Array[Byte](0, 0, 0, 1), inputDone = true)
+
+ // there was a bug Daffodil that is most easily observed using the
jdom infoset outputter
+ // with a non skipping infoset walker. With this setup, when an
element fails to parse
+ // inside a choice dispatch (and no surrounding points of uncertainty)
the infoset walker
+ // could walk into the failed element, which leads to an SDE when
using the JDOM infoset
+ // outputter. This SDE prevents backtracking so we do not see a
diagnostic about the
+ // choice dispatch branch failing. If the bug is fixed, we should
never walk into the
+ // invalid element, we should not get an SDE, and we should get a
diagnostic about choice
+ // dispatch.
+ cli.expectErr("Parse Error: Choice dispatch branch failed")
+
+ // this is the core failure diagnostic, which we see regardless of bug
+ cli.expectErr("Parse Error: Insufficient bits in data.")
+ }(ExitCode.ParseError)
+ }
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/infoset/InfosetImpl.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/infoset/InfosetImpl.scala
index 6dbd2278e..7d14db9c1 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/infoset/InfosetImpl.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/infoset/InfosetImpl.scala
@@ -161,13 +161,32 @@ sealed trait DINode {
var wouldHaveBeenFreed: Boolean = false
/**
- * When unparsing, arrays and complex elements have a notion of being
finalized.
- * This is when we know that no more elements will be added to them, so
things
- * like fn:count can return a value knowing it won't change, and fn:exists
can
- * return false, knowing nobody will subsequently append the item that was
being
- * questioned.
+ * All node types have a concept of being finalized. When parsing, this is
used
+ * by the InfosetWalker to know if the element could change (e.g. simple type
+ * modifying the element value, complex or array adding a child) and thus if
it
+ * can project the element to the target infoset. When unparsing, this is
used
+ * for comlplex and arrays, so we know if more elements could be added to
them,
+ * so functions like fn:count can return a value knowing it won't change, and
+ * fn:exists can return false, knowing nobody will subsequently append the
item
+ * that was being questioned.
*/
- var isFinal: Boolean = false
+ private var _isFinal: Boolean = false
+
+ /**
+ * Use to mark a node as final, indicating that its value will not change or
have
+ * any children added to it. Setting an element as final does not preclude it
from
+ * being discarded by backtracking, i.e. it is only locally final, but might
still
+ * be inside an enclosing PoU.
+ *
+ * This cannot be called if an element is already marked as final to help
ensure
+ * correct use.
+ */
+ def setFinal(): Unit = {
+ Assert.invariant(!_isFinal)
+ _isFinal = true
+ }
+
+ def isFinal: Boolean = _isFinal
/**
* use to require it be finalized or throw the appropriate
@@ -1134,7 +1153,7 @@ sealed trait DIElement
}
def setNilled(): Unit = {
- Assert.invariant(erd.isNillable)
+ Assert.invariant(erd.isNillable && !isFinal)
_isNilled = true
}
@@ -1260,6 +1279,7 @@ final class DIArray(
}
def append(ie: DIElement): Unit = {
+ Assert.invariant(!isFinal)
_contents += ie
ie.setArray(this)
}
@@ -1341,6 +1361,11 @@ sealed class DISimple(override val erd:
ElementRuntimeData)
this.setValid(true)
}
+ override def setFinal(): Unit = {
+ Assert.invariant(hasValue || _isNilled)
+ super.setFinal()
+ }
+
/**
* Parsing of a text number first does setDataValue to a string, then a
conversion does overwrite data value
* with a number. Unparsing does setDataValue to a value, then
overwriteDataValue to a string.
@@ -1351,6 +1376,7 @@ sealed class DISimple(override val erd:
ElementRuntimeData)
}
def overwriteDataValue(x: DataValuePrimitiveNullable): Unit = {
+ Assert.invariant(!isFinal)
//
// let's find places where we're putting a string in the infoset
// but the simple type is not string. That happens when parsing or
unparsing text Numbers, text booleans, text Date/Times.
@@ -1801,6 +1827,15 @@ sealed class DIComplex(override val erd:
ElementRuntimeData)
occurrence
)
+ // SequenceParserBase uses special logic for unordered sequences and
arrays, so it
+ // never sets DIArrays in unordered as final. Now that we have
finished parsing the
+ // unordered sequence and flattened its arrays, we can mark the
arrays as final and
+ // allow the infoset walker to project it to an infoset. Note that
the InfosetWalker
+ // will not walk past active points of uncertainty, so even though
this is final we
+ // don't have to worry that we might backtrack and remove
it--setFinal is just about
+ // local finality.
+ a.setFinal()
+
} else {
if (nodes.length > 1) {
val diag = new InfosetMultipleScalarError(erd)
@@ -1821,6 +1856,8 @@ sealed class DIComplex(override val erd:
ElementRuntimeData)
* When slot contains an array, this appends to the end of the array.
*/
def addChild(e: DIElement, tunable: DaffodilTunables): Unit = {
+ Assert.invariant(!isFinal)
+
if (e.runtimeData.isArray) {
val childERD = e.runtimeData
val needsNewArray =
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
index 51a2e1265..79d047998 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
@@ -368,12 +368,9 @@ class DataProcessor(
state.setMaybeProcessor(Maybe(p))
if (state.processorStatus == Success) {
- // At this point all infoset nodes have been set final, all infoset
- // walker blocks released, and all elements walked. The one exception
- // is the root node has not been set final because isFinal is handled
- // by the sequence parser and there is no sequence around the root
- // node. So mark it final and do one last walk to end the document.
- state.infoset.child(0).isFinal = true
+ // At this point all infoset nodes have been set final, all PoUs
+ // resolved, and all infoset walker blocks released. Do one last walk
+ // to project any unwalked elements to the target infoset
state.walker.walk(lastWalk = true)
Assert.invariant(state.walker.isFinished)
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementCombinator1.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementCombinator1.scala
index 5cdd7dd32..f6ab4d3b3 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementCombinator1.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementCombinator1.scala
@@ -206,6 +206,13 @@ abstract class ElementParserBase(
validate(pstate)
}
+ // We successfully finished parsing this element. There are no more
changes expected to be
+ // made so we can mark it as final and allow the InfosetWalker to
project it to the target
+ // infoset. Note that the InfosetWalker will not walk past active points
of uncertainty,
+ // so even though this is final we don't have to worry that it might we
might backtrack
+ // and remove it--setFinal is just about local finality.
+ pstate.infoset.setFinal()
+
} finally {
parseEnd(pstate)
if (pstate.dataProc.isDefined) pstate.dataProc.value.endElement(pstate,
this)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementKindParsers.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementKindParsers.scala
index ccdda5de0..1f9934df1 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementKindParsers.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ElementKindParsers.scala
@@ -213,17 +213,6 @@ abstract class ChoiceDispatchCombinatorParserBase(
if (pstate.processorStatus eq Success) {
Logger.log.debug(s"Choice dispatch success: ${parser}")
-
- // We usually rely on the sequence parser to set elements as final.
- // But choices with scalar elements do not necessarily have a
- // sequence surrounding them and so they aren't set final. In order
- // to set these elements final, we do it here as well. We will
- // attempt to walk the infoset after the PoU is discarded.
- val newLastChildNode = pstate.infoset.maybeLastChild
- if (newLastChildNode.isDefined) {
- newLastChildNode.get.isFinal = true
- }
-
} else {
Logger.log.debug(s"Choice dispatch failed: ${parser}")
val diag =
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/Parser.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/Parser.scala
index 20f5305c6..c90d57f98 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/Parser.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/Parser.scala
@@ -276,17 +276,6 @@ class ChoiceParser(ctxt: RuntimeData, val childParsers:
Array[Parser])
// Choice branch was successfull. Break out of the loop and let
// withPointOfUncertainty discard the pou
successfullyParsedChildBranch = true
-
- // We usually rely on the sequence parser to set elements as final.
- // But choices with scalar elements do not necessarily have a
- // sequence surrounding them and so they aren't set final. In order
- // to set these elements final, we do it here as well. We will
- // attempt to walk the infoset after the PoU is discarded.
- val newLastChildNode = pstate.infoset.maybeLastChild
- if (newLastChildNode.isDefined) {
- newLastChildNode.get.isFinal = true
- }
-
} else {
// Failed to parse this branch alternative. Create diagnostic and
// check if anything resolved the associated point of uncertainty
@@ -310,8 +299,6 @@ class ChoiceParser(ctxt: RuntimeData, val childParsers:
Array[Parser])
}
}
}
-
- pstate.walker.walk()
}
if (!successfullyParsedChildBranch) {
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
index 738ffd80e..cce424fdf 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
@@ -357,6 +357,21 @@ abstract class RepeatingChildParser(
state.mpstate.arrayIterationIndexStack.pop()
val occurrences = state.mpstate.occursIndexStack.pop() - 1
super.endArray(state, occurrences)
+
+ // if there were no issues parsing this array, and we actually created
some elements, we
+ // need to mark the DIArray as final to allow the InfosetWalker to end the
array and walk to
+ // later siblings. Note that the InfosetWalker will not walk past active
points of
+ // uncertainty, so even though this is final we don't have to worry that
it might we might
+ // backtrack and remove it--setFinal is just about local finality. Also,
this
+ // RepeatingChildParser is used for both arrays and optional elements. We
only need to do
+ // this for arrays since optional elements are set final in the
ElementParser
+ if ((state.processorStatus eq Success) && occurrences > 0 && erd.isArray) {
+ // state.infoset is the parent of the array we need to set as final. We
know we added an
+ // array and we know its the last child of the parent, so we can just
get that directly
+ val parent = state.infoset
+ val array = parent.maybeLastChild.get
+ array.setFinal()
+ }
}
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
index 58c2868da..a0366cf81 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
@@ -114,9 +114,6 @@ abstract class SequenceParserBase(
//
while (!isDone && (scpIndex < limit) && (pstate.processorStatus eq
Success)) {
- // keep track of the current last child node. If the last child changes
- // while parsing, we know a new child was added in this loop
-
child = children(scpIndex).asInstanceOf[SequenceChildParser]
child match {
@@ -216,29 +213,9 @@ abstract class SequenceParserBase(
pstate.mpstate.moveOverOneGroupIndexOnly()
}
- val newLastChildNode = pstate.infoset.maybeLastChild
- if (newLastChildNode.isDefined) {
- // We have potentially added a child to to this complex during
- // this array loop.
- //
- // If the new child is a DIArray, we know this DIArray has at
- // least one element, but we don't know if we actually added a
- // new one in this loop or not. So just get the last array
- // element and set it as final anyways.
- //
- // If it's not a DIArray, that means it's just an optional
- // simple/complex and that will get set final below where all
- // other non-array elements get set as final.
- val lastChild = newLastChildNode.get
- if (lastChild.isArray) {
- // not simple or complex, must be an array
- val lastArrayElem = lastChild.maybeLastChild
- if (lastArrayElem.isDefined) {
- lastArrayElem.get.isFinal = true
- pstate.walker.walk()
- }
- }
- }
+ // we might have added a new instance to the array. Attempt to
project it to an
+ // infoset if there are no PoU's or anything blocking it
+ pstate.walker.walk()
} // end while for each repeat
parser.endArray(pstate)
@@ -335,50 +312,9 @@ abstract class SequenceParserBase(
} // end case scalarParser
} // end match case parser
- // now that we have finished parsing a single instance of this
sequence,
- // we need to potentially set things as final, get the last child to
- // determine if it changed from the saved last child, which lets us
know
- // if a new child was actually added.
- val newLastChildNode = pstate.infoset.maybeLastChild
-
- if (!isOrdered) {
- // In the special case of unordered sequences with arrays, we do not
- // use the RepatingChildParser. Instead we parse on instance at a
time
- // in this loop. So array elements aren't set final above like normal
- // arrays are.
- //
- // So if the last child node is a DIArray, we must set new array
- // elements as final here. We can't know if we actually added a new
- // DIArray element or not, so just set the last one as final
- // regardless.
- //
- // Note that we do not need to do a null check because in an
unordered
- // sequence we are blocking, so we can't possibly walk/free any of
- // these newly added elements.
- if (newLastChildNode.isDefined && newLastChildNode.get.isArray) {
- // we have a new last child, and it's not simple or complex, so
must
- // be an array. Set its last child final
- newLastChildNode.get.maybeLastChild.get.isFinal = true
- }
- }
-
- // We finished parsing one part of a sequence, which could either be an
- // array, simple, or complex. We aren't sure if we actually added a new
- // element or not, but in case we did, mark the last node as final.
- //
- // Additionally, if this is an ordered sequence, try to walk the
infoset
- // to output events for this potentially new element. If this is an
- // unordered sequence, walking is unnecessary. This is because we may
- // need to reorder the infoset once this unordered sequence is complete
- // (via flattenAndValidateChildNodes below) and cannot walk until that
- // happens. To ensure we don't walk even if a child parser tries to
call
- // walk() we incremented infosetWalkerBlockCount at the beginning of
this
- // function, so the walker is effectively blocked from making any
- // progress. So we don't even bother calling walk() in this case.
- if (newLastChildNode.isDefined) {
- newLastChildNode.get.isFinal = true
- if (isOrdered) pstate.walker.walk()
- }
+ // we finished parsing one whole thing (scalar element, entire array,
etc). Attempt to
+ // project it to an infoset if there are no PoU's or anything blocking
it
+ pstate.walker.walk()
scpIndex += 1
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/unparsers/runtime1/ElementUnparser.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/unparsers/runtime1/ElementUnparser.scala
index e86dc802c..7c5c2e5a9 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/unparsers/runtime1/ElementUnparser.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/unparsers/runtime1/ElementUnparser.scala
@@ -466,7 +466,7 @@ sealed trait RegularElementUnparserStartEndStrategy extends
ElementUnparserStart
if (lastChildMaybe.isDefined) {
val lastChild = lastChildMaybe.get
if (lastChild.isArray && (lastChild.erd ne newElem.erd)) {
- lastChild.isFinal = true
+ lastChild.setFinal()
parentComplex.freeChildIfNoLongerNeeded(
parentComplex.numChildren - 1,
state.releaseUnneededInfoset
@@ -527,7 +527,7 @@ sealed trait RegularElementUnparserStartEndStrategy extends
ElementUnparserStart
// is no sibling following the array, so it must be set here.
val lastChild = cur.maybeLastChild
if (lastChild.isDefined && lastChild.get.isArray) {
- lastChild.get.isFinal = true
+ lastChild.get.setFinal()
cur.freeChildIfNoLongerNeeded(cur.numChildren - 1,
state.releaseUnneededInfoset)
}
}
@@ -535,8 +535,12 @@ sealed trait RegularElementUnparserStartEndStrategy
extends ElementUnparserStart
// cur is finished, mark it as final and free if possible. Note that we
// need the container and not the parent of the current element to free
// it. This way if this element is in an array, we free this element
- // from the array
- cur.isFinal = true
+ // from the array. We also do not set hidden IVC elements as
+ // final--although we allow hidden IVC elements when unparsing, they
+ // never get a value so we can't set them as final without breaking
+ // assertions. Nothing can access hidden IVC elements, so this should
+ // not break anything
+ if (!state.withinHiddenNest || erd.isRepresented) cur.setFinal()
val curContainer =
if (cur.erd.isArray) cur.diParent.maybeLastChild.get
else cur.diParent
@@ -551,7 +555,7 @@ sealed trait RegularElementUnparserStartEndStrategy extends
ElementUnparserStart
// so mark the DIDocument as final
val doc = state.documentElement
Assert.invariant(!doc.isFinal)
- doc.isFinal = true
+ doc.setFinal()
}
move(state)
@@ -614,7 +618,7 @@ trait OVCStartEndStrategy extends
ElementUnparserStartEndStrategy {
if (lastChildMaybe.isDefined) {
val lastChild = lastChildMaybe.get
if (lastChild.isArray) {
- lastChild.isFinal = true
+ lastChild.setFinal()
parentComplex.freeChildIfNoLongerNeeded(
parentComplex.numChildren - 1,
state.releaseUnneededInfoset
@@ -645,7 +649,7 @@ trait OVCStartEndStrategy extends
ElementUnparserStartEndStrategy {
// so mark the DIDocument as final
val doc = state.documentElement
Assert.invariant(!doc.isFinal)
- doc.isFinal = true
+ doc.setFinal()
}
move(state)