| /* 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.parsers |
| |
| import edu.illinois.ncsa.daffodil.api.DaffodilTunableParameters |
| import edu.illinois.ncsa.daffodil.dpath.AsIntConverters |
| import edu.illinois.ncsa.daffodil.equality.ViewEqual |
| import edu.illinois.ncsa.daffodil.exceptions.Assert |
| import edu.illinois.ncsa.daffodil.processors.ElementRuntimeData |
| import edu.illinois.ncsa.daffodil.processors.Failure |
| import edu.illinois.ncsa.daffodil.processors.PState |
| import edu.illinois.ncsa.daffodil.processors.Evaluatable |
| import edu.illinois.ncsa.daffodil.processors.{ Parser, ParserObject } |
| import edu.illinois.ncsa.daffodil.processors.RuntimeData |
| import edu.illinois.ncsa.daffodil.processors.Success |
| import edu.illinois.ncsa.daffodil.processors.WithParseErrorThrowing |
| import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.OccursCountKind |
| import edu.illinois.ncsa.daffodil.util.LogLevel |
| import java.lang.{ Long => JLong } |
| |
| abstract class RepParser(n: Long, rParser: Parser, context: ElementRuntimeData, baseName: String) |
| extends ParserObject(context) { |
| |
| override lazy val childProcessors = Seq(rParser) |
| |
| val intN = n.toInt |
| |
| def checkN(pstate: PState, n: Long): Boolean = { |
| if (n > DaffodilTunableParameters.maxOccursBounds) { |
| // TODO: how can we go after bigger than max int bytes? We have 64-bit computers |
| // after all.... |
| PE(pstate, "Occurs count %s exceeds implementation maximum of %s.", n, DaffodilTunableParameters.maxOccursBounds) |
| false |
| } else true |
| } |
| |
| final def parse(pstate: PState): Unit = { |
| if (!checkN(pstate, n)) return |
| parseAllRepeats(pstate) |
| // pstate.mpstate.clearDelimitedText |
| } |
| |
| protected def parseAllRepeats(pstate: PState): Unit |
| |
| override def toString = "Rep" + baseName + "(" + rParser.toString + ")" |
| |
| override def toBriefXML(depthLimit: Int = -1): String = { |
| if (depthLimit == 0) "..." else |
| "<Rep" + baseName + " name='" + context.name + "' n='" + n + "'>" + rParser.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, rParser: Parser, pstate: PState, context: RuntimeData, iParser: Parser): Unit = { |
| while (pstate.mpstate.arrayPos <= intN) { |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, iParser) |
| rParser.parse1(pstate) |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, iParser) |
| if (pstate.status ne Success) |
| return // fail if we don't get them all |
| pstate.mpstate.moveOverOneArrayIndexOnly |
| } |
| } |
| } |
| |
| class RepExactlyNParser(n: Long, rParser: Parser, context: ElementRuntimeData) |
| extends RepParser(n, rParser, context, "ExactlyN") { |
| |
| def parseAllRepeats(pstate: PState): Unit = { |
| var i = 0 |
| while (i < intN) { |
| i += 1 |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, rParser) |
| rParser.parse1(pstate) |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, rParser) |
| if (pstate.status ne Success) { |
| val cause = pstate.status.asInstanceOf[Failure].cause |
| PE(pstate, "Failed to populate %s[%s]. Expected %s item(s). Cause: %s.", |
| context.prefixedName, pstate.mpstate.arrayPos, n, |
| cause) // they all must succeed, otherwise we fail here. |
| return |
| } |
| pstate.mpstate.moveOverOneArrayIndexOnly |
| } |
| } |
| } |
| |
| class RepAtMostTotalNParser(n: Long, rParser: Parser, erd: ElementRuntimeData) |
| extends RepParser(n, rParser, erd, "AtMostTotalN") { |
| |
| def parseAllRepeats(initialState: PState): Unit = { |
| if (initialState.mpstate.arrayPos <= intN) { |
| val startState = initialState.mark |
| var priorState = initialState.mark |
| val pstate = initialState |
| var returnFlag = false |
| while (!returnFlag && (pstate.mpstate.arrayPos <= intN)) { |
| // Since each one could fail, each is a new point of uncertainty. |
| |
| pstate.pushDiscriminator |
| |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this) |
| |
| rParser.parse1(pstate) |
| |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this) |
| |
| if (pstate.status ne Success) { |
| // |
| // Did not succeed |
| // |
| // Was a discriminator set? |
| // |
| if (pstate.discriminator == true) { |
| // we fail the whole RepUnbounded, because there was a discriminator set |
| // before the failure. |
| pstate.reset(startState) |
| pstate.discard(priorState) |
| returnFlag = true |
| } else { |
| // |
| // backout any element appended as part of this attempt. |
| // |
| pstate.reset(priorState) |
| pstate.discard(startState) |
| returnFlag = true // success at prior state. |
| } |
| } else { |
| // |
| // Success |
| // |
| pstate.discard(priorState) |
| priorState = pstate.mark |
| pstate.mpstate.moveOverOneArrayIndexOnly |
| returnFlag = false |
| } |
| |
| pstate.popDiscriminator |
| } |
| if (returnFlag == false) { |
| // we exited the loop due to arrayPos hitting the upper limit |
| pstate.discard(priorState) |
| pstate.discard(startState) |
| } |
| } |
| } |
| } |
| |
| class RepExactlyTotalNParser(n: Long, rParser: Parser, context: ElementRuntimeData) |
| extends RepParser(n, rParser, context, "ExactlyTotalN") { |
| |
| def parseAllRepeats(pstate: PState): Unit = { |
| Rep.loopExactlyTotalN(intN, rParser, pstate, context, this) |
| |
| if (pstate.status ne Success) { |
| PE(pstate, "Failed to populate %s[%s]. Expected %s item(s).", |
| context.prefixedName, pstate.mpstate.arrayPos, n) // they all must succeed, otherwise we fail here. |
| } |
| } |
| } |
| |
| class RepUnboundedParser(occursCountKind: OccursCountKind.Value, rParser: Parser, erd: ElementRuntimeData) |
| extends RepParser(-1, rParser, erd, "Unbounded") { |
| |
| def parseAllRepeats(initialState: PState): Unit = { |
| Assert.invariant(initialState.status eq Success) |
| val startState = initialState.mark |
| val pstate = initialState |
| var priorState = initialState.mark |
| var returnFlag = false |
| while (!returnFlag && (pstate.status eq Success)) { |
| |
| // erd.maxOccurs.foreach { maxOccurs => |
| // if ((occursCountKind == OccursCountKind.Implicit) && |
| // (maxOccurs == -1)) { |
| // erd.minOccurs.foreach { minOccurs => |
| // if (pstate.mpstate.arrayPos - 1 <= minOccurs) { |
| // // Is required element |
| // // Need to trigger default value creation |
| // // in right situations (like the element is defaultable) |
| // // This is relatively easy for simple types |
| // // for complex types, defaulting is trickier as one |
| // // must recursively traverse the type, and then determine one |
| // // has not advanced the data at all. |
| // } |
| // } |
| // } |
| // } |
| |
| // |
| // Every parse is a new point of uncertainty. |
| pstate.pushDiscriminator |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this) |
| rParser.parse1(pstate) |
| if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this) |
| if (pstate.status ne Success) { |
| // |
| // Did not succeed |
| // |
| // Was a discriminator set? |
| // |
| if (pstate.discriminator == true) { |
| // we fail the whole RepUnbounded, because there was a discriminator set |
| // before the failure. |
| pstate.reset(startState) |
| pstate.discard(priorState) |
| } else { |
| // |
| // no discriminator, so suppress the failure. Loop terminated with prior element. |
| // |
| |
| log(LogLevel.Debug, "Failure suppressed. This is normal termination of a occursCountKind='parsed' array.") |
| pstate.reset(priorState) |
| pstate.discard(startState) |
| } |
| returnFlag = true |
| } else { |
| // Success |
| // Need to check for forward progress |
| if (pstate.bitPos =#= priorState.bitPos0b) { |
| pstate.discard(priorState) // didn't move, but might have assigned variables, have to undo those. |
| pstate.discard(startState) |
| PE(pstate, |
| "RepUnbounded - No forward progress at byte %s. Attempt to parse %s " + |
| "succeeded but consumed no data.\nPlease re-examine your schema to correct this infinite loop.", |
| pstate.bytePos, erd.prettyName) |
| returnFlag = true |
| } else { |
| pstate.discard(priorState) |
| priorState = pstate.mark |
| pstate.mpstate.moveOverOneArrayIndexOnly |
| returnFlag = false |
| } |
| } |
| pstate.popDiscriminator |
| } |
| Assert.invariant(returnFlag == true) |
| } |
| } |
| |
| class OccursCountExpressionParser(occursCountEv: Evaluatable[JLong], erd: ElementRuntimeData) |
| extends ParserObject(erd) with WithParseErrorThrowing { |
| |
| override lazy val childProcessors = Nil |
| |
| def parse(pstate: PState): Unit = withParseErrorThrowing(pstate) { |
| val oc = occursCountEv.evaluate(pstate) |
| val ocLong = AsIntConverters.asLong(oc) |
| if (ocLong < 0 || |
| ocLong > DaffodilTunableParameters.maxOccursBounds) { |
| PE(pstate, "Evaluation of occursCount expression %s returned out of range value %s.", occursCountEv, ocLong) |
| return |
| } |
| pstate.mpstate.updateBoundsHead(ocLong) |
| } |
| |
| override def toString = toBriefXML() // "OccursCount(" + e.occursCount.prettyExpr + ")" |
| |
| override def toBriefXML(depthLimit: Int = -1) = { |
| "<OccursCount>" + occursCountEv.toString + "</OccursCount>" |
| } |
| } |
| |
| class RepAtMostOccursCountParser(rParser: Parser, intN: Long, erd: ElementRuntimeData) |
| extends RepParser(intN, rParser, erd, "AtMostOccursCount") { |
| def parseAllRepeats(pstate: PState): Unit = { |
| // repeat either n times, or occursCount times if that's less than n. |
| val n = math.min(pstate.mpstate.occursBounds, erd.minOccurs.get) |
| Rep.loopExactlyTotalN(intN.toInt, rParser, pstate, erd, this) |
| if (pstate.status ne Success) { |
| PE(pstate, "Failed to populate %s[%s]. Expected at most %s items.", |
| erd.prefixedName, pstate.mpstate.arrayPos, n) // they all must succeed, otherwise we fail here. |
| return |
| } |
| } |
| } |
| |
| class RepExactlyTotalOccursCountParser(rParser: Parser, erd: ElementRuntimeData) |
| extends RepParser(-1, rParser, erd, "ExactlyTotalOccursCount") { |
| def parseAllRepeats(pstate: PState): Unit = { |
| val ocInt = pstate.mpstate.occursBounds.toInt |
| Rep.loopExactlyTotalN(ocInt, rParser, pstate, erd, this) |
| if (pstate.status ne Success) { |
| PE(pstate, "Failed to populate %s[%s]. Expected %s item(s).", |
| erd.prefixedName, pstate.mpstate.arrayPos, ocInt) // they all must succeed, otherwise we fail here. |
| return |
| } |
| } |
| } |