blob: 23a1e07d01006cd813c23b9e6d2d8580054f5938 [file] [log] [blame]
package edu.illinois.ncsa.daffodil.processors
/* Copyright (c) 2012-2013 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.
*/
import java.math.BigInteger
import java.text.{ ParseException, ParsePosition }
import java.util.regex.Pattern
import java.nio.{ CharBuffer, ByteBuffer }
import java.nio.charset.Charset
import java.nio.charset.MalformedInputException
import scala.collection.mutable.Queue
import edu.illinois.ncsa.daffodil.Implicits._
import edu.illinois.ncsa.daffodil.dsom._
import edu.illinois.ncsa.daffodil.compiler._
import edu.illinois.ncsa.daffodil.xml.XMLUtils
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.{ YesNo, LengthKind, ByteOrder, LengthUnits }
import edu.illinois.ncsa.daffodil.util.{ Debug, LogLevel, Logging, Info }
import edu.illinois.ncsa.daffodil.util.Misc.bytes2Hex
import edu.illinois.ncsa.daffodil.processors._
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.exceptions.UnsuppressableException
import com.ibm.icu.text.{ NumberFormat, DecimalFormat }
import edu.illinois.ncsa.daffodil.grammar.Terminal
import scala.util.parsing.input.{ Reader }
import java.sql.Timestamp
import edu.illinois.ncsa.daffodil.grammar.Gram
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TextTrimKind
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TextStringJustification
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TextNumberJustification
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TextCalendarJustification
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TextBooleanJustification
import edu.illinois.ncsa.daffodil.processors.{ Parser => DaffodilParser }
abstract class StringLength(e: ElementBase)
extends DelimParserBase(e, true)
with TextReader
with Padded
with WithParseErrorThrowing {
val charset = e.knownEncodingCharset
val stringLengthInBitsFnc = e.knownEncodingStringBitLengthFunction
val codepointWidth = e.knownEncodingWidthInBits
//lazy val dp = new DFDLDelimParserStatic(e.knownEncodingStringBitLengthFunction)
lazy val removePaddingParser: Option[dp.Parser[String]] = dp.generateRemovePaddingParser(justificationTrim, padChar)
def lengthText: String
def parserName: String
def getLength(pstate: PState): (Long, PState)
def parseInput(start: PState, charset: Charset, nBytes: Long): PState
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = String.format("%sParser(%s)", parserName, lengthText)
def parse(pstate: PState): PState = withParseErrorThrowing(pstate) {
log(LogLevel.Debug, "Parsing starting at bit position: %s", pstate.bitPos)
val (nBytes, start) = getLength(pstate)
log(LogLevel.Debug, "Explicit length %s", nBytes)
if (start.bitPos % 8 != 0) { return PE(start, "%s - not byte aligned.", parserName) }
try {
val postState = parseInput(start, charset, nBytes)
return postState
} catch {
case m: MalformedInputException => { return PE(start, "%s - MalformedInputException: \n%s", parserName, m.getMessage()) }
case e: IndexOutOfBoundsException => { return PE(start, "%s - Insufficient Bits in field: IndexOutOfBounds: \n%s", parserName, e.getMessage()) }
case u: UnsuppressableException => throw u
case e: Exception => { return PE(start, "%s - Exception: \n%s", parserName, e.getStackTraceString) }
}
pstate
}
}
def unparser: Unparser = new Unparser(e) {
override def toString = String.format("%sUnparser(%s)", parserName, lengthText)
// val encoder = e.knownEncodingEncoder
def unparse(start: UState): UState = {
Assert.notYetImplemented()
}
}
}
trait VariableLength { self: Terminal =>
// Length is an expression
private lazy val eb = self.context.asInstanceOf[ElementBase]
val expr = eb.length
val exprText = expr.prettyExpr
def getLength(pstate: PState): (Long, PState) = {
val R(lengthAsAny, newVMap) = expr.evaluate(pstate.parentElement, pstate.variableMap, pstate)
val length = lengthAsAny.asInstanceOf[Long]
val start = pstate.withVariables(newVMap)
(length, start)
}
}
trait FixedLength { self: Terminal =>
// Length is a constant
private lazy val eb = self.context.asInstanceOf[ElementBase]
//Assert.invariant(eb.knownEncodingWidthInBits != -1)
// def getLength(pstate: PState): (Long, PState) = {
// (eb.fixedLength, pstate)
// }
}
abstract class StringLengthInChars(e: ElementBase, nChars: Long)
extends StringLength(e)
with WithParseErrorThrowing {
lazy val nCharParser = dp.generateInputNCharactersParser(nChars)
def getLength(pstate: PState): (Long, PState) = {
(nChars, pstate)
}
def parseInput(start: PState, charset: Charset, nChars: Long): PState = start
override def parser: DaffodilParser = new PrimParser(this, e) {
String.format("%sParser(%s)", parserName, lengthText)
def parse(start: PState): PState = withParseErrorThrowing(start) {
log(LogLevel.Debug, "Parsing starting at bit position: %s", start.bitPos)
// no longer require alignment (some encodings aren't whole bytes)
// if (start.bitPos % 8 != 0) { return PE(start, "StringFixedLengthInVariableWidthCharacters - not byte aligned.") }
log(LogLevel.Debug, "Retrieving reader")
val reader = getReader(charset, start.bitPos, start)
val result = dp.parseInputNCharacters(nCharParser, reader, removePaddingParser, justificationTrim)
result match {
case _: DelimParseFailure =>
return PE(start, "Parse failed to find exactly %s characters.", nChars)
case s: DelimParseSuccess => {
val parsedField = s.field
val parsedBits = s.numBits
val endBitPos = start.bitPos + parsedBits
log(LogLevel.Debug, "Parsed: %s", parsedField)
log(LogLevel.Debug, "Ended at bit position: %s", endBitPos)
val endCharPos = if (start.charPos == -1) nChars else start.charPos + nChars
val currentElement = start.parentElement
currentElement.setDataValue(parsedField)
val postState = start.withPos(endBitPos, endCharPos, Some(s.next))
postState
}
}
}
}
}
abstract class StringLengthInBytes(e: ElementBase)
extends StringLength(e) {
def parseInput(start: PState, charset: Charset, nBytes: Long): PState = {
val in = start.inStream
val decoder = charset.newDecoder()
val reader = getReader(charset, start.bitPos, start)
val bytes = in.getBytes(start.bitPos, nBytes.toInt)
val cb = decoder.decode(ByteBuffer.wrap(bytes))
val result = cb.toString
//val endBitPos = start.bitPos + (result.length * codepointWidth) // handles 7-bit or wider chars
val endBitPos = start.bitPos + stringLengthInBitsFnc(result)
log(LogLevel.Debug, "Parsed: " + result)
log(LogLevel.Debug, "Ended at bit position " + endBitPos)
val endCharPos = start.charPos + result.length
val currentElement = start.parentElement
val trimmedResult = dp.removePadding(removePaddingParser, justificationTrim, result)
// Assert.invariant(currentElement.getName != "_document_")
// Note: this side effect is backtracked, because at points of uncertainty, pre-copies of a node are made
// and when backtracking occurs they are used to replace the nodes modified by sub-parsers.
currentElement.setDataValue(trimmedResult)
//
// if the number of bytes was a multiple of the codepointWidth then
// we will have parsed all the bytes, so the endBitPos and endCharPos
// are synchronized still.
//
val postState = {
// TODO: Shouldn't the 8 * nBytes really be codepointWidth * nBytes?
if ((endBitPos - start.bitPos) == (8 * nBytes)) {
start.withPos(endBitPos, endCharPos, Some(reader))
} else {
Assert.invariant((endBitPos - start.bitPos) < (8 * nBytes))
start.withPos(endBitPos, -1, None)
// -1 means a subsequent primitive will have to construct
// a new reader at said bitPosition
}
}
return postState
}
}
case class StringFixedLengthInBytesFixedWidthCharacters(e: ElementBase, nBytes: Long)
extends StringLengthInBytes(e)
with FixedLength {
lazy val parserName = "StringFixedLengthInBytesFixedWidthCharacters"
lazy val lengthText = e.length.constantAsString
def getLength(pstate: PState): (Long, PState) = {
(nBytes, pstate)
}
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringFixedLengthInBytesVariableWidthCharacters(e: ElementBase, nBytes: Long)
extends StringLengthInBytes(e)
with FixedLength {
lazy val parserName = "StringFixedLengthInBytesVariableWidthCharacters"
lazy val lengthText = nBytes.toString()
def getLength(pstate: PState): (Long, PState) = {
(nBytes, pstate)
}
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringFixedLengthInVariableWidthCharacters(e: ElementBase, numChars: Long)
extends StringLengthInChars(e, numChars)
with FixedLength {
lazy val parserName = "StringFixedLengthInVariableWidthCharacters"
lazy val lengthText = e.length.constantAsString
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringVariableLengthInBytes(e: ElementBase)
//extends Terminal(e, true)
extends StringLengthInBytes(e)
with VariableLength {
lazy val parserName = "StringVariableLengthInBytes"
lazy val lengthText = exprText
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringVariableLengthInBytesVariableWidthCharacters(e: ElementBase)
//extends Terminal(e, true)
extends StringLengthInBytes(e)
with VariableLength {
lazy val parserName = "StringVariableLengthInBytesVariableWidthCharacters"
lazy val lengthText = exprText
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringVariableLengthInVariableWidthCharacters(e: ElementBase)
extends StringLengthInBytes(e)
with VariableLength {
lazy val parserName = "StringVariableLengthInVariableWidthCharacters"
lazy val lengthText = e.length.constantAsString
// val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
}
case class StringPatternMatched(e: ElementBase)
extends Terminal(e, true)
with WithParseErrorThrowing with TextReader with Padded {
val charset = e.knownEncodingCharset
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = "StringPatternMatched"
val pattern = e.lengthPattern
// The pattern will always be defined
lazy val dp = new DFDLDelimParserStatic(e.knownEncodingStringBitLengthFunction)
lazy val patternParser = dp.generateInputPatternedParser(pattern)
// TODO: Add parameter for changing CharBuffer size
val eName = e.toString()
def parse(start: PState): PState = withParseErrorThrowing(start) {
// withLoggingLevel(LogLevel.Info)
{
log(LogLevel.Debug, "StringPatternMatched - %s - Parsing pattern at byte position: %s", eName, (start.bitPos >> 3))
log(LogLevel.Debug, "StringPatternMatched - %s - Parsing pattern at bit position: %s", eName, start.bitPos)
// some encodings aren't whole bytes.
// if (start.bitPos % 8 != 0) { return PE(start, "StringPatternMatched - not byte aligned.") }
val bytePos = (start.bitPos >> 3).toInt
log(LogLevel.Debug, "Retrieving reader")
val reader = getReader(charset, start.bitPos, start)
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
// val result = d.parseInputPatterned(pattern, reader)
val result = dp.parseInputPatterned(patternParser, reader)
val postState = result match {
case _: DelimParseFailure => {
// TODO: Is this right? A no match constitutes zero length. So Nil would need to be checked?
// Because we check for Nil first, this is valid and allowed.
PE(start, "%s: No match found!", this.toString())
}
case s: DelimParseSuccess => {
val endBitPos = start.bitPos + s.numBits
log(LogLevel.Debug, "StringPatternMatched - Parsed: %s", s.field)
log(LogLevel.Debug, "StringPatternMatched - Ended at bit position %s", endBitPos)
val endCharPos = if (start.charPos == -1) s.field.length() else start.charPos + s.field.length()
val currentElement = start.parentElement
currentElement.setDataValue(s.field)
start.withPos(endBitPos, endCharPos, Some(s.next))
}
}
postState
}
}
}
def unparser: Unparser = new Unparser(e) {
def unparse(start: UState): UState = {
Assert.notYetImplemented()
}
}
}
abstract class StringDelimited(e: ElementBase)
extends DelimParserBase(e, true)
with TextReader
with Padded
with WithParseErrorThrowing {
lazy val es = e.escapeScheme
lazy val esObj = EscapeScheme.getEscapeScheme(es, e)
lazy val tm = e.allTerminatingMarkup
lazy val cname = toString
val eName = e.toString()
val charset = e.knownEncodingCharset
val elemBase = e
//val dp = new DFDLDelimParserStatic(e.knownEncodingStringBitLengthFunction)
// If there are any static delimiters, pre-process them here
val staticDelimsRaw = e.allTerminatingMarkup.filter(x => x.isConstant).map { _.constantAsString }
val staticDelimsCooked1 = staticDelimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, e).cooked })
val staticDelimsCooked = staticDelimsCooked1.flatten
val (staticDelimsParser, staticDelimsRegex) = dp.generateDelimiter(staticDelimsCooked.toSet)
val combinedStaticDelimsParser = dp.combineLongest(staticDelimsParser)
// def parseMethod(hasDelim: Boolean, delimsParser: dp.Parser[String], delimsRegex: Array[String],
// reader: Reader[Char]): DelimParseResult
def parseMethod(hasDelim: Boolean, delimsParser: dp.Parser[String], delimsRegex: Array[String],
reader: Reader[Char]): DelimParseResult = {
// TODO: Change DFDLDelimParser calls to get rid of Array.empty[String] since we're only passing a single list the majority of the time.
if (esObj.escapeSchemeKind == EscapeSchemeKind.Block) {
val (escapeBlockParser, escapeBlockEndRegex, escapeEscapeRegex) = dp.generateEscapeBlockParsers2(delimsParser,
esObj.escapeBlockStart, esObj.escapeBlockEnd, esObj.escapeEscapeCharacter, justificationTrim, padChar, true)
val removeEscapeBlocksRegex = dp.removeEscapesBlocksRegex(escapeEscapeRegex, escapeBlockEndRegex)
val parseInputParser = dp.generateInputParser2(dp.emptyParser, delimsParser, Array.empty[String], delimsRegex,
hasDelim, justificationTrim, padChar, true)
dp.parseInputEscapeBlock(escapeBlockParser, dp.emptyParser, delimsParser, reader,
justificationTrim, removeEscapeBlocksRegex, parseInputParser)
} else if (esObj.escapeSchemeKind == EscapeSchemeKind.Character) {
val delimsRegexCombined = dp.combineDelimitersRegex(Array.empty[String], delimsRegex)
val escapeCharacterParser = dp.generateInputEscapeCharacterParser2(delimsParser, delimsRegexCombined,
hasDelim, esObj.escapeCharacter, esObj.escapeEscapeCharacter, justificationTrim, padChar, true)
val esRegex = dp.convertDFDLLiteralToRegex(esObj.escapeCharacter)
val esEsRegex = dp.convertDFDLLiteralToRegex(esObj.escapeEscapeCharacter)
val removeEscapeCharacterRegex = dp.generateRemoveEscapeCharactersSameRegex(esRegex)
val removeUnescapedEscapesRegex = dp.removeUnescapedEscapesRegex(esEsRegex, esRegex)
val removeEscapeEscapesThatEscapeRegex = dp.removeEscapeEscapesThatEscapeRegex(esEsRegex, esRegex)
val removeEscapeRegex = dp.removeEscapeRegex(esRegex)
dp.parseInputEscapeCharacter(escapeCharacterParser, dp.emptyParser, delimsParser, reader,
justificationTrim, removeEscapeCharacterRegex, removeUnescapedEscapesRegex,
removeEscapeEscapesThatEscapeRegex, removeEscapeRegex, esObj.escapeCharacter,
esObj.escapeEscapeCharacter)
} else {
//d.parseInput(Set.empty[String], delimsCooked.toSet, reader, justificationTrim, padChar)
val parseInputParser = dp.generateInputParser2(dp.emptyParser, delimsParser, Array.empty[String], delimsRegex,
hasDelim, justificationTrim, padChar, true)
dp.parseInput(parseInputParser, dp.emptyParser, delimsParser, reader, justificationTrim)
}
}
def getDelims(pstate: PState): (List[String], Array[String], dp.Parser[String], Option[VariableMap]) = {
(staticDelimsCooked, staticDelimsRegex, combinedStaticDelimsParser, None)
}
// def errorIfDelimsHaveWSPStar(delims: List[String]) = {
// /* Nothing to do here only applies to Dynamic case */ }
def errorIfDelimsHaveWSPStar(delims: List[String]) = {
// TODO: refactor. This check isn't needed in static case. Only dynamic case.
if (delims.filter(x => x == "%WSP*;").length > 0) {
// We cannot detect this error until expressions have been evaluated!
log(LogLevel.Debug, "%s - Failed due to WSP* detected as a delimiter for lengthKind=delimited", eName)
elemBase.schemaDefinitionError("WSP* cannot be used as a delimiter when lengthKind=delimited!")
}
}
def processResult(result: DelimParseResult, state: PState): PState = {
result match {
case f: DelimParseFailure =>
return parser.PE(state, "%s - %s - Parse failed.", this.toString(), eName)
case s: DelimParseSuccess => {
val field = s.get
val numBits = s.numBits
log(LogLevel.Debug, "%s - Parsed: %s Parsed Bytes: %s (bits %s)", eName, field, numBits / 8, numBits)
val endCharPos = if (state.charPos == -1) s.numCharsRead else state.charPos + s.numCharsRead
val endBitPos = state.bitPos + numBits
val currentElement = state.parentElement
currentElement.setDataValue(field)
return state.withPos(endBitPos, endCharPos, Some(s.next))
}
}
}
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = cname + "(" + tm.map { _.prettyExpr } + ")"
def parse(start: PState): PState = withParseErrorThrowing(start) {
val (delimsCooked, delimsRegex, delimsParser, vars) = getDelims(start)
// We must feed variable context out of one evaluation and into the next.
// So that the resulting variable map has the updated status of all evaluated variables.
val postEvalState = vars match {
case Some(v) => start.withVariables(v)
case None => start
}
// // TODO: refactor. This check isn't needed in static case. Only dynamic case.
// if (delimsCooked.filter(x => x == "%WSP*;").length > 0) {
// // We cannot detect this error until expressions have been evaluated!
// log(LogLevel.Debug, "%s - Failed due to WSP* detected as a delimiter for lengthKind=delimited", eName)
// e.schemaDefinitionError("WSP* cannot be used as a delimiter when lengthKind=delimited!")
// }
errorIfDelimsHaveWSPStar(delimsCooked)
log(LogLevel.Debug, "%s - Looking for: %s Count: %s", eName, delimsCooked, delimsCooked.length)
val bytePos = (postEvalState.bitPos >> 3).toInt
log(LogLevel.Debug, "%s - Starting at bit pos: %s", eName, postEvalState.bitPos)
log(LogLevel.Debug, "%s - Starting at byte pos: %s", eName, bytePos)
val reader = getReader(charset, postEvalState.bitPos, postEvalState)
val hasDelim = delimsCooked.length > 0
val result = try {
parseMethod(hasDelim, delimsParser, delimsRegex, reader)
} catch {
case mie: MalformedInputException =>
throw new ParseError(e, Some(postEvalState), "Malformed input, length: %s", mie.getInputLength())
}
// result match {
// case f: DelimParseFailure =>
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// val field = s.get
// val numBits = s.numBits
// log(LogLevel.Debug, "%s - Parsed: %s Parsed Bytes: %s (bits %s)", eName, field, numBits / 8, numBits)
// val endCharPos = if (postEvalState.charPos == -1) s.numCharsRead else postEvalState.charPos + s.numCharsRead
// val endBitPos = postEvalState.bitPos + numBits
// val currentElement = postEvalState.parentElement
// currentElement.setDataValue(field)
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next))
// }
// }
processResult(result, postEvalState)
}
}
def unparser: Unparser = new Unparser(e) {
override def toString = cname + "(" + tm.map { _.prettyExpr } + ")"
def unparse(start: UState): UState =
// withLoggingLevel(LogLevel.Info)
{
val data = start.currentElement.getText
val encoder = charset.newEncoder()
start.outStream.setEncoder(encoder)
start.outStream.fillCharBuffer(data)
log(LogLevel.Debug, "Unparsed: " + start.outStream.getData)
start
}
}
}
case class StringDelimitedEndOfDataStatic(e: ElementBase)
extends StringDelimited(e) {
}
trait Dynamic { self: StringDelimited =>
override def getDelims(pstate: PState): (List[String], Array[String], dp.Parser[String], Option[VariableMap]) = {
// We must feed variable context out of one evaluation and into the next.
// So that the resulting variable map has the updated status of all evaluated variables.
var vars = pstate.variableMap
val dynamicDelimsRaw = elemBase.allTerminatingMarkup.filter(x => !x.isConstant).map {
x =>
{
val R(res, newVMap) = x.evaluate(pstate.parentElement, vars, pstate)
vars = newVMap
res
}
}
val dynamicDelimsCooked1 = dynamicDelimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, elemBase).cooked })
val dynamicDelimsCooked = dynamicDelimsCooked1.flatten
val (dynamicDelimsParser, dynamicDelimsRegex) = dp.generateDelimiter(dynamicDelimsCooked.toSet)
// Combine dynamic and with static delims if they exist
val delimsParser = dp.combineLongest(dynamicDelimsParser.union(staticDelimsParser))
val delimsCooked = dynamicDelimsCooked.union(staticDelimsCooked)
val delimsRegex = dynamicDelimsRegex.union(staticDelimsRegex)
(delimsCooked, delimsRegex, delimsParser, Some(vars))
}
// override def errorIfDelimsHaveWSPStar(delims: List[String]) = {
// // TODO: refactor. This check isn't needed in static case. Only dynamic case.
// if (delims.filter(x => x == "%WSP*;").length > 0) {
// // We cannot detect this error until expressions have been evaluated!
// log(LogLevel.Debug, "%s - Failed due to WSP* detected as a delimiter for lengthKind=delimited", eName)
// elemBase.schemaDefinitionError("WSP* cannot be used as a delimiter when lengthKind=delimited!")
// }
// }
}
case class StringDelimitedEndOfDataDynamic(e: ElementBase)
extends StringDelimited(e) with Dynamic