blob: cbee0ff1f2100e4fa211f0983ee7e775f56eb7c9 [file] [log] [blame]
/*
* 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.
*/
package org.apache.daffodil.processors.unparsers
import org.apache.daffodil.processors.SequenceRuntimeData
import org.apache.daffodil.processors.ElementRuntimeData
import org.apache.daffodil.processors.TermRuntimeData
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.processors.parsers.EndArrayChecksMixin
import org.apache.daffodil.processors.parsers.MinMaxRepeatsMixin
import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
/**
* base for unparsers for the children of sequences.
*
* There is one sequence child unparser for each child (declared) of the sequence.
*
* These do not iterate over multiple recurring instances. That iteration happens
* in the caller. These unparse only a single occurrence when the child unparser
* is for an array/optional element.
*/
abstract class SequenceChildUnparser(
val childUnparser: Unparser,
val srd: SequenceRuntimeData,
val trd: TermRuntimeData)
extends CombinatorUnparser(srd) {
override def runtimeDependencies = Vector()
}
/**
* Base for unparsers of array/optional elements.
*
* The unparse() method unparses exactly one occurrance, does NOT iterate over
* all the occurrences.
*/
abstract class RepeatingChildUnparser(
override val childUnparser: Unparser,
override val srd: SequenceRuntimeData,
val erd: ElementRuntimeData)
extends SequenceChildUnparser(childUnparser, srd, erd)
with MinMaxRepeatsMixin
with EndArrayChecksMixin {
/**
* Unparse exactly one occurrence of an array/optional element.
*
* Iterating for arrays/optionals is done in the caller.
*/
override protected def unparse(state: UState): Unit = {
childUnparser.unparse1(state)
}
override lazy val runtimeDependencies = Vector()
override def toString = "RepUnparser(" + childUnparser.toString + ")"
override def toBriefXML(depthLimit: Int = -1): String = {
if (depthLimit == 0) "..." else
"<RepUnparser name='" + erd.name + "'>" + childUnparser.toBriefXML(depthLimit - 1) +
"</RepUnparser>"
}
/**
* Sets up for the start of an array/optional. For true array, pulls an event, which must be a start-array
* event.
*/
final def startArrayOrOptional(state: UState): Unit = {
val ev = state.inspectAccessor
if (ev.erd.isArray) {
// only pull start array event for a true array, not an optional.
val event = state.advanceOrError
Assert.invariant(event.isStart && event.isArray)
// pull the next event, so that nothing else sees or deals with array start events
state.inspect
}
}
/**
* Ends an array/optional. For a true array, pulls an event which must be an end-array event.
* Validates array dimensions if validation has been requested.
*/
final def endArrayOrOptional(currentArrayERD: ElementRuntimeData, state: UState): Unit = {
if (currentArrayERD.isArray) {
// only pull end array event for a true array, not an optional
val event = state.advanceOrError
if (!(event.isEnd && event.isArray)) {
UE(state, "Expected array end event for %s, but received %s.",
erd.namedQName.toExtendedSyntax, event)
}
}
val actualOccurs = state.arrayPos
// State could be Success or Failure here.
endArray(state, actualOccurs)
}
/**
* Determines if the incoming event matches the current term, so we
* should run its unparser.
*
* If the term is an element and the event is a start element,
* then true if the incoming element event namedQName matches the expected element namedQName.
* Always true if the term is a model-group and the event is a start element.
* If the event is not a start, it must be an endArray for the enclosing complex element, and
* the answer is false.
*/
final def shouldDoUnparser(unparser: RepeatingChildUnparser, state: UState): Boolean = {
val childRD = unparser.trd
val res =
childRD match {
case erd: ElementRuntimeData => {
// check to see if we have a matching
// incoming infoset event
if (state.inspect) {
val ev = state.inspectAccessor
if (ev.isStart) {
if (ev.erd eq erd) {
true
} else {
// System.err.println("Stopping occurrences(1) of %s due to event %s".format(erd.namedQName, ev))
false // event not a start for this element
}
} else {
Assert.invariant(ev.isEnd)
// could be end of simple element - we handle on the start event. Nothing to do.
// or could be end of complex event, i.e., we've peeked ahead and found the end of a complex element.
// It has to be the complex element that ultimately encloses this sequence.
// Though that's not a unique element given that this sequence could be inside
// a global group definition that is reused in multiple places.
// Nothing to do for complex type either.
false
}
} else {
// was no event, so no unparse
// System.err.println("Stopping occurrences of(4) %s due to No infoset event".format(erd.namedQName))
false
}
}
case _ => {
// since only elements can be optional, anything else is non-optional
true
}
}
val chk: Boolean =
if (res)
unparser.checkArrayPosAgainstMaxOccurs(state)
else
false
chk
}
/**
* True if arrayPos is less than maxOccurs, but only if we care about
* maxOccurs. Always true if occursCountKind is not one where we
* bound occurrences with maxOccurs.
*
*/
def checkArrayPosAgainstMaxOccurs(state: UState): Boolean
/**
* For OccursCountKind 'implicit', we need to check for arrayPos in range
* and enforce the min/maxOccurs bounds.
*
* maxReps is passed in, since it will already have been computed before hand.
*
* This depends on maxReps and minReps being properly setup to represent the
* bounds checking that should be done. E.g., if 'implicit' but maxReps is
* 'unbounded', then maxReps will be Long.MaxValue.
*/
def checkFinalOccursCountBetweenMinAndMaxOccurs(
state: UState,
unparser: RepeatingChildUnparser,
numOccurrences: Int, maxReps: Long, arrPos: Long): Unit = {
import OccursCountKind._
val minReps = unparser.minRepeats(state)
val ev = state.inspectAccessor
val erd = unparser.erd
if (numOccurrences < minReps) {
//
// Defaultable feature not yet implemented.
//
// Assert.invariant(!erd.isDefaultable) // should have handled this elsewhere
//
UE(state, "Expected %s additional %s elements, but received %s.", minReps - numOccurrences, erd.namedQName, ev)
}
//
// Now if there is an unspecified number of occurrences
// and we're supposed to respect min and maxOccurs
// (Per DFDL Spec 16.1)
//
val ock = erd.maybeOccursCountKind.get
// check against maxReps + 1 since we incremented to the next array position
// before calling this check.
if ((ock eq Implicit) && unparser.isBoundedMax && (arrPos > maxReps + 1))
UE(state, "Expected maximum of %s elements, but received %s.", maxReps, arrPos - 1)
//
// check that if we could consume more (didn't end due to maxReps)
// Then we ended with whatever's next NOT being another occurrence of this
// same element.
//
if (arrPos < maxReps &&
minReps < maxReps &&
(ock ne Implicit)) {
if (state.inspect) {
val ev = state.inspectAccessor
if (ev.isStart && ev.erd.namedQName == erd)
// This is the error we get if there are more events in the infoset event stream
// for an array, than are allowed by maxOccurs.
UE(state, "More than maxOccurs %s occurrences of %s in Infoset input: Expected no more events for %s, but received %s.", maxReps, erd.namedQName, ev)
}
}
}
}