blob: 69eb81be92872e0a38f81f01de57d0a2e8f7de7a [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 PrimParser(gram: Gram, contextArg: SchemaComponent)
extends DaffodilParser(contextArg) {
def toBriefXML(depthLimit: Int = -1): String = {
"<" + gram.name + "/>"
}
override def toString = toBriefXML()
}
case class ElementBegin(e: ElementBase) extends Terminal(e, true) {
val isHidden = e.isHidden
def parser: DaffodilParser = new PrimParser(this, e) {
override def toBriefXML(depthLimit: Int = -1): String = {
"<ElementBegin name='" + e.name + "'/>"
}
/**
* ElementBegin just adds the element we are constructing to the infoset and changes
* the state to be referring to this new element as what we're parsing data into.
*/
def parse(start: PState): PState = {
val currentElement = Infoset.newElement(e, isHidden)
log(LogLevel.Debug, "currentElement = %s", currentElement)
val priorElement = start.infoset
priorElement.addElement(currentElement)
log(LogLevel.Debug, "priorElement = %s", priorElement)
val postState = start.withParent(currentElement)
postState
}
}
def unparser = new DummyUnparser(e)
// def unparser: Unparser = new Unparser(e) {
// override def toString = "<" + e.name + ">"
//
// /**
// * Changes the state to refer to the next element in the infoset as the element to unparse.
// */
// def unparse(start: UState): UState = {
// val nextElement = {
// //
// // TODO FIXME: THis can't be correct. The elementBegin shouldn't be writing out element contents.
// // That should happen in content unparsers. This unparser should just set things up so content
// // unparsers see the correct element to take the contents of. Which really means just changing the
// // parent pointer in the UState.
// //
// // TODO: should not try/catch - should return a failed UState on error
// try { //if content contains elements
// if (!start.childIndexStack.isEmpty) {
// if (start.childPos != 1) { //if not first child, write unparsed result of previous child to outputStream
// // val encoder = e.knownEncodingEncoder
// // start.outStream.setEncoder(encoder)
// start.outStream.write()
// }
// start.currentElement.getContent().get(start.childPos.asInstanceOf[Int] - 1).asInstanceOf[org.jdom.Element]
// } else {
// // val encoder = e.knownEncodingEncoder
// // start.outStream.setEncoder(encoder)
// start.currentElement.getContent().get(0).asInstanceOf[org.jdom.Element]
// }
// } catch {
// case u: UnsuppressableException => throw u
// case e: Exception => start.currentElement //if content is text
// }
// }
//
// start.withCurrent(nextElement).moveOverByOneElement
// }
// }
}
// FIXME: ComplexElementBeginPattern
// Must be refactored to share code with ElementBegin, because enhancements made
// there must otherwise be reproduced here. Ditto for ComplexElementEndPattern and EndElement.
// Furthermore, the whole ElementBegin/ElementEnd pairs idiom wants to be turned into
// A runtime parser combinator for diagnostics/debugging reasons, so that a try-finally can be used
// to insure we remove failed elements from the infoset, etc. See StmtEval, which is this kind of
// an 'enclosing' combinator I'm describing.
//case class ComplexElementBeginPattern(e: ElementBase)
// extends Terminal(e, e.isComplexType == true && e.lengthKind == LengthKind.Pattern)
// with WithParseErrorThrowing {
// Assert.invariant(e.isComplexType)
//
// val charset = e.knownEncodingCharset
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = "<" + e.name + " dfdl:lengthKind='pattern'>"
// var cbuf = CharBuffer.allocate(1024) // TODO: Performance: get a char buffer from a pool.
// val pattern = e.lengthPattern
//
// /**
// * ElementBegin just adds the element we are constructing to the infoset and changes
// * the state to be referring to this new element as what we're parsing data into.
// */
// def parse(start: PState): PState = withParseErrorThrowing(start) {
// withLoggingLevel(LogLevel.Info) {
// val eName = e.toString()
//
// log(LogLevel.Debug, "ComplexElementBeginPattern - %s - Parsing pattern at byte position: %s", eName, (start.bitPos >> 3)))
// log(LogLevel.Debug, "ComplexElementBeginPattern - %s - Parsing pattern at bit position: %s", eName, start.bitPos))
//
// val in = start.inStream
//
// val reader = in.getCharReader(charset, start.bitPos)
//
// val d = new delimsearch.DelimParser(e)
//
// var result: delimsearch.DelimParseResultult = new delimsearch.DelimParseResultult
//
// result = d.parseInputPatterned(pattern, reader)
//
// val postState1 =
// if (result.isSuccess) {
// val endBitPos = start.bitPos + result.numBits
// log(LogLevel.Debug, "Parsed: %s", result.field))
// log(LogLevel.Debug, "Ended at bit position %s", endBitPos))
//
// // LETS NOT DO IT THIS WAY
// // val limitedInStream = in.withLimit(start.bitPos, endBitPos)
// // Since we've created a new sub-stream with just the limited part of data in it,
// // don't forget to have the position in it start at zero.
// // start withEndBitLimit (endBitPos) withInStream (limitedInStream) withPos (0, 0)
// // INSTEAD, LETS GET THE END LIMIT RIGHT
// start withEndBitLimit (endBitPos)
// } else { return PE(start, "%s: No match found!", this.toString()) }
// val currentElement = Infoset.newElement(e)
// log(LogLevel.Debug, "currentElement = %s", currentElement))
// val priorElement = postState1.infoset
// priorElement.addElement(currentElement)
// log(LogLevel.Debug, "priorElement = %s", priorElement))
// val postState2 = postState1 withParent (currentElement)
// postState2
// }
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
abstract class ElementEndBase(e: ElementBase) extends Terminal(e, true) {
def toPrettyString = "</" + e.name + prettyStringModifier + ">"
def prettyStringModifier: String
def move(pstate: PState): PState // implement for different kinds of "moving over to next thing"
def parser: DaffodilParser = new PrimParser(this, e) {
override def toBriefXML(depthLimit: Int = -1): String = {
"<ElementEnd name='" + e.name + "'/>"
}
override def toString = toPrettyString
/**
* ElementEnd just moves back to the parent element of the current one.
*/
def parse(start: PState): PState = {
val currentElement = start.parentElement
// Assert.invariant(currentElement.getName() != "_document_" )
val priorElement = currentElement.parent
log(LogLevel.Debug, "priorElement = %s", priorElement)
val postState = move(start.withParent(priorElement))
postState
}
}
def unparser: Unparser = new Unparser(e) {
override def toString = "</" + e.name + ">"
/**
* Changes state to refer to parent element of the current one.
*/
def unparse(start: UState): UState = {
val postState = {
if (start.currentElement.getName != start.rootName) {
val parent = start.currentElement.getParentElement()
val state = start.withCurrent(parent)
state
} else {
start
}
}
postState
}
}
}
case class ElementEnd(e: ElementBase) extends ElementEndBase(e) {
def move(pstate: PState) = pstate.moveOverByOneElement
def prettyStringModifier = ""
}
case class ElementEndNoRep(e: ElementBase) extends ElementEndBase(e) {
// if there is no rep (inputValueCalc), then we do create a new child so that index must advance,
// but we don't create anything new as far as the group is concerned, and we don't want
// the group 'thinking' that there's a prior sibling inside the group and placing a
// separator after it. So in the case of NoRep, we don't advance group child, just element child.
def move(pstate: PState) = pstate.moveOverOneElementChildOnly
def prettyStringModifier = "(NoRep)"
}
//case class ComplexElementEndPattern(e: ElementBase) extends Terminal(e, e.isComplexType == true && e.lengthKind == LengthKind.Pattern) {
// // TODO: Should this be more generic; is there a way to detect state from the current element to tell us if it's time
// // to pop the input stack?
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = "</" + e.name + " dfdl:lengthKind='pattern'>"
//
// /**
// * ElementEnd just moves back to the parent element of the current one.
// */
// def parse(start: PState): PState = {
// val currentElement = start.parentElement
// log(LogLevel.Debug, "currentElement = %s", currentElement))
// var priorElement = currentElement.parent
// log(LogLevel.Debug, "priorElement = %s", priorElement))
// val postState = start.withParent(priorElement).moveOverByOneElement.withLastInStream()
// postState
// }
// }
//
// def unparser: Unparser = new DummyUnparser(e)
//}
///**
// * The I/O layer should be written to use Java's NIO Channels, and Direct ByteBuffers for file I/O. This is the
// * fastest stuff in the Java stack.
// *
// * The basic design for known-length parsing
// * is to do a bounds check on whether we have enough buffer space to accomplish the reading of data
// * within the buffer. If not, repositioning/refreshing the buffer. If so, then we want to issue a
// * single block read per element which both reads and converts to the right character encoding, or
// * directly to the right type (float, long, double, etc) when the result type is a primitive.
// *
// * For types with more complexity, (binary dates for example) then the previous would still be done
// * followed by a conversion of some sort.
// */
//
//case class StringFixedLengthInBytes(e: ElementBase, nBytes: Long)
// extends Terminal(e, true)
// with TextReader
// with Padded
// with WithParseErrorThrowing {
//
// val charset = e.knownEncodingCharset
//
// def parser: DaffodilParser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1) = {
// "<StringFixedLengthInBytesParser length='" + nBytes + "'/>"
// }
//
// override def toString = toBriefXML()
//
// val codepointWidth = e.knownEncodingWidthInBits
// Assert.invariant(codepointWidth != -1)
//
// def parse(start: PState): PState = withParseErrorThrowing(start) {
// // withLoggingLevel(LogLevel.Info)
// {
//
// log(LogLevel.Debug, "StringFixedLengthInBytes - Parsing starting at bit position: %s", start.bitPos))
//
// // if (start.bitPos % 8 != 0) { return PE(start, "StringFixedLengthInBytes - not byte aligned.") }
//
// val in = start.inStream
//
// val bytePos = (start.bitPos >> 3).toInt
//
// val decoder = charset.newDecoder()
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// try {
// //
// // nBytes can be a little bit longer that the decoder actually consumes
// // because some characters are less than a byte wide (7-bit ascii packed variant)
// //
// // Note: This code goes around the DFDLCharReader layer because that layer doesn't provide
// // us a way to limit the number of bytes. (Perhaps that could be changed so this could use it?)
// //
//
// val reader = getReader(charset, start.bitPos, start)
// val bytes = in.getBytes(start.bitPos, nBytes.toInt)
// decoder.reset()
// val cb = decoder.decode(ByteBuffer.wrap(bytes))
// val result = cb.toString()
// val endBitPos = start.bitPos + (result.length * codepointWidth) // handles 7-bit or wider chars
// log(LogLevel.Debug, "Parsed: %s", result))
// log(LogLevel.Debug, "Ended at bit position %s", endBitPos))
// val endCharPos = start.charPos + result.length
// val currentElement = start.parentElement
// val trimmedResult = d.removePadding(result, justificationTrim, padChar)
// // 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 =
// 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
// } catch {
// case m: java.nio.charset.MalformedInputException => { return PE(start, "StringFixedLengthInBytes - Malformed data. Could not decode into %s characters.", charset.name()) }
// case e: IndexOutOfBoundsException => { return PE(start, "StringFixedLengthInBytes - Insufficient Bits in field: IndexOutOfBounds: \n%s", e.getMessage()) }
// case u: UnsuppressableException => throw u
// // case e: Exception => { return PE(start, "StringFixedLengthInBytes - Exception: \n%s", e.getMessage()) }
// }
// }
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// override def toString = "StringFixedLengthInBytesUnparser(" + nBytes + ")"
//
// def unparse(start: UState): UState = {
// // setLoggingLevel(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 StringFixedLengthInBytesVariableWidthCharacters(e: ElementBase, nBytes: Long)
// extends Terminal(e, true)
// with WithParseErrorThrowing
// with Padded {
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = "StringFixedLengthInBytesVariableWidthCharactersParser(" + nBytes + ")"
//
// val charset = e.knownEncodingCharset
//
// def parse(start: PState): PState = withParseErrorThrowing(start) {
// // withLoggingLevel(LogLevel.Info)
// {
//
// log(LogLevel.Debug, "Parsing starting at bit position: %s", start.bitPos))
//
// // no longer require alignment (some charsets aren't aligned.)
// // if (start.bitPos % 8 != 0) { return PE(start, "StringFixedLengthInBytesVariableWidthCharacters - not byte aligned.") }
//
// val in = start.inStream
//
// val decoder = charset.newDecoder()
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
// try {
// val bytes = in.getBytes(start.bitPos, nBytes.toInt)
// decoder.reset()
// val cb = decoder.decode(ByteBuffer.wrap(bytes))
// val result = cb.toString
// val endBitPos = start.bitPos + (nBytes.toInt * 8)
// log(LogLevel.Debug, "Parsed: %s", result))
// log(LogLevel.Debug, "Ended at bit position %s", endBitPos))
// val endCharPos = start.charPos + result.length
// val currentElement = start.parentElement
// // 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(d.removePadding(result, justificationTrim, padChar))
// val postState = start.withPos(endBitPos, -1, None) // charPos = -1 tells subsequent calls to construct
// return postState
// } catch {
// case e: IndexOutOfBoundsException => { return PE(start, "StringFixedLengthInBytesVariableWidthCharacters - Insufficient Bits in field. IndexOutOfBoundsException: Index was: %s", e.getMessage()) }
// case u: UnsuppressableException => throw u
// case e: Exception => { return PE(start, "StringFixedLengthInBytesVariableWidthCharacters - Exception: \n%s", e.getMessage()) }
// }
// start
// }
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
//
//case class StringFixedLengthInVariableWidthCharacters(e: ElementBase, nChars: Long)
// extends Terminal(e, true)
// with WithParseErrorThrowing with TextReader with Padded {
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = "StringFixedLengthInVariableWidthCharactersParser(" + nChars + ")"
//
// val charset = e.knownEncodingCharset
//
// 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 byteReader = in.byteReader.atPos(bytePos)
// //
// // val reader = byteReader.charReader(decoder.charset().name())
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = d.parseInputNCharacters(nChars, reader, justificationTrim, padChar)
//
// 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 = start.charPos + nChars
// //val endCharPos = reader.characterPos + nChars
// val endCharPos = if (start.charPos == -1) nChars else start.charPos + nChars
// val currentElement = start.parentElement
// currentElement.setDataValue(parsedField)
// // val postState = start.withPos(endBitPos, endCharPos)
// val postState = start.withPos(endBitPos, endCharPos, Some(s.next))
// postState
//
// // //setLoggingLevel(LogLevel.Info)
// //
// // log(LogLevel.Debug, this.toString() + " - Parsing starting at bit position: " + start.bitPos))
// //
// // // We don't know the width of the characters, so decode as much data as possible.
// // // We will truncate as necessary later.
// // val in = start.inStream
// // val (endBitPos, _) = in.fillCharBufferMixedData(cbuf, start.bitPos, decoder)
// //
// // val result = cbuf.toString
// //
// // if (result == null) { return start.failed(this.toString() + " - Result was null!") }
// //
// // val finalResult = result.substring(0, nChars.toInt) // Truncate
// // val finalResultBytes = finalResult.getBytes(decoder.charset()).length
// // val finalBitPos = 8 * finalResultBytes + start.bitPos
// //
// // if (finalResult.length < nChars) { return start.failed(this.toString() + " - Result(" + finalResult.length + ") was not at least nChars (" + nChars + ") long.") }
// //
// // log(LogLevel.Debug, "Parsed: " + finalResult))
// // log(LogLevel.Debug, "Ended at bit position " + finalBitPos))
// // val endCharPos = start.charPos + nChars
// // val currentElement = start.parentElement
// // // 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.addContent(new org.jdom.Text(finalResult))
// // val postState = start.withPos(finalBitPos, endCharPos)
// // postState
// }
// }
// }
// }
//
// def unparser: Unparser = new DummyUnparser(e)
//}
//
//case class StringDelimitedEndOfData(eArg: ElementBase)
// extends Terminal(eArg, true)
// with WithParseErrorThrowing with TextReader with Padded {
// def e = eArg
// lazy val es = e.escapeScheme
// lazy val esObj = EscapeScheme.getEscapeScheme(es, e)
// lazy val tm = e.allTerminatingMarkup
// lazy val cname = toString
//
// val charset = e.knownEncodingCharset
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = cname + "(" + tm.map { _.prettyExpr } + ")"
//
// def parse(start: PState): PState = withParseErrorThrowing(start) {
//
// val eName = e.toString()
//
// // 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 = start.variableMap
// val delimsRaw = e.allTerminatingMarkup.map {
// x =>
// {
// val R(res, newVMap) = x.evaluate(start.parentElement, vars, start)
// vars = newVMap
// res
// }
// }
// val delimsCooked1 = delimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, e).cooked })
// val delimsCooked = delimsCooked1.flatten
// //System.err.println("startCharPos: " + start.charPos)
// val postEvalState = start.withVariables(vars)
//
// 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!")
// }
//
// 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 d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = try {
//
// if (esObj.escapeSchemeKind == EscapeSchemeKind.Block) {
// // result = d.parseInputEscapeBlock(Set.empty[String], delimsCooked.toSet, reader,
// // esObj.escapeBlockStart, esObj.escapeBlockEnd, esObj.escapeEscapeCharacter)
// d.parseInputEscapeBlock(Set.empty[String], delimsCooked.toSet, reader,
// esObj.escapeBlockStart, esObj.escapeBlockEnd, esObj.escapeEscapeCharacter, justificationTrim, padChar)
// } else if (esObj.escapeSchemeKind == EscapeSchemeKind.Character) {
// d.parseInputEscapeCharacter(Set.empty[String], delimsCooked.toSet, reader,
// esObj.escapeCharacter, esObj.escapeEscapeCharacter, justificationTrim, padChar)
// } else {
// d.parseInput(Set.empty[String], delimsCooked.toSet, reader, justificationTrim, padChar)
// }
// } catch {
// case mie: MalformedInputException =>
// throw new ParseError(e, Some(postEvalState), "Malformed input, length: %s", mie.getInputLength())
// }
//
// result match {
// case f: DelimParseFailure =>
// //System.err.println("StringDelimitedEndOfData_END: " + new Timestamp(System.currentTimeMillis()));
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// //System.err.println("StringDelimitedEndOfData_END: " + new Timestamp(System.currentTimeMillis()));
// val field = s.get
// val numBits = s.numBits
// log(LogLevel.Debug, "%s - Parsed: %s Parsed Bytes: %s (bits %s)", eName, field, numBits / 8, numBits))
// //System.err.println(postEvalState.charPos)
// //val endCharPos = reader.characterPos + field.length()
// val endCharPos = if (postEvalState.charPos == -1) s.numCharsRead else postEvalState.charPos + s.numCharsRead
// //val endCharPos = postEvalState.charPos + field.length()
// val endBitPos = postEvalState.bitPos + numBits
// val currentElement = postEvalState.parentElement
// currentElement.setDataValue(field)
// //return postEvalState.withPos(endBitPos, endCharPos)
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next))
// }
// }
// }
// }
//
// 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 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
//
// // TODO: Add parameter for changing CharBuffer size
//
// def parse(start: PState): PState = withParseErrorThrowing(start) {
// // withLoggingLevel(LogLevel.Info)
// {
// val eName = e.toString()
//
// 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 byteReader = in.byteReader.atPos(bytePos)
// // val reader = byteReader.charReader(decoder.charset().name())
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = d.parseInputPatterned(pattern, reader)
//
// val postState = result match {
// case _: DelimParseFailure => { 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 = start.charPos + result.field.length()
// //val endCharPos = reader.characterPos + result.field.length()
// 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)
// start.withPos(endBitPos, endCharPos, Some(s.next))
// }
//
// }
// // log(LogLevel.Debug, "Parsing starting at bit position: " + start.bitPos))
// // val in = start.inStream
// // var bitOffset = 0L
// //
// // val (result, endBitPos, theState) = in.fillCharBufferWithPatternMatch(cbuf, start.bitPos, decoder, pattern)
// //
// // val postState = theState match {
// // case SearchResult.NoMatch => start.failed(this.toString() + ": No match found!")
// // case SearchResult.PartialMatch => start.failed(this.toString() + ": Partial match found!")
// // case SearchResult.FullMatch => {
// // log(LogLevel.Debug, "Parsed: " + result))
// // log(LogLevel.Debug, "Ended at bit position " + endBitPos))
// // val endCharPos = start.charPos + result.length()
// // val currentElement = start.parentElement
// // currentElement.setDataValue(result)
// // start.withPos(endBitPos, endCharPos)
// // }
// // }
// // postState
// postState
// }
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
abstract class ConvertTextNumberPrim[S](e: ElementBase, guard: Boolean)
extends Terminal(e, guard) {
protected def getNum(s: Number): S
protected val GramName: String
protected val GramDescription: String
override def toString = "to(xs:" + GramName + ")"
protected def numFormat: NumberFormat
protected def isInt: Boolean
protected def isInvalidRange(n: java.lang.Number): Boolean
protected def getStringFormat(n: S): String
def compare(num1: Number, num2: Number) = {
val bd1 = new java.math.BigDecimal(num1.toString)
val bd2 = new java.math.BigDecimal(num2.toString)
bd1.compareTo(bd2)
}
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = "to(xs:" + GramName + ")"
def parse(start: PState): PState = {
val node = start.parentElement
var str = node.dataValue
Assert.invariant(str != null) // worst case it should be empty string. But not null.
val resultState = try {
// Strip leading + (sign) since the DecimalFormat can't handle it
if (str.length > 0 && str.charAt(0) == '+') {
// TODO: There needs to be a way to restore '+' in the unparse, but that will be in the format field
str = str.substring(1)
}
if (str == "") return PE(start, "Convert to %s (for xs:%s): Cannot parse number from empty string", GramDescription, GramName)
// Need to use Decimal Format to parse even though this is an Integral number
val df = numFormat
val pos = new ParsePosition(0)
val num = try {
df.parse(str, pos)
} catch {
case u: UnsuppressableException => throw u
case e: Exception =>
return PE(start, "Convert to %s (for xs:%s): Parse of '%s' threw exception %s",
GramDescription, GramName, str, e)
}
if (isInvalidRange(num)) {
return PE(start, "Convert to %s (for xs:%s): Out of Range: '%s' converted to %s, is not in range for the type.",
GramDescription, GramName, str, num)
}
// Verify that what was parsed was what was passed exactly in byte count.
// Use pos to verify all characters consumed & check for errors!
if (pos.getIndex != str.length) {
return PE(start, "Convert to %s (for xs:%s): Unable to parse '%s' (using up all characters).",
GramDescription, GramName, str)
}
// convert to proper type
val asNumber = getNum(num)
// This isn't entirely correct, Number doesn't implement comparator
// must perform this check using BigDecimal.compareTo, found when implementing
// facets. 01/29/2013
// // Verify no digits lost (the number was correctly transcribed)
// if (isInt && asNumber.asInstanceOf[Number] != num) {
// // Transcription error
// return PE(start, "Convert to %s (for xs:%s): Invalid data: '%s' parsed into %s, which converted into %s.",
// GramDescription, GramName, str, num, asNumber)
// }
// Verify no digits lost (the number was correctly transcribed)
if (isInt && (compare(asNumber.asInstanceOf[Number], num) != 0)) {
// Transcription error
return PE(start, "Convert to %s (for xs:%s): Invalid data: '%s' parsed into %s, which converted into %s.",
GramDescription, GramName, str, num, asNumber)
}
// The following change was made because of the issues with the float
// adding a position of precision to the Number object. At some point we
// will want to revert this back to the actual type but this is a quick fix
// for the issues we were having with the 0.003 vs 0.0030 error in test_DelimProp_05
//
//node.setText(asNumber.toString)
val result = getStringFormat(asNumber)
node.setDataValue(result)
start
} // catch { case e: Exception => start.failed("Failed to convert %s to an xs:%s" + GramName) }
resultState
}
}
// TODO: consolidate duplicate code
def unparser: Unparser = new Unparser(e) {
override def toString = "to(xs:" + GramName + ")"
/*
* Converts data to number format, returns unparse exception if data cannot be converted to given format.
*/
def unparse(start: UState): UState = {
// TODO: OK to get from infoset?
var str = start.currentElement.getText //gets data from element being unparsed
Assert.invariant(str != null) // worst case it should be empty string. But not null.
if (str == "") return UE(start, "Convert to %s (for xs:%s): Cannot unparse number from empty string", GramDescription, GramName)
//make sure data can parse to appropriate type
val df = numFormat
val pos = new ParsePosition(0)
val num = try {
df.parse(str, pos)
} catch {
case u: UnsuppressableException => throw u
case e: Exception =>
return UE(start, "Convert to %s (for xs:%s): Unparse of '%s' threw exception %s",
GramDescription, GramName, str, e)
}
if (isInvalidRange(num)) {
return UE(start, "Convert to %s (for xs:%s): Out of Range: '%s' converted to %s, is not in range for the type.",
GramDescription, GramName, str, num)
}
// Verify that what was unparsed was what was passed exactly in byte count.
// Use pos to verify all characters consumed & check for errors!
if (pos.getIndex != str.length) {
return UE(start, "Convert to %s (for xs:%s): Unable to unparse '%s' (using up all characters).",
GramDescription, GramName, str)
}
// convert to proper type
val asNumber = getNum(num)
// Verify no digits lost (the number was correctly transcribed)
if (isInt && asNumber.asInstanceOf[Number] != num) {
// Transcription error
return UE(start, "Convert to %s (for xs:%s): Invalid data: '%s' unparsed into %s, which converted into %s.",
GramDescription, GramName, str, num, asNumber)
}
// TODO: Restore leading '+' sign and leading/trailing 0's, etc. (Need to overwrite number with old formatting in CharBuffer
// log(LogLevel.Debug, "Adding text number " + asNumber.toString))
start
}
}
}
abstract class ConvertTextIntegerNumberPrim[T](e: ElementBase, g: Boolean)
extends ConvertTextNumberPrim[T](e, g) {
override def numFormat = NumberFormat.getIntegerInstance()
override def isInt = true
protected override def getStringFormat(n: T): String = n.toString()
protected def isInvalidRange(n: java.lang.Number): Boolean = {
//
// Note: Scala has no class analogous to java.lang.Number. There's no common
// base class above its number types (as there isn't above the Java *primitive* number types.)
//
// We're being handed here a java 'boxed' number type, and those have common parent Number.
//
// println("number's actual type is: " + n.getClass.getName)
//
// This method only for things that fit in range of a Long. (i.e., not unbounded size Integer, and not unsignedLong
// Nevertheless, if invalid data much too long for the real numeric type is what is found in the data
// then a java BigInteger (or maybe even BigDecimal might get passed here.
//
// The only thing we can check is whether there is conversion to a long available.
// e.g., like this: Assert.invariant(n.isInstanceOf[{ def longValue : Long}])
// But that's eliminated by erasure, so we'll just do without.
//
if (n == null) false // we tolerate null here. Something else figures out the error.
else {
val l = n.longValue
isInvalidRange(l)
}
}
def min: Long
def max: Long
private def isInvalidRange(l: Long) = {
l < min || l > max
}
}
abstract class ConvertTextFloatingPointNumberPrim[T](e: ElementBase, g: Boolean)
extends ConvertTextNumberPrim[T](e, g) {
override def numFormat = NumberFormat.getNumberInstance() // .getScientificInstance() Note: scientific doesn't allow commas as grouping separators.
override def isInt = false
protected override def getStringFormat(n: T): String = {
//val trailingZeroes = """0*(?!<[1-9])$"""
val trailingZeroes = """(?<=[1-9])(0*)$""".r
val trailingZeroesBeforeExponent = """(?<=[1-9])(0*?)(?=E.*)""".r
val nAsStr = n.toString()
if (nAsStr.contains("E") || nAsStr.contains("e")) {
// Exponent
return trailingZeroesBeforeExponent.replaceAllIn(nAsStr, "")
} else {
return trailingZeroes.replaceAllIn(nAsStr, "")
}
nAsStr
}
}
case class ConvertTextIntegerPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[BigInteger](e, true) {
protected override def getNum(num: Number) = new BigInteger(num.toString)
protected override val GramName = "integer"
protected override val GramDescription = "Unbounded Integer"
protected override def isInvalidRange(n: java.lang.Number): Boolean = false
def min = -1 // ignored
def max = -1 // ignored
}
case class ConvertTextLongPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Long](e, true) {
protected override def getNum(num: Number) = num.longValue
protected override val GramName = "long"
protected override val GramDescription = "Long Integer"
val min = Long.MinValue
val max = Long.MaxValue
}
case class ConvertTextIntPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Int](e, true) {
protected override def getNum(num: Number) = num.intValue
protected override val GramName = "int"
protected override val GramDescription = "Integer"
val min = Int.MinValue.toLong
val max = Int.MaxValue.toLong
}
case class ConvertTextShortPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Short](e, true) {
protected override def getNum(num: Number) = num.shortValue
protected override val GramName = "short"
protected override val GramDescription = "Short Integer"
val min = Short.MinValue.toLong
val max = Short.MaxValue.toLong
}
case class ConvertTextBytePrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Byte](e, true) {
protected override def getNum(num: Number) = num.byteValue
protected override val GramName = "byte"
protected override val GramDescription = "Byte"
val min = Byte.MinValue.toLong
val max = Byte.MaxValue.toLong
}
case class ConvertTextUnsignedLongPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[BigInteger](e, true) {
protected override def getNum(num: Number) = new BigInteger(num.toString)
protected override val GramName = "unsignedLong"
protected override val GramDescription = "Unsigned Long"
protected override def isInvalidRange(jn: java.lang.Number) = {
jn match {
case n: BigInteger => {
n.compareTo(BigInteger.ZERO) < 0 || n.compareTo(BigInteger.ONE.shiftLeft(64)) >= 0
}
case null => false // tolerate null. Deal with that error elsewhere.
case _ => {
val n = jn.longValue()
n < 0 // note: the other side of the check is inherently ok since a Long must be smaller than an unsignedLong.
}
}
}
val min = 0.toLong
val max = -1.toLong // unused.
}
case class ConvertTextUnsignedIntPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Long](e, true) {
protected override def getNum(num: Number) = num.longValue
protected override val GramName = "unsignedInt"
protected override val GramDescription = "Unsigned Int"
val min = 0L
val max = (1L << 32) - 1L
}
// TODO: Restore leading '+' sign and leading/trailing 0's
case class ConvertTextUnsignedShortPrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Int](e, true) {
protected override def getNum(num: Number) = num.intValue
protected override val GramName = "unsignedShort"
protected override val GramDescription = "Unsigned Short"
val min = 0L
val max = (1L << 16) - 1L
}
case class ConvertTextUnsignedBytePrim(e: ElementBase) extends ConvertTextIntegerNumberPrim[Short](e, true) {
protected override def getNum(num: Number) = num.shortValue
protected override val GramName = "unsignedByte"
protected override val GramDescription = "Unsigned Byte"
val min = 0L
val max = (1L << 8) - 1L
}
case class ConvertTextDoublePrim(e: ElementBase) extends ConvertTextFloatingPointNumberPrim[Double](e, true) {
protected override def getNum(num: Number) = num.doubleValue
protected override val GramName = "double"
protected override val GramDescription = "Double"
protected def isInvalidRange(n: java.lang.Number): Boolean = false
}
case class ConvertTextFloatPrim(e: ElementBase) extends ConvertTextFloatingPointNumberPrim[Float](e, true) {
protected override def getNum(num: Number) = num.floatValue
protected override val GramName = "float"
protected override val GramDescription = "Float"
protected def isInvalidRange(n: java.lang.Number): Boolean = {
if (n == null) return false // tolerate null here. We catch that error elsewhere.
val d = n.doubleValue()
if (d.isNaN) false
else (d < Float.MinValue) || d > Float.MaxValue
}
}
abstract class Primitive(e: AnnotatedSchemaComponent, guard: Boolean = false)
extends Terminal(e, guard) {
override def toString = "Prim[" + name + "]"
def parser: DaffodilParser = DummyParser(e)
def unparser: Unparser = DummyUnparser(e)
}
abstract class DelimParserBase(e: Term, guard: Boolean) extends Terminal(e, guard){
override def toString = "DelimParserBase[" + name + "]"
val dp = new DFDLDelimParserStatic(e.knownEncodingStringBitLengthFunction)
}
abstract class ZonedTextNumberPrim(e: ElementBase, guard: Boolean) extends Terminal(e, guard) {
def parser: DaffodilParser = new PrimParser(this, e) {
def parse(start: PState): PState = {
Assert.notYetImplemented()
}
}
def unparser: Unparser = new Unparser(e) {
def unparse(start: UState): UState = {
Assert.notYetImplemented()
}
}
}
case class ZonedTextBytePrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
case class ZonedTextShortPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
case class ZonedTextIntPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
case class ZonedTextLongPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
trait RuntimeExplicitLengthMixin[T] {
self: Terminal =>
def e: ElementBase
lazy val toBits = e.lengthUnits match {
case LengthUnits.Bits => 1
case LengthUnits.Bytes => 8
case _ => e.schemaDefinitionError("Binary Numbers must have length units of Bits or Bytes.")
}
def getBitLength(s: PState): (PState, Long) = {
val R(nBytesAsAny, newVMap) = e.length.evaluate(s.parentElement, s.variableMap, s)
val nBytes = nBytesAsAny.asInstanceOf[Long]
val start = s.withVariables(newVMap)
(start, nBytes * toBits)
}
def getLength(s: PState): (PState, Long) = {
val R(nBytesAsAny, newVMap) = e.length.evaluate(s.parentElement, s.variableMap, s)
val nBytes = nBytesAsAny.asInstanceOf[Long]
val start = s.withVariables(newVMap)
(start, nBytes)
}
}
trait KnownLengthInBitsMixin[T] {
self: BinaryNumberBase[T] =>
def len: Long
def getBitLength(s: PState) = (s, len) // already in bits, so no multiply by 8 for this one.
}
trait RuntimeExplicitByteOrderMixin[T] {
self: BinaryNumberBase[T] =>
def e: ElementBase
def getByteOrder(s: PState): (PState, java.nio.ByteOrder) = {
val R(byteOrderAsAny, newVMap) = e.byteOrder.evaluate(s.parentElement, s.variableMap, s)
val dfdlByteOrderEnum = ByteOrder(byteOrderAsAny.toString, e)
val byteOrder = dfdlByteOrderEnum match {
case ByteOrder.BigEndian => java.nio.ByteOrder.BIG_ENDIAN
case ByteOrder.LittleEndian => java.nio.ByteOrder.LITTLE_ENDIAN
}
val start = s.withVariables(newVMap)
(start, byteOrder)
}
}
trait SignedNumberMixin[T] {
self: BinaryNumberBase[T] =>
def convertValue(n: BigInt, msb: Int): T = {
val signed = n.testBit(msb - 1) match { // msb is zero-based bit counting
case true => n - (BigInt(1) << msb)
case false => n
}
signed.asInstanceOf[T]
}
}
trait UnsignedNumberMixin[T] {
self: BinaryNumberBase[T] =>
def convertValue(n: BigInt, msb: Int): T = n.asInstanceOf[T]
}
// TODO: Double Conversion as a Sign-Trait
abstract class BinaryNumberBase[T](val e: ElementBase) extends Terminal(e, true) {
lazy val primName = e.primType.name
lazy val staticByteOrderString = e.byteOrder.constantAsString
lazy val staticByteOrder = ByteOrder(staticByteOrderString, context)
lazy val (staticJByteOrder, label) = staticByteOrder match {
case ByteOrder.BigEndian => (java.nio.ByteOrder.BIG_ENDIAN, "BE")
case ByteOrder.LittleEndian => (java.nio.ByteOrder.LITTLE_ENDIAN, "LE")
}
//def getNum(t: Number): BigInt
protected def getBitLength(s: PState): (PState, Long)
protected def getByteOrder(s: PState): (PState, java.nio.ByteOrder)
protected def convertValue(n: BigInt, msb: Int): T
override def toString = "binary(xs:" + primName + ", " + label + ")"
val gram = this
protected val GramName = e.primType.name
protected val GramDescription = { GramName(0).toUpper + GramName.substring(1, GramName.length) }
def parser = new PrimParser(this, e) {
override def toString = gram.toString
def parse(start0: PState): PState = {
try {
val (start1, nBits) = getBitLength(start0)
val (start, bo) = getByteOrder(start1)
val (value, newPos) = start.inStream.getBitSequence(start.bitPos, nBits, bo)
//if (GramName == "hexBinary") {
// val bytes = value.asInstanceOf[Array[Byte]]
// var asString: StringBuilder = new StringBuilder()
// for (i <- 0 until bytes.length) {
// val byte = String.format("%02X", bytes(i).asInstanceOf[java.lang.Byte])
// asString.append(byte)
// }
// start.parentElement.setDataValue(asString.toString())
//} else
val convertedValue: T = convertValue(value, nBits toInt)
start.parentElement.setDataValue(convertedValue.toString)
start.withPos(newPos, -1, None)
} catch {
case e: IndexOutOfBoundsException => { return PE(start0, "BinaryNumber - Insufficient Bits for xs:%s : IndexOutOfBounds: \n%s", primName, e.getMessage()) }
case u: UnsuppressableException => throw u
case e: Exception => { return PE(start0, "BinaryNumber - Exception: \n%s", e.getStackTraceString) }
}
}
}
def unparser = DummyUnparser
}
class UnsignedRuntimeLengthRuntimeByteOrderBinaryNumber[T](e: ElementBase) extends BinaryNumberBase[T](e)
with RuntimeExplicitLengthMixin[T] with RuntimeExplicitByteOrderMixin[T] with UnsignedNumberMixin[T] {
}
class UnsignedKnownLengthRuntimeByteOrderBinaryNumber[T](e: ElementBase, val len: Long) extends BinaryNumberBase[T](e)
with RuntimeExplicitByteOrderMixin[T] with KnownLengthInBitsMixin[T] with UnsignedNumberMixin[T] {
}
class SignedRuntimeLengthRuntimeByteOrderBinaryNumber[T](e: ElementBase) extends BinaryNumberBase[T](e)
with RuntimeExplicitLengthMixin[T] with RuntimeExplicitByteOrderMixin[T] with SignedNumberMixin[T] {
}
class SignedKnownLengthRuntimeByteOrderBinaryNumber[T](e: ElementBase, val len: Long) extends BinaryNumberBase[T](e)
with RuntimeExplicitByteOrderMixin[T] with KnownLengthInBitsMixin[T] with SignedNumberMixin[T] {
}
// Not needed. No runtime-determined lengths for binary floats.
//class FloatingPointRuntimeLengthRuntimeByteOrderBinaryNumber[T](e: ElementBase) extends BinaryNumberBase[T](e)
// with RuntimeExplicitLengthMixin[T] with RuntimeExplicitByteOrderMixin[T] with FloatingPointMixin[T] {
//}
class FloatKnownLengthRuntimeByteOrderBinaryNumber(e: ElementBase, val len: Long)
extends BinaryNumberBase[Float](e)
with RuntimeExplicitByteOrderMixin[Float]
with KnownLengthInBitsMixin[Float] {
final def convertValue(n: BigInt, ignored_msb: Int): Float = {
val nWith33rdBit = n | (BigInt(1) << 33) // make sure we have 5 bytes here. Then we'll ignore 5th byte.
val ba = nWith33rdBit.toByteArray
val bb = java.nio.ByteBuffer.wrap(ba)
val res = ba.length match {
case 5 => bb.getFloat(1)
case _ => Assert.invariantFailed("byte array should be 5 long")
}
res
}
}
class DoubleKnownLengthRuntimeByteOrderBinaryNumber(e: ElementBase, val len: Long)
extends BinaryNumberBase[Double](e)
with RuntimeExplicitByteOrderMixin[Double]
with KnownLengthInBitsMixin[Double] {
final def convertValue(n: BigInt, ignored_msb: Int): Double = {
val nWith65thBit = n | (BigInt(1) << 65) // make sure we have 9 bytes of bigint here. Then we'll ignore the 9th byte.
val ba = nWith65thBit.toByteArray
val bb = java.nio.ByteBuffer.wrap(ba)
val res = ba.length match {
case 9 => bb.getDouble(1) // ignore first byte.
case _ => Assert.invariantFailed("byte array should be 9 long")
}
res
}
}
case class PackedIntPrim(e: ElementBase) extends Primitive(e, false)
case class BCDIntPrim(e: ElementBase) extends Primitive(e, false)
//abstract class StaticDelimiter(kindString: String, delim: String, e: Term, guard: Boolean = true)
// extends StaticText(delim, e, kindString, guard)
//
//abstract class StaticText(delim: String, e: Term, kindString: String, guard: Boolean = true)
// extends Terminal(e, guard)
// with WithParseErrorThrowing with TextReader {
//
// val charset = e.knownEncodingCharset
//
// lazy val es = e.escapeScheme
// lazy val esObj = EscapeScheme.getEscapeScheme(es, e)
//
// val term = e.asInstanceOf[Term]
// lazy val staticTexts = delim.split("\\s").toList
//
// val staticTextsCooked: Queue[String] = new Queue
//
// staticTexts.foreach(x => staticTextsCooked.enqueue(EntityReplacer.replaceAll(x)))
//
// lazy val delimsRaw = e.allTerminatingMarkup.map { _.constantAsString }
// lazy val delimsCooked1 = delimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, e).cooked })
// lazy val delimsCooked = delimsCooked1.flatten
//
// // Here we expect that remoteDelims shall be defined as those delimiters who are not
// // also defined locally. That is to say that local should win over remote.
// lazy val remoteDelims = delimsCooked.toSet.diff(staticTextsCooked.toSet)
//
// // here we define the parsers so that they are pre-compiled/generated
// lazy val delimParser = new DFDLDelimParserStatic(e.knownEncodingStringBitLengthFunction)
// lazy val (pInputDelimiterParser, pIsLocalDelimParser) =
// delimParser.generateInputDelimiterParsers(staticTextsCooked.toSet, remoteDelims)
//
// def parseMethod(input: Reader[Char]): DelimParseResult = {
// val result: DelimParseResult = delimParser.parseInputDelimiter(pInputDelimiterParser, pIsLocalDelimParser, input)
// result
// }
//
// def parser: DaffodilParser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1) = {
// "<" + kindString + ">" + delim + " " + delimsRaw + "</" + kindString + ">"
// }
//
// e.schemaDefinitionWarning(e.ignoreCase == YesNo.No, "Property ignoreCase='yes' not supported.")
//
// Assert.invariant(delim != "") // shouldn't be here at all in this case.
// override def toString = kindString + "('" + delim + "')" // with terminating markup: " + term.prettyTerminatingMarkup + ")"
//
// // val delimsRaw = e.allTerminatingMarkup.map { _.constantAsString }
// // val delimsCooked1 = delimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, e).cooked })
// // val delimsCooked = delimsCooked1.flatten
// //
// // // Here we expect that remoteDelims shall be defined as those delimiters who are not
// // // also defined locally. That is to say that local should win over remote.
// // val remoteDelims = delimsCooked.toSet.diff(staticTextsCooked.toSet)
// //System.err.println("startCharPos: " + start.charPos)
//
// def parse(start: PState): PState = withParseErrorThrowing(start) {
// // withLoggingLevel(LogLevel.Debug)
// {
// val eName = e.toString()
//
// // 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 = start.variableMap
//
// val postEvalState = start.withVariables(vars)
//
// log(LogLevel.Debug, "%s - Parsing delimiter at byte position: %s", eName, (postEvalState.bitPos >> 3)))
// log(LogLevel.Debug, "%s - Parsing delimiter at bit position: %s", eName, postEvalState.bitPos))
//
// log(LogLevel.Debug, "%s - Looking for local(%s) not remote (%s).", eName, staticTextsCooked.toSet, remoteDelims))
//
// val bytePos = (postEvalState.bitPos >> 3).toInt
//
// log(LogLevel.Debug, "Retrieving reader state."))
// val reader = getReader(charset, start.bitPos, postEvalState)
//
// //val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// // Well they may not be delimiters, but the logic is the same as for a
// // set of static delimiters.
// //val result = d.parseInputDelimiter(staticTextsCooked.toSet, remoteDelims, reader)
// //val result: DelimParseResult = delimParser.parseInputDelimiter(pInputDelimiterParser, pIsLocalDelimParser, reader)
// val result = parseMethod(reader)
//
// log(LogLevel.Debug, "%s - %s - DelimParseResultult: %s", this.toString(), eName, result))
//
// result match {
// case _: DelimParseFailure => {
// log(LogLevel.Debug, "%s - %s: Delimiter not found!", this.toString(), eName))
// return PE(start, "%s - %s: Delimiter not found!", this.toString(), eName)
// }
// case s: DelimParseSuccess if (s.delimiterLoc == DelimiterLocation.Remote) => {
// log(LogLevel.Debug, "%s - %s: Remote delimiter found instead of local!", this.toString(), eName))
// return PE(start, "%s - %s: Remote delimiter found instead of local!", this.toString(), eName)
// }
// case s: DelimParseSuccess =>
// {
// val numBits = e.knownEncodingStringBitLengthFunction(s.delimiter)
// val endCharPos = if (postEvalState.charPos == -1) s.delimiter.length else postEvalState.charPos + s.delimiter.length()
// val endBitPosDelim = numBits + postEvalState.bitPos
//
// log(LogLevel.Debug, "%s - Found %s", eName, s.delimiter))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPosDelim >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position %s", eName, endBitPosDelim))
//
// return postEvalState.withPos(endBitPosDelim, endCharPos, Some(s.next))
// }
// postEvalState
// }
// }
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// val t = e.asInstanceOf[Term]
// override def toString = "StaticText('" + delim + "' with terminating markup: " + t.prettyTerminatingMarkup + ")"
// // setLoggingLevel(LogLevel.Info)
// e.schemaDefinitionWarning(e.ignoreCase == YesNo.No, "Property ignoreCase='yes' is not supported.")
// Assert.invariant(delim != "") //shouldn't be here at all in this case
//
// def unparse(start: UState): UState = {
// val encoder = e.knownEncodingCharset.newEncoder()
// start.outStream.setEncoder(encoder)
// start.outStream.fillCharBuffer(unparserDelim)
// log(LogLevel.Debug, "Unparsed: " + start.outStream.getData))
// start
// }
// }
//
// def unparserDelim: String
//}
//
//class DynamicDelimiter(delimExpr: CompiledExpression, e: Term, guard: Boolean = true) extends Primitive(e, guard)
//
////case class StaticInitiator(e: Term) extends StaticDelimiter(e.initiator.constantAsString, e)
//case class StaticInitiator(e: Term) extends StaticDelimiter("Init", e.initiator.constantAsString, e) {
// Assert.invariant(e.hasInitiator)
// lazy val unparserDelim = e.initiator.constantAsString.split("""\s""").head
//}
////case class StaticTerminator(e : Term) extends StaticDelimiter(e.terminator.constantAsString, e)
//case class StaticTerminator(e: Term) extends StaticDelimiter("Term", e.terminator.constantAsString, e) {
// Assert.invariant(e.hasTerminator)
// lazy val unparserDelim = e.terminator.constantAsString.split("""\s""").head
//}
//case class DynamicInitiator(e: Term) extends DynamicDelimiter(e.initiator, e)
//case class DynamicTerminator(e: Term) extends DynamicDelimiter(e.terminator, e)
//
//case class StaticSeparator(s: Sequence, t: Term) extends StaticDelimiter("Sep", s.separator.constantAsString, t) {
// Assert.invariant(s.hasSeparator)
// lazy val unparserDelim = s.separator.constantAsString.split("""\s""").head
//}
//case class DynamicSeparator(s: Sequence, t: Term) extends DynamicDelimiter(s.separator, t)
case class StartChildren(ct: ComplexTypeBase, guard: Boolean = true) extends Terminal(ct.element, guard) {
def parser: DaffodilParser = new PrimParser(this, ct.element) {
override def toString = "StartChildren"
def parse(start: PState): PState = {
val postState = start.withChildIndexStack(1L :: start.childIndexStack)
postState
}
}
def unparser: Unparser = new Unparser(ct.element) {
override def toString = "StartChildren"
def unparse(start: UState): UState = {
val postState = start.withChildIndexStack(1L :: start.childIndexStack)
postState
}
}
}
case class StartSequence(sq: Sequence, guard: Boolean = true) extends Terminal(sq, guard) {
def parser: DaffodilParser = new PrimParser(this, sq) {
override def toString = "StartSequence"
def parse(start: PState): PState = {
val postState = start.withGroupIndexStack(1L :: start.groupIndexStack)
postState
}
}
def unparser: Unparser = new Unparser(sq) {
override def toString = "StartSequence"
def unparse(start: UState): UState = {
val postState = start.withGroupIndexStack(1L :: start.groupIndexStack)
postState
}
}
}
case class Nada(sc: Term) extends Terminal(sc, true) {
override def isEmpty = false
// cannot optimize this out! It is used as an alternative to things
// with the intention of "find this and this, or find nothing"
def parser: DaffodilParser = new PrimParser(this, sc) {
override def toString = "Nada"
def parse(start: PState): PState = start
}
def unparser: Unparser = new Unparser(sc) {
override def toString = "Nada"
def unparse(start: UState): UState = start
}
}
case class GroupPosGreaterThan(groupPos: Long, term: Term, guard: Boolean = true) extends Terminal(term, guard) {
def parser: DaffodilParser = new PrimParser(this, term) {
override def toString = "GroupPosGreaterThan(" + groupPos + ")"
def parse(start: PState): PState = {
val res = if (start.groupPos > groupPos) {
start.withDiscriminator(true)
} else {
PE(start, "Group position not greater than (%s)", groupPos)
}
res
}
}
def unparser: Unparser = new Unparser(term) {
override def toString = "GroupPosGreaterThan(" + groupPos + ")"
def unparse(start: UState): UState = {
val res = if (start.groupPos > groupPos) {
start.withDiscriminator(true)
} else {
UE(start, "Group position not greater than (%s)", groupPos)
}
res
}
}
}
case class ChildPosGreaterThan(childPos: Long, term: Term, guard: Boolean = true) extends Terminal(term, guard) {
def parser: DaffodilParser = new PrimParser(this, term) {
override def toString = "ChildPosGreaterThan(" + childPos + ")"
def parse(start: PState): PState = {
val res = if (start.childPos > childPos) {
start.withDiscriminator(true)
} else {
PE(start, "Child position not greater than (%s)", childPos)
}
res
}
}
def unparser: Unparser = new Unparser(term) {
override def toString = "ChildPosGreaterThan(" + childPos + ")"
def unparse(start: UState): UState = {
val res = if (start.childPos > childPos) {
start.withDiscriminator(true)
} else {
UE(start, "Child position not greater than (%s)", childPos)
}
res
}
}
}
case class ArrayPosGreaterThan(arrayPos: Long, term: Term, guard: Boolean = true) extends Terminal(term, guard) {
def parser: DaffodilParser = new PrimParser(this, term) {
override def toString = "ArrayPosGreaterThan(" + arrayPos + ")"
def parse(start: PState): PState = {
val res = try {
if (start.arrayPos > arrayPos) {
start.withDiscriminator(true)
} else {
PE(start, "Array position not greater than (%s)", arrayPos)
}
} catch { case e => PE(start, "No array position") }
res
}
}
def unparser: Unparser = new Unparser(term) {
override def toString = "ArrayPosGreaterThan(" + arrayPos + ")"
def unparse(start: UState): UState = {
val res = try {
if (start.arrayPos > arrayPos) {
start.withDiscriminator(true)
} else {
UE(start, "Array position not greater than (%s)", arrayPos)
}
} catch { case e => UE(start, "No array position") }
res
}
}
}
case class EndChildren(ct: ComplexTypeBase, guard: Boolean = true) extends Terminal(ct.element, guard) {
def parser: DaffodilParser = new PrimParser(this, ct.element) {
override def toString = "EndChildren"
def parse(start: PState): PState = {
val postState = start.withChildIndexStack(start.childIndexStack.tail)
postState
}
}
def unparser: Unparser = new Unparser(ct.element) {
override def toString = "EndChildren"
def unparse(start: UState): UState = {
val postState = start.withChildIndexStack(start.childIndexStack.tail)
postState
}
}
}
case class EndSequence(sq: Sequence, guard: Boolean = true) extends Terminal(sq, guard) {
def parser: DaffodilParser = new PrimParser(this, sq) {
override def toString = "EndSequence"
def parse(start: PState): PState = {
// When we end a sequence group, we have created a group child in the parent
// so we advance that index.
val postState = start.withGroupIndexStack(start.groupIndexStack.tail).moveOverOneGroupIndexOnly
postState
}
}
def unparser: Unparser = new Unparser(sq) {
override def toString = "EndSequence"
def unparse(start: UState): UState = {
val postState = start.withGroupIndexStack(start.groupIndexStack.tail).moveOverOneGroupIndexOnly
postState
}
}
}
case class StartArray(e: ElementBase, guard: Boolean = true) extends Terminal(e, guard) {
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = "StartArray"
def parse(start: PState): PState = {
val postState1 = start.withArrayIndexStack(1L :: start.arrayIndexStack)
val postState2 = postState1.withOccursCountStack(Compiler.occursCountMax :: postState1.occursCountStack)
postState2
}
}
def unparser: Unparser = new Unparser(e) {
override def toString = "StartArray"
def unparse(start: UState): UState = {
val postState = start.withArrayIndexStack(1L :: start.arrayIndexStack)
postState
}
}
}
case class EndArray(e: ElementBase, guard: Boolean = true) extends Terminal(e, guard) {
def parser: DaffodilParser = new PrimParser(this, e) {
override def toString = "EndArray"
def parse(start: PState): PState = {
val postState1 = start.withArrayIndexStack(start.arrayIndexStack.tail)
val postState2 = postState1.withOccursCountStack(postState1.occursCountStack.tail)
postState2
}
}
def unparser: Unparser = new Unparser(e) {
override def toString = "EndArray"
def unparse(start: UState): UState = {
val postState = start.withArrayIndexStack(start.arrayIndexStack.tail)
postState
}
}
}
case class NoValue(e: GlobalElementDecl, guard: Boolean = true) extends Primitive(e, guard)
case class SaveInputStream(e: ElementBase, guard: Boolean = true) extends Primitive(e, guard)
case class SetEmptyInputStream(e: ElementBase, guard: Boolean = true) extends Primitive(e, guard)
case class RestoreInputStream(e: ElementBase, guard: Boolean = true) extends Primitive(e, guard)
//case class Value(e: SchemaComponent, guard: Boolean = true) extends Primitive(e, guard)
case class NotStopValue(e: ElementBase with LocalElementMixin) extends Primitive(e, e.hasStopValue)
case class StopValue(e: ElementBase with LocalElementMixin) extends Primitive(e, e.hasStopValue)
case class TheDefaultValue(e: ElementBase) extends Primitive(e, e.isDefaultable)
//case class LiteralNilExplicitLengthInBytes(e: ElementBase)
// extends LiteralNilInBytesBase(e, "LiteralNilExplicit") {
//
// val expr = e.length
// val exprText = expr.prettyExpr
//
// final def computeLength(start: PState) = {
// val R(nBytesAsAny, newVMap) = expr.evaluate(start.parentElement, start.variableMap, start)
// val nBytes = nBytesAsAny.toString().toLong //nBytesAsAny.asInstanceOf[Long]
// (nBytes, newVMap)
// }
//
//}
//
//case class LiteralNilKnownLengthInBytes(e: ElementBase, lengthInBytes: Long)
// extends LiteralNilInBytesBase(e, "LiteralNilKnown") {
//
// final def computeLength(start: PState) = {
// (lengthInBytes, start.variableMap)
// }
//
//}
//
//abstract class LiteralNilInBytesBase(e: ElementBase, label: String)
// extends StaticText(e.nilValue, e, label, e.isNillable)
// with Padded {
//
// protected def computeLength(start: PState): (Long, VariableMap)
//
// // We are to assume that we can always read nBytes
// // a failure to read nBytes is a failure period.
//
// lazy val unparserDelim = Assert.notYetImplemented()
//
// override def parser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1): String = {
// "<" + name + " nilValue='" + e.nilValue + "'/>"
// }
//
// val isEmptyAllowed = e.nilValue.contains("%ES;")
// val eName = e.toString()
// val nilValuesCooked = new ListOfStringValueAsLiteral(e.nilValue, e).cooked
// val charsetName = charset.name()
//
// def parse(start: PState): PState = {
// // withLoggingLevel(LogLevel.Debug)
// {
//
// // TODO: What if someone passes in nBytes = 0 for Explicit length, is this legal?
//
// val (nBytes: Long, newVMap: VariableMap) = computeLength(start)
// val postEvalState = start.withVariables(newVMap)
// log(LogLevel.Debug, "Explicit length %s", nBytes))
//
// //val postEvalState = start //start.withVariables(vars)
//
// log(LogLevel.Debug, "%s - Looking for: %s Count: %s", eName, nilValuesCooked, nilValuesCooked.length))
// val in = postEvalState.inStream
//
// 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))
//
// // some encodings aren't whole bytes
// // if (postEvalState.bitPos % 8 != 0) { return PE(postEvalState, "LiteralNilPattern - not byte aligned.") }
//
// val decoder = charset.newDecoder()
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
// try {
// val reader = in.getCharReader(charset, postEvalState.bitPos)
// val bytes = in.getBytes(postEvalState.bitPos, nBytes.toInt)
// val cb = decoder.decode(ByteBuffer.wrap(bytes))
// val result = cb.toString
// val trimmedResult = d.removePadding(result, justificationTrim, padChar)
// val endBitPos = postEvalState.bitPos + (nBytes.toInt * 8)
// val endCharPos = if (postEvalState.charPos == -1) result.length() else postEvalState.charPos + result.length()
//
// // We have a field, is it empty?
// val isFieldEmpty = trimmedResult.length == 0 //result.length() == 0
//
// if (isFieldEmpty && isEmptyAllowed) {
// // Valid!
// postEvalState.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// } else if (isFieldEmpty && !isEmptyAllowed) {
// // Fail!
// return PE(postEvalState, "%s - Empty field found but not allowed!", eName)
// } else if (d.isFieldDfdlLiteral(trimmedResult, nilValuesCooked.toSet)) {
// // Contains a nilValue, Success!
// postEvalState.parentElement.makeNil()
//
// log(LogLevel.Debug, "%s - Found %s", eName, trimmedResult))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPos >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position ", eName, endBitPos))
//
// return postEvalState.withPos(endBitPos, endCharPos, Some(reader)) // Need to advance past found nilValue
// } else {
// // Fail!
// return PE(postEvalState, "%s - Does not contain a nil literal!", eName)
// }
// } catch {
// case e: IndexOutOfBoundsException => {
// // In this case, we failed to get the bytes
// if (isEmptyAllowed) {
// // Valid!
// postEvalState.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// } else {
// return PE(postEvalState, "%s - Insufficient Bytes in field; required %s", name, nBytes)
// }
// }
// case u: UnsuppressableException => throw u
// case e: Exception => { return PE(postEvalState, "%s - Exception: \n%s", name, e.getMessage()) }
// }
// }
// }
//
// }
//
// override def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
//
//case class LiteralNilExplicitLengthInChars(e: ElementBase)
// extends StaticText(e.nilValue, e, "LiteralNilExplicit", e.isNillable)
// with Padded {
// // We are to assume that we can always read nChars
// // a failure to read nChars is a failure period.
//
// // TODO: LiteralNilExplicitLengthInChars really is a variation of LiteralNilPattern
// lazy val unparserDelim = Assert.notYetImplemented()
//
// override def parser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1): String = {
// "<" + name + " nilValue='" + e.nilValue + "'/>"
// }
//
// val isEmptyAllowed = e.nilValue.contains("%ES;")
// val eName = e.toString()
// val nilValuesCooked = new ListOfStringValueAsLiteral(e.nilValue, e).cooked
// val charsetName = charset.name()
// val expr = e.length
// val exprText = expr.prettyExpr
//
// def parse(start: PState): PState = {
// // withLoggingLevel(LogLevel.Info)
// {
//
// //val postEvalState = start //start.withVariables(vars)
//
// val R(nCharsAsAny, newVMap) = expr.evaluate(start.parentElement, start.variableMap, start)
// val nChars = nCharsAsAny.asInstanceOf[String] //nBytesAsAny.asInstanceOf[Long]
// val postEvalState = start.withVariables(newVMap)
// log(LogLevel.Debug, "Explicit length %s", nChars))
//
// val pattern = "(?s)^.{%s}".format(nChars)
//
// log(LogLevel.Debug, "%s - Looking for: %s Count: %s", eName, nilValuesCooked, nilValuesCooked.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))
//
// // Don't check this here. This can vary by encoding.
// //if (postEvalState.bitPos % 8 != 0) { return PE(start, "LiteralNilPattern - not byte aligned.") }
//
// log(LogLevel.Debug, "Retrieving reader state."))
// val reader = getReader(charset, start.bitPos, start)
//
// if (nChars == 0 && isEmptyAllowed) {
// log(LogLevel.Debug, "%s - explicit length of 0 and %ES; found as nilValue.", eName))
// postEvalState.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// }
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = d.parseInputPatterned(pattern, reader)
//
// result match {
// case _: DelimParseFailure =>
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// // We have a field, is it empty?
// val field = d.removePadding(s.field, justificationTrim, padChar)
// val isFieldEmpty = field.length() == 0
//
// if (isFieldEmpty && isEmptyAllowed) {
// // Valid!
// start.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// } else if (isFieldEmpty && !isEmptyAllowed) {
// // Fail!
// return PE(postEvalState, "%s - Empty field found but not allowed!", eName)
// } else if (d.isFieldDfdlLiteral(field, nilValuesCooked.toSet)) {
// // Contains a nilValue, Success!
// start.parentElement.makeNil()
//
// val numBits = s.numBits //e.knownEncodingStringBitLength(result.field)
// val endCharPos =
// if (postEvalState.charPos == -1) s.field.length
// else postEvalState.charPos + s.field.length
// val endBitPos = numBits + start.bitPos
//
// log(LogLevel.Debug, "%s - Found %s", eName, s.field))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPos >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position ", eName, endBitPos))
//
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next)) // Need to advance past found nilValue
// } else {
// // Fail!
// return PE(postEvalState, "%s - Does not contain a nil literal!", eName)
// }
// }
// }
// }
// }
// }
//
// override def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//
//}
//
//case class LiteralNilExplicit(e: ElementBase, nUnits: Long)
// extends StaticText(e.nilValue, e, "LiteralNilExplicit", e.isNillable)
// with Padded {
// lazy val unparserDelim = Assert.notYetImplemented()
// //val stParser = super.parser
//
// override def parser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1): String = {
// "<" + name + " nilValue='" + e.nilValue + "'/>"
// }
//
// val pattern = e.lengthPattern
//
// val isEmptyAllowed = e.nilValue.contains("%ES;")
// val eName = e.toString()
// val nilValuesCooked = new ListOfStringValueAsLiteral(e.nilValue, e).cooked
// val charsetName = charset.name()
//
// def parse(start: PState): PState = {
// // withLoggingLevel(LogLevel.Info)
// {
//
// val postEvalState = start //start.withVariables(vars)
//
// log(LogLevel.Debug, "%s - Looking for: %s Count: %s", eName, nilValuesCooked, nilValuesCooked.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))
//
// if (postEvalState.bitPos % 8 != 0) { return PE(start, "LiteralNilPattern - not byte aligned.") }
//
// log(LogLevel.Debug, "Retrieving reader state."))
// val reader = getReader(charset, start.bitPos, start)
//
// // val byteReader = in.byteReader.atPos(bytePos)
// // val reader = byteReader.charReader(decoder.charset().name())
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = d.parseInputPatterned(pattern, reader)
//
// result match {
// case _: DelimParseFailure =>
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// // We have a field, is it empty?
// val field = d.removePadding(s.field, justificationTrim, padChar)
// val isFieldEmpty = field.length() == 0
//
// if (isFieldEmpty && isEmptyAllowed) {
// // Valid!
// start.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// } else if (isFieldEmpty && !isEmptyAllowed) {
// // Fail!
// return PE(postEvalState, "%s - Empty field found but not allowed!", eName)
// } else if (d.isFieldDfdlLiteral(field, nilValuesCooked.toSet)) {
// // Contains a nilValue, Success!
// start.parentElement.makeNil()
//
// val numBits = s.numBits //e.knownEncodingStringBitLength(result.field)
// //val endCharPos = start.charPos + result.field.length()
// val endCharPos =
// if (postEvalState.charPos == -1) s.field.length
// else postEvalState.charPos + s.field.length
// val endBitPos = numBits + start.bitPos
//
// log(LogLevel.Debug, "%s - Found %s", eName, s.field))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPos >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position ", eName, endBitPos))
//
// //return postEvalState.withPos(endBitPos, endCharPos) // Need to advance past found nilValue
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next)) // Need to advance past found nilValue
// } else {
// // Fail!
// return PE(postEvalState, "%s - Does not contain a nil literal!", eName)
// }
// }
// }
// }
// }
// }
//
// override def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
//
//case class LiteralNilPattern(e: ElementBase)
// extends StaticText(e.nilValue, e, "LiteralNilPattern", e.isNillable)
// with Padded {
// lazy val unparserDelim = Assert.notYetImplemented()
// //val stParser = super.parser
//
// override def parser = new PrimParser(this, e) {
//
// override def toBriefXML(depthLimit: Int = -1): String = {
// "<" + name + " nilValue='" + e.nilValue + "'/>"
// }
//
// val pattern = e.lengthPattern
//
// val isEmptyAllowed = e.nilValue.contains("%ES;")
// val eName = e.toString()
// val nilValuesCooked = new ListOfStringValueAsLiteral(e.nilValue, e).cooked
// val charsetName = charset.name()
//
// def parse(start: PState): PState = {
// // withLoggingLevel(LogLevel.Info)
// {
//
// val postEvalState = start //start.withVariables(vars)
//
// log(LogLevel.Debug, "%s - Looking for: %s Count: %s", eName, nilValuesCooked, nilValuesCooked.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))
//
// if (postEvalState.bitPos % 8 != 0) { return PE(start, "LiteralNilPattern - not byte aligned.") }
//
// log(LogLevel.Debug, "Retrieving reader state."))
// val reader = getReader(charset, start.bitPos, start)
//
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val result = d.parseInputPatterned(pattern, reader)
//
// result match {
// case _: DelimParseFailure =>
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// // We have a field, is it empty?
// val field = d.removePadding(s.field, justificationTrim, padChar)
// val isFieldEmpty = field.length() == 0
//
// if (isFieldEmpty && isEmptyAllowed) {
// // Valid!
// start.parentElement.makeNil()
// return postEvalState // Empty, no need to advance
// } else if (isFieldEmpty && !isEmptyAllowed) {
// // Fail!
// return PE(postEvalState, "%s - Empty field found but not allowed!", eName)
// } else if (d.isFieldDfdlLiteral(field, nilValuesCooked.toSet)) {
// // Contains a nilValue, Success!
// start.parentElement.makeNil()
//
// val numBits = s.numBits //e.knownEncodingStringBitLength(result.field)
//
// val endCharPos =
// if (postEvalState.charPos == -1) s.field.length
// else postEvalState.charPos + s.field.length
// val endBitPos = numBits + start.bitPos
//
// log(LogLevel.Debug, "%s - Found %s", eName, s.field))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPos >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position ", eName, endBitPos))
//
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next)) // Need to advance past found nilValue
// } else {
// // Fail!
// return PE(postEvalState, "%s - Does not contain a nil literal!", eName)
// }
// }
// }
// }
// }
// }
//
// override def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
//
//case class LiteralNilDelimitedOrEndOfData(e: ElementBase)
// extends StaticText(e.nilValue, e, "LiteralNilDelimitedOrEndOfData", e.isNillable)
// with Padded {
// lazy val unparserDelim = Assert.notYetImplemented()
//
// val stParser = super.parser
//
// override def parser = new PrimParser(this, e) {
// override def toString = "LiteralNilDelimitedOrEndOfData(" + e.nilValue + ")"
//
// def parse(start: PState): PState = {
// // withLoggingLevel(LogLevel.Debug)
// {
// val eName = e.toString()
//
// // TODO: Why are we even doing this here? LiteralNils don't care about delimiters
// // and LiteralNils don't have expressions! I think we can get rid of this variable
// // mapping stuff.
// //
// // 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 = start.variableMap
// val delimsRaw = e.allTerminatingMarkup.map {
// x =>
// {
// val R(res, newVMap) = x.evaluate(start.parentElement, vars, start)
// vars = newVMap
// res
// }
// }
// val delimsCooked1 = delimsRaw.map(raw => { new ListOfStringValueAsLiteral(raw.toString, e).cooked })
// val delimsCooked = delimsCooked1.flatten
// //val nilValuesCooked1 = delimsRaw.map(raw => { new ListOfStringValueAsLiteral(e.nilValue, e).cooked })
// val nilValuesCooked = new ListOfStringValueAsLiteral(e.nilValue, e).cooked //nilValuesCooked1.flatten
// val postEvalState = start.withVariables(vars)
//
// 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))
//
// // Don't check alignment this way. This is encoding specific. (Some encodings not byte aligned)
// // if (postEvalState.bitPos % 8 != 0) { return PE(start, "LiteralNilDelimitedOrEndOfData - not byte aligned.") }
//
// log(LogLevel.Debug, "Retrieving reader state."))
// val reader1 = getReader(charset, start.bitPos, start)
// // val byteReader = in.byteReader.atPos(bytePos)
// // val reader = byteReader.charReader(decoder.charset().name())
//
// // 1. Parse up until terminating Markup
// // 2. Compare resultant field to nilValue(s)
// // Same, success
// // Diff, fail
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
// val result =
// if (esObj.escapeSchemeKind == EscapeSchemeKind.Block) {
// // result = d.parseInputEscapeBlock(Set.empty[String], delimsCooked.toSet, reader,
// // esObj.escapeBlockStart, esObj.escapeBlockEnd, esObj.escapeEscapeCharacter)
// d.parseInputEscapeBlock(Set.empty[String], delimsCooked.toSet, reader1,
// esObj.escapeBlockStart, esObj.escapeBlockEnd, esObj.escapeEscapeCharacter, justificationTrim, padChar)
// } else if (esObj.escapeSchemeKind == EscapeSchemeKind.Character) {
// d.parseInputEscapeCharacter(Set.empty[String], delimsCooked.toSet, reader1,
// esObj.escapeCharacter, esObj.escapeEscapeCharacter, justificationTrim, padChar)
// } else {
// d.parseInput(Set.empty[String], delimsCooked.toSet, reader1, justificationTrim, padChar)
// }
//
// result match {
// case f: DelimParseFailure =>
// return PE(postEvalState, "%s - %s - Parse failed.", this.toString(), eName)
// case s: DelimParseSuccess => {
// // We have a field, is it empty?
// val field = d.removePadding(s.field, justificationTrim, padChar)
// val isFieldEmpty = field.length() == 0 // Note: field has been stripped of padChars
// val isEmptyAllowed = e.nilValue.contains("%ES;") // TODO: move outside parser
// if (isFieldEmpty && !isEmptyAllowed) {
// // Fail!
// return PE(postEvalState, "%s - Empty field found but not allowed!", eName)
// } else if ((isFieldEmpty && isEmptyAllowed) || // Empty, but must advance past padChars if there were any.
// d.isFieldDfdlLiteral(field, nilValuesCooked.toSet)) { // Not empty, but matches.
// // Contains a nilValue, Success!
// start.parentElement.makeNil()
//
// val numBits = s.numBits
// //val endCharPos = start.charPos + result.field.length()
// val endCharPos = if (postEvalState.charPos == -1) s.numCharsRead else postEvalState.charPos + s.numCharsRead
// val endBitPos = numBits + start.bitPos
//
// log(LogLevel.Debug, "%s - Found %s", eName, s.field))
// log(LogLevel.Debug, "%s - Ended at byte position %s", eName, (endBitPos >> 3)))
// log(LogLevel.Debug, "%s - Ended at bit position ", eName, endBitPos))
//
// //return postEvalState.withPos(endBitPos, endCharPos) // Need to advance past found nilValue
// return postEvalState.withPos(endBitPos, endCharPos, Some(s.next)) // Need to advance past found nilValue
// } else {
// // Fail!
// return PE(postEvalState, "%s - Does not contain a nil literal!", eName)
// }
// }
// }
// }
// }
// }
//
// override def unparser: Unparser = new Unparser(e) {
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
//
//case class LogicalNilValue(e: ElementBase) extends Primitive(e, e.isNillable)
// As soon as you turn these on (by removing the false and putting the real guard), then schemas all need to have
// these properties in them, which is inconvenient until we have multi-file schema support and format references.
case class LeadingSkipRegion(e: Term) extends Terminal(e, e.leadingSkip > 0) {
e.schemaDefinition(e.leadingSkip < Compiler.maxSkipLength, "Property leadingSkip %s is larger than limit %s", e.leadingSkip, Compiler.maxSkipLength)
def parser: DaffodilParser = new PrimParser(this, e) {
def parse(pstate: PState) = {
val newBitPos = 8 * (pstate.bytePos + e.leadingSkip)
pstate.withPos(newBitPos, -1, None)
}
override def toString = "leadingSkip(" + e.leadingSkip + ")"
}
def unparser: Unparser = new Unparser(e) {
def unparse(ustate: UState) = {
Assert.notYetImplemented()
}
}
}
case class AlignmentFill(e: Term) extends Primitive(e, false) // e.alignment != AlignmentType.Implicit)
case class TrailingSkipRegion(e: Term) extends Primitive(e, false) // e.trailingSkip > 0)
case class PrefixLength(e: ElementBase) extends Primitive(e, e.lengthKind == LengthKind.Prefixed)
case class UnicodeByteOrderMark(e: GlobalElementDecl) extends Primitive(e, false)
case class FinalUnusedRegion(e: ElementBase) extends Primitive(e, false)
abstract class NewVariableInstanceBase(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends Terminal(decl, true) {
val (uri, localName) = XMLUtils.QName(decl.xml, stmt.ref, decl.schemaDocument)
val expName = XMLUtils.expandedQName(uri, localName)
}
case class NewVariableInstanceStart(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
def parser: DaffodilParser = new PrimParser(this, decl) {
stmt.notYetImplemented("newVariableInstance")
def parse(pstate: PState) = {
stmt.notYetImplemented("newVariableInstance")
}
}
def unparser: Unparser = Assert.notYetImplemented()
}
case class NewVariableInstanceEnd(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
def parser: DaffodilParser = new PrimParser(this, decl) {
stmt.notYetImplemented("newVariableInstance")
def parse(pstate: PState) = stmt.notYetImplemented("newVariableInstance")
}
def unparser: Unparser = Assert.notYetImplemented()
}
abstract class AssertPatternBase(decl: AnnotatedSchemaComponent, stmt: DFDLAssertionBase)
extends Terminal(decl, true)
with WithParseErrorThrowing
with TextReader {
val eName = decl.prettyName
val testPattern = stmt.testTxt
val csName = decl.knownEncodingCharset.name()
val charset = decl.knownEncodingCharset
def parser: DaffodilParser
def unparser: Unparser
}
case class AssertPatternPrim(decl: AnnotatedSchemaComponent, stmt: DFDLAssert)
extends AssertPatternBase(decl, stmt) {
val kindString = "AssertPatternPrim"
def parser: DaffodilParser = new PrimParser(this, decl) {
override def toBriefXML(depthLimit: Int = -1) = {
"<" + kindString + ">" + testPattern + "</" + kindString + ">"
}
def parse(start: PState): PState =
// withLoggingLevel(LogLevel.Info)
{
withParseErrorThrowing(start) {
val lastState = start // .withLastState
val bytePos = (lastState.bitPos >> 3).toInt
log(LogLevel.Debug, "%s - Starting at bit pos: %s", eName, lastState.bitPos)
log(LogLevel.Debug, "%s - Starting at byte pos: %s", eName, bytePos)
log(LogLevel.Debug, "%s - Looking for testPattern = %s", eName, testPattern)
if (lastState.bitPos % 8 != 0) {
return PE(lastState, "%s - not byte aligned.", eName)
}
log(LogLevel.Debug, "Retrieving reader")
val reader = getReader(charset, start.bitPos, lastState)
val d = new DelimParser(decl.knownEncodingStringBitLengthFunction)
val result = d.parseInputPatterned(testPattern, reader)
val postState = result match {
case s: DelimParseSuccess => {
val endBitPos = lastState.bitPos + s.numBits
log(LogLevel.Debug, "Assert Pattern success for testPattern %s", testPattern)
start
}
case _ => {
log(LogLevel.Debug, "Assert Pattern fail for testPattern %s", testPattern)
val diag = new AssertionFailed(decl, start, stmt.message)
start.failed(diag)
}
}
postState
}
}
}
def unparser: Unparser = new Unparser(decl) {
def unparse(start: UState): UState = {
start
}
}
}
case class DiscriminatorPatternPrim(decl: AnnotatedSchemaComponent, stmt: DFDLAssertionBase)
extends AssertPatternBase(decl, stmt) {
val kindString = "DiscriminatorPatternPrim"
def parser: DaffodilParser = new PrimParser(this, decl) {
override def toBriefXML(depthLimit: Int = -1) = {
"<" + kindString + ">" + testPattern + "</" + kindString + ">"
}
def parse(start: PState): PState =
// withLoggingLevel(LogLevel.Info)
{
withParseErrorThrowing(start) {
val lastState = start // .withLastState
val bytePos = (lastState.bitPos >> 3).toInt
log(LogLevel.Debug, "%s - Starting at bit pos: %s", eName, lastState.bitPos)
log(LogLevel.Debug, "%s - Starting at byte pos: %s", eName, bytePos)
log(LogLevel.Debug, "%s - Looking for testPattern = %s", eName, testPattern)
if (lastState.bitPos % 8 != 0) {
return PE(lastState, "%s - not byte aligned.", eName)
}
log(LogLevel.Debug, "Retrieving reader")
val reader = getReader(charset, start.bitPos, lastState)
val d = new DelimParser(decl.knownEncodingStringBitLengthFunction)
val result = d.parseInputPatterned(testPattern, reader)
// Only want to set the discriminator if it is true
// we do not want to modify it unless it's true
if (result.isSuccess) { return start.withDiscriminator(true) }
val diag = new AssertionFailed(decl, start, stmt.message)
start.failed(diag)
}
}
}
def unparser: Unparser = new Unparser(decl) {
def unparse(start: UState): UState = {
start
}
}
}
abstract class AssertBase(
decl: AnnotatedSchemaComponent,
exprTextArg: String,
msg: String,
discrim: Boolean, // are we a discriminator or not.
assertKindName: String)
extends ExpressionEvaluatorBase(decl) {
override val baseName = assertKindName
override lazy val expandedTypeName = XMLUtils.XSD_BOOLEAN
lazy val exprText = exprTextArg
def unparser = DummyUnparser
def parser: DaffodilParser = new ExpressionEvaluationParser(this, decl) {
def parse(start: PState): PState =
// withLoggingLevel(LogLevel.Info)
{
withParseErrorThrowing(start) {
log(LogLevel.Debug, "This is %s", toString)
val R(res, newVMap) = eval(start)
val testResult = res.asInstanceOf[Boolean]
val postState = start.withVariables(newVMap)
if (testResult) {
postState.withDiscriminator(discrim)
} else {
// The assertion failed. Prepare a failure message etc. in case backtracking ultimately fails from here.
val diag = new AssertionFailed(decl, postState, msg)
postState.failed(diag)
}
}
}
}
}
abstract class AssertBooleanPrimBase(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase,
discrim: Boolean, // are we a discriminator or not.
assertKindName: String) extends AssertBase(decl, stmt.testTxt, stmt.message, discrim, assertKindName)
case class AssertBooleanPrim(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase)
extends AssertBooleanPrimBase(decl, stmt, false, "assert") {
}
case class DiscriminatorBooleanPrim(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase)
extends AssertBooleanPrimBase(decl, stmt, true, "discriminator")
case class InitiatedContent(
decl: AnnotatedSchemaComponent)
extends AssertBase(decl,
"{ true() }", // always true. We're just an assertion that says an initiator was found.
"initiatedContent. This message should not be used.",
true,
"initiatedContent")
case class SetVariable(decl: AnnotatedSchemaComponent, stmt: DFDLSetVariable)
extends ExpressionEvaluatorBase(decl) {
val baseName = "SetVariable[" + stmt.localName + "]"
lazy val exprText = stmt.value
lazy val expandedTypeName = stmt.defv.extType
def parser: DaffodilParser = new SetVariableParser(this, decl, stmt)
def unparser = DummyUnparser
}
/**
* Refactored primitives that use expressions to put expression evaluation in one place.
* On this base (for the primitive), and a corresponding parser base class for the
* actual evaluation.
*
* That fixed a bug where a SDE wasn't being reported until the parser was run that
* could have been reported at compilation time.
*
* Anything being computed that involves the dsom or grammar objects or attributes of them,
* should be done in the grammar primitives, and NOT in the parser.
* This is important to insure errors are captured at compilation time and
* reported on relevant objects.
*/
abstract class ExpressionEvaluatorBase(e: AnnotatedSchemaComponent) extends Terminal(e, true) {
override def toString = baseName + "(" + exprText + ")"
def toBriefXML(depthLimit: Int = -1) = {
"<" + baseName + ">" + exprText + "</" + baseName + ">"
}
def baseName: String
def exprText: String
def expandedTypeName: String
val expressionTypeSymbol = {
// println(expandedTypeName)
e.expressionCompiler.convertTypeString(expandedTypeName)
}
val expr = e.expressionCompiler.compile(expressionTypeSymbol, exprText)
}
case class InputValueCalc(e: ElementBase)
extends ExpressionEvaluatorBase(e) {
val baseName = "InputValueCalc"
lazy val Some(exprText) = e.inputValueCalcOption
lazy val pt = e.primType
lazy val ptn = pt.name
lazy val expandedTypeName = XMLUtils.expandedQName(XMLUtils.XSD_NAMESPACE, ptn)
def parser: DaffodilParser = new IVCParser(this, e)
def unparser = DummyUnparser
}
/**
* Common parser base class for any parser that evaluates an expression.
*/
abstract class ExpressionEvaluationParser(context: ExpressionEvaluatorBase, e: AnnotatedSchemaComponent)
extends Parser(e) with WithParseErrorThrowing {
def toBriefXML(depthLimit: Int = -1) = context.toBriefXML(depthLimit)
def eval(start: PState) = {
val currentElement = start.parentElement
val R(res, newVMap) =
context.expr.evaluate(currentElement, start.variableMap, start)
// val result = res.toString // Everything in JDOM is a string!
R(res, newVMap)
}
}
class IVCParser(context: InputValueCalc, e: ElementBase)
extends ExpressionEvaluationParser(context, e) {
Assert.invariant(e.isSimpleType)
def parse(start: PState): PState =
// withLoggingLevel(LogLevel.Info)
{
withParseErrorThrowing(start) {
log(LogLevel.Debug, "This is %s", toString)
val currentElement = start.parentElement
val R(res, newVMap) = eval(start)
currentElement.setDataValue(res.toString)
val postState = start.withVariables(newVMap) // inputValueCalc consumes nothing. Just creates a value.
postState
}
}
}
class SetVariableParser(context: ExpressionEvaluatorBase, decl: AnnotatedSchemaComponent, stmt: DFDLSetVariable)
extends ExpressionEvaluationParser(context, decl) {
def parse(start: PState): PState =
// withLoggingLevel(LogLevel.Info)
{
withParseErrorThrowing(start) {
log(LogLevel.Debug, "This is %s", toString)
val R(res, newVMap) = eval(start)
val newVMap2 = newVMap.setVariable(stmt.defv.extName, res, decl)
val postState = start.withVariables(newVMap2)
postState
}
}
}
//case class StringExplicitLengthInBytes(e: ElementBase)
// extends Terminal(e, true)
// with TextReader
// with Padded
// with WithParseErrorThrowing {
// val expr = e.length
// val exprText = expr.prettyExpr
// // val maxBytes = Compiler.maxFieldContentLengthInBytes
// var cbuf: CharBuffer = CharBuffer.allocate(0) // TODO: Performance: get a char buffer from a pool.
// var cbufSize = 0
//
// val charset = e.knownEncodingCharset
//
// def parser: DaffodilParser = new PrimParser(this, e) {
// override def toString = "StringExplicitLengthInBytesParser(" + exprText + ")"
//
// def parse(pstate: PState): PState = withParseErrorThrowing(pstate) {
//
// log(LogLevel.Debug, "Parsing starting at bit position: %s", pstate.bitPos))
//
// val R(nBytesAsAny, newVMap) = expr.evaluate(pstate.parentElement, pstate.variableMap, pstate)
// val nBytes = nBytesAsAny.asInstanceOf[Long]
// val start = pstate.withVariables(newVMap)
// log(LogLevel.Debug, "Explicit length %s", nBytes))
//
// if (start.bitPos % 8 != 0) { return PE(start, "StringExplicitLengthInBytes - not byte aligned.") }
//
// val in = start.inStream
// val decoder = charset.newDecoder()
// val d = new DelimParser(e.knownEncodingStringBitLengthFunction)
//
// val bytePos = (start.bitPos >> 3).toInt
//
// try {
// 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 + (nBytes.toInt * 8)
// log(LogLevel.Debug, "Parsed: " + result))
// log(LogLevel.Debug, "Ended at bit position " + endBitPos))
// val endCharPos = start.charPos + result.length
// val currentElement = start.parentElement
// // 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(d.removePadding(result, justificationTrim, padChar))
// // FIXME: 16-bit characters and odd number of bytes - won't work.
// // Needs to skip last byte, and pass endCharPos -1 (refactor with StringFixedLengthInBytes)
// val postState = start.withPos(endBitPos, endCharPos, Some(reader))
// return postState
// } catch {
// case e: IndexOutOfBoundsException => { return PE(start, "StringExplicitLengthInBytesParser - Insufficient Bits in field: IndexOutOfBounds: \n%s", e.getMessage()) }
// case u: UnsuppressableException => throw u
// case e: Exception => { return PE(start, "StringExplicitLengthInBytesParser - Exception: \n%s", e.getStackTraceString) }
// }
// pstate
// }
// }
//
// def unparser: Unparser = new Unparser(e) {
// override def toString = "StringExplicitLengthInBytesUnparser(" + exprText + ")"
// // val encoder = e.knownEncodingEncoder
//
// def unparse(start: UState): UState = {
// Assert.notYetImplemented()
// }
// }
//}
trait TextReader extends Logging {
/**
* Readers are stored in the PState within the InStream object.
*/
def getReader(charset: Charset, bitPos: Long, state: PState): DFDLCharReader = {
// withLoggingLevel(LogLevel.Info)
{
val csName = charset.name()
log(LogLevel.Debug, "Retrieving reader at bytePos %s", bitPos >> 3)
// Do we already have a reader in the PState?
val res = state.inStream.getCharReader(charset, bitPos)
res
}
}
}
trait Padded { self: Terminal =>
var padChar = ""
lazy val eBase = self.context.asInstanceOf[ElementBase]
lazy val justificationTrim: TextJustificationType.Type = eBase.textTrimKind match {
case TextTrimKind.None => TextJustificationType.None
case TextTrimKind.PadChar if eBase.isSimpleType => {
val theJust = eBase.primType.myPrimitiveType match {
case PrimType.Int | PrimType.Byte | PrimType.Short | PrimType.Long |
PrimType.Integer | PrimType.UInt | PrimType.UByte | PrimType.UShort |
PrimType.ULong | PrimType.Double | PrimType.Float => {
padChar = eBase.textNumberPadCharacter
eBase.textNumberJustification match {
case TextNumberJustification.Left => TextJustificationType.Left
case TextNumberJustification.Right => TextJustificationType.Right
case TextNumberJustification.Center => TextJustificationType.Center
}
}
case PrimType.String => {
padChar = eBase.textStringPadCharacter
eBase.textStringJustification match {
case TextStringJustification.Left => TextJustificationType.Left
case TextStringJustification.Right => TextJustificationType.Right
case TextStringJustification.Center => TextJustificationType.Center
}
}
case PrimType.DateTime | PrimType.Date | PrimType.Time => {
padChar = eBase.textCalendarPadCharacter
eBase.textCalendarJustification match {
case TextCalendarJustification.Left => TextJustificationType.Left
case TextCalendarJustification.Right => TextJustificationType.Right
case TextCalendarJustification.Center => TextJustificationType.Center
}
}
case PrimType.Boolean => {
padChar = eBase.textBooleanPadCharacter
eBase.textBooleanJustification match {
case TextBooleanJustification.Left => TextJustificationType.Left
case TextBooleanJustification.Right => TextJustificationType.Right
case TextBooleanJustification.Center => TextJustificationType.Center
}
}
case _ => TextJustificationType.None
}
theJust
}
case _ => TextJustificationType.None
}
}