blob: e430ad86d0d42426c299b44b12d4d803c847f0c8 [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.parsers
import org.apache.daffodil.processors.ElementRuntimeData
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.processors.Failure
import org.apache.daffodil.processors.Success
sealed abstract class SeparatorParseStatus
object SeparatorParseStatus {
type Type = SeparatorParseStatus
sealed abstract class SeparatorSuccess extends Type
case object SeparatorFound extends SeparatorSuccess
case object SeparatorNotNeeded extends SeparatorSuccess
case object SeparatorFailed extends Type
}
/**
* Abstracts away separator position. Digests result of parsing
* item and associated separator into a summary status.
*/
sealed abstract class SeparatorParseHelper(
protected val sep: Parser,
protected val childParser: Parser,
scParserArg: Separated)
extends Serializable {
protected val scParser = scParserArg.asInstanceOf[SequenceChildParser with Separated]
def parseOneWithSeparator(state: PState, requiredOptional: RequiredOptionalStatus): ParseAttemptStatus
/**
* Sets PState status to failure when separator is not found.
*/
final protected def failedSeparator(pstate: PState, kind: String): Unit = {
val cause = pstate.processorStatus.asInstanceOf[Failure].cause
scParser.trd match {
case erd: ElementRuntimeData if (erd.isArray) =>
sep.PE(pstate, "Failed to populate %s[%s]. Missing %s separator. Cause: %s",
erd.prefixedName, pstate.mpstate.arrayPos, kind, cause)
case _ =>
sep.PE(pstate, "Failed to parse %s separator. Cause: %s.", kind, cause)
}
}
}
final class PrefixSeparatorHelper(sep: Parser, childParser: Parser, scParserArg: Separated)
extends SeparatorParseHelper(sep, childParser, scParserArg)
with InfixPrefixSeparatorHelperMixin {
override def kind = "prefix"
override def parseOneWithSeparator(pstate: PState, requiredOptional: RequiredOptionalStatus): ParseAttemptStatus =
parseOneWithInfixOrPrefixSeparator(true, pstate, requiredOptional)
}
final class InfixSeparatorHelper(sep: Parser, childParser: Parser, scParserArg: Separated)
extends SeparatorParseHelper(sep, childParser, scParserArg)
with InfixPrefixSeparatorHelperMixin {
override def kind = "infix"
override def parseOneWithSeparator(pstate: PState, requiredOptional: RequiredOptionalStatus): ParseAttemptStatus = {
val infixSepShouldBePresent =
pstate.mpstate.groupPos > 1
parseOneWithInfixOrPrefixSeparator(infixSepShouldBePresent, pstate, requiredOptional)
}
}
trait InfixPrefixSeparatorHelperMixin { self: SeparatorParseHelper =>
protected def kind: String
final protected def parseOneWithInfixOrPrefixSeparator(
shouldParseTheSep: Boolean,
pstate: PState,
requiredOptional: RequiredOptionalStatus): ParseAttemptStatus = {
val sepStatus =
if (shouldParseTheSep) {
// If a separator is expected, but optional, we may generate errors
// while searching for it which should not be propagated upward
requiredOptional match {
case _: RequiredOptionalStatus.Optional => {
// Create a point of uncertainty with which to reset if we find errors
pstate.withPointOfUncertainty("parseOneWithInfixOrPrefixSeparator",childParser.context) { pou =>
sep.parse1(pstate)
val rv = if (pstate.processorStatus eq Success) {
SeparatorParseStatus.SeparatorFound
} else {
// reset to the point of uncertainty to discard the errors
pstate.resetToPointOfUncertainty(pou)
SeparatorParseStatus.SeparatorFailed
}
rv
}
}
case _ => {
sep.parse1(pstate)
if (pstate.processorStatus eq Success)
SeparatorParseStatus.SeparatorFound
else
SeparatorParseStatus.SeparatorFailed
}
}
} else
SeparatorParseStatus.SeparatorNotNeeded
val prevBitPosBeforeChild = pstate.bitPos0b
sepStatus match {
case _: SeparatorParseStatus.SeparatorSuccess => {
childParser.parse1(pstate)
val pas =
scParser.parseResultHelper.computeParseAttemptStatus(
scParser,
prevBitPosBeforeChild, pstate,
requiredOptional)
pas
}
case _ => {
requiredOptional match {
case _: RequiredOptionalStatus.Required => {
failedSeparator(pstate, kind)
}
case _ => // No action
}
scParser.parseResultHelper.computeFailedSeparatorParseAttemptStatus(
scParser,
prevBitPosBeforeChild, pstate,
isZL = true,
requiredOptional)
}
}
}
}
final class PostfixSeparatorHelper(sep: Parser, child: Parser, scParserArg: Separated,
isSimpleDelimited: Boolean)
extends SeparatorParseHelper(sep, child, scParserArg) {
import ParseAttemptStatus._
override def parseOneWithSeparator(pstate: PState, requiredOptional: RequiredOptionalStatus): ParseAttemptStatus = {
val prevBitPosBeforeChild = pstate.bitPos0b
pstate.withPointOfUncertainty("PostfixSeparatorHelper", childParser.context) { pou =>
childParser.parse1(pstate)
val childSuccessful = pstate.processorStatus eq Success
val childFailure = !childSuccessful // just makes later logic easier to read
val bitPosAfterChildAttempt = pstate.bitPos0b
val prh = scParser.parseResultHelper
// This seems odd, but there are cases where after a failure
// the bitPos has not yet been repositioned back to where it started.
// This does happen for complex types.
val hasZLChildAttempt = prevBitPosBeforeChild == bitPosAfterChildAttempt
if (childSuccessful) {
val dataOnlyRep =
prh.computeParseAttemptStatus(
scParser,
prevBitPosBeforeChild, pstate, requiredOptional)
sep.parse1(pstate)
if (pstate.processorStatus eq Success) {
// we got the postfix sep after successful parse of the data item
// so whatever the status of the item was, that's the status overall.
dataOnlyRep
} else {
// child successful, but
// no postfix sep found.
// capture failure reason, but reset position to before the child,
// and remove any elements. That way this behaves like a prefix
// or infix separator, where if you don't get the sep, then you
// usually (except first one in infix case) don't run the child parser
val failure = pstate.processorStatus.asInstanceOf[Failure]
Assert.invariant(!pstate.isPointOfUncertaintyResolved(pou))
pstate.resetToPointOfUncertainty(pou)
pstate.setFailed(failure.cause)
failedSeparator(pstate, "postfix")
prh.computeFailedSeparatorParseAttemptStatus(scParser, prevBitPosBeforeChild, pstate, hasZLChildAttempt, requiredOptional)
}
} else {
Assert.invariant(childFailure)
// We might be tempted to just try the postfix sep now,
// to see if the postfix sep is there immediately.
// This is, however, problematic.
//
// We're depending on this item being an element with lengthKind="delimited"
// which can't have a zero-length representation, so that if there is zero-data before
// the postfix separator, we can declare the item to be absentRep.
//
// But what if the element is not lengthKind 'delimited'.
// Or what if it has complex type? But has a non-zero-length structure where this
// same delimiter is used at a more deeply nested depth?
//
// If the type is simple, and lengthKind 'delimited' we can proceed.
// Otherwise we punt and call it missing.
//
if (isSimpleDelimited) {
val failure = pstate.processorStatus.asInstanceOf[Failure]
Assert.invariant(!pstate.isPointOfUncertaintyResolved(pou))
pstate.resetToPointOfUncertainty(pou)
sep.parse1(pstate)
if (pstate.isSuccess) {
val posAfterSep = pstate.bitPos0b
// failed child, but success on postfix sep.
// behave here just like a prefix separator that was then followed by a child failure
pstate.setFailed(failure.cause)
val pas =
prh.computeParseAttemptStatus(
scParser,
prevBitPosBeforeChild, pstate,
requiredOptional)
val res = pas match {
case AbsentRep => {
pstate.dataInputStream.setBitPos0b(posAfterSep)
MissingSeparator
}
case _ => pas
}
res
} else {
// the separator failed on ZL data
// so no chance on a ZL representation here.
val isZL = false
prh.computeFailedSeparatorParseAttemptStatus(scParser, prevBitPosBeforeChild, pstate, isZL, requiredOptional)
}
} else {
// not simple delimited. We really have no way of knowing
// if trying to parse just the sep now makes any sense at all.
// (ex: the child could be failing because it is fixed length, with asserts that check the value
// that fail.)
val isZL = false
prh.computeFailedParseAttemptStatus(scParser, prevBitPosBeforeChild, pstate, isZL, requiredOptional)
}
}
}
}
}