blob: b9adb9216ad7773950bd767432706aabbc0fff20 [file] [log] [blame]
/* Copyright (c) 2012-2015 Tresys Technology, LLC. All rights reserved.
*
* Developed by: Tresys Technology, LLC
* http://www.tresys.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal with
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the names of Tresys Technology, nor the names of its contributors
* may be used to endorse or promote products derived from this Software
* without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
* SOFTWARE.
*/
package edu.illinois.ncsa.daffodil.processors.unparsers
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.processors.ElementRuntimeData
import edu.illinois.ncsa.daffodil.processors.RuntimeData
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.OccursCountKind
abstract class RepUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData, baseName: String)
extends UnparserObject(context) {
override lazy val childProcessors = Seq(rUnparser)
val intN = n.toInt
def checkN(ustate: UState, n: Long) {
if (n > ustate.tunable.maxOccursBounds) {
// TODO: how can we go after bigger than max int bytes? We have 64-bit computers
// after all....
UE(ustate, "Occurs count %s exceeds implementation maximum of %s.", n, ustate.tunable.maxOccursBounds)
}
}
final def unparse(ustate: UState): Unit = {
checkN(ustate, n)
unparseAllRepeats(ustate)
}
protected def unparseAllRepeats(ustate: UState): Unit
override def toString = "Rep" + baseName + "(" + rUnparser.toString + ")"
override def toBriefXML(depthLimit: Int = -1): String = {
if (depthLimit == 0) "..." else
"<Rep" + baseName + ">" + rUnparser.toBriefXML(depthLimit - 1) +
"</Rep" + baseName + ">"
}
}
/**
* This object is so that we can share the iteration idioms between situations
* where we know N statically, and where dynamic evaluation computes N.
*
* In these cases, there are no new points of uncertainty because computed or
* otherwise, we know N.
*/
object Rep {
def loopExactlyTotalN(intN: Int, rUnparser: Unparser, ustate: UState, context: RuntimeData, iParser: Unparser): Unit = {
while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
// Debugger.beforeRepetition(ustate, iParser)
rUnparser.unparse1(ustate, context)
// Debugger.afterRepetition(ustate, iParser)
ustate.moveOverOneArrayIndexOnly
}
}
}
class RepExactlyNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
extends RepUnparser(n, rUnparser, context, "ExactlyN") {
def unparseAllRepeats(ustate: UState) {
1 to intN foreach { _ =>
{
// Debugger.beforeRepetition(pResult, this)
rUnparser.unparse1(ustate, context)
// Debugger.afterRepetition(pResult, this)
ustate.moveOverOneArrayIndexOnly
}
}
}
}
class RepAtMostTotalNUnparser(n: Long, rUnparser: Unparser, erd: ElementRuntimeData)
extends RepUnparser(n, rUnparser, erd, "AtMostTotalN") {
def unparseAllRepeats(ustate: UState): Unit = {
while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
// Debugger.beforeRepetition(ustate, this)
rUnparser.unparse1(ustate, context)
if (ustate.isInspectArrayEnd) return
// Debugger.afterRepetition(ustate, this)
ustate.moveOverOneArrayIndexOnly
}
}
}
class RepExactlyTotalNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
extends RepUnparser(n, rUnparser, context, "ExactlyTotalN") {
def unparseAllRepeats(ustate: UState) {
Rep.loopExactlyTotalN(intN, rUnparser, ustate, context, this)
}
}
/**
* This is a bit tricky: Since we are streaming the elements to the unparser,
* it will receive a DIArray element, but the length of that DIArray element at the
* time it is received, will probably be 0 because the child elements (not even
* one of them) will have been pulled yet. So we cannot just measure how big the array
* is and iterate that many times. Rather, we have to iterate until we come to
* an End(DIArray) event.
*
* This requires the ability to look ahead into the input stream by 1, see if it
* is EndArray, and if so consume it and end the iteration.
*/
class RepUnboundedUnparser(occursCountKind: OccursCountKind.Value, rUnparser: Unparser, erd: ElementRuntimeData)
extends RepUnparser(-1, rUnparser, erd, "Unbounded") {
def unparseAllRepeats(ustate: UState) {
Assert.invariant(ustate.currentInfosetNodeMaybe.isDefined)
while (!ustate.isInspectArrayEnd) {
// Debugger.beforeRepetition(ustate, this)
rUnparser.unparse1(ustate, erd)
// Debugger.afterRepetition(ustate, this)
ustate.moveOverOneArrayIndexOnly
}
}
}
//
// Unparsers don't evaluate or use expression values. The number of occurrences is the number
// in the infoset (augmented up to minOccurs with default values when appropriate)
//
class RepAtMostOccursCountUnparser(rUnparser: Unparser, intN: Long, erd: ElementRuntimeData)
extends RepUnparser(intN, rUnparser, erd, "AtMostOccursCount") {
def unparseAllRepeats(ustate: UState) {
// repeat either n times, or occursCount times if that's less than n.
// val n = math.min(ustate.occursBounds, erd.minOccurs.get)
Rep.loopExactlyTotalN(intN.toInt, rUnparser, ustate, erd, this)
}
}