blob: 604cd34a1e6f97ed17f49ea13ebea2640da45713 [file] [log] [blame]
/* Copyright (c) 2012-2015 Tresys Technology, LLC. All rights reserved.
*
* Developed by: Tresys Technology, LLC
* http://www.tresys.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal with
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the names of Tresys Technology, nor the names of its contributors
* may be used to endorse or promote products derived from this Software
* without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
* SOFTWARE.
*/
package edu.illinois.ncsa.daffodil.processors
import edu.illinois.ncsa.daffodil.dsom._
import edu.illinois.ncsa.daffodil.grammar.Terminal
import edu.illinois.ncsa.daffodil.grammar.Gram
import edu.illinois.ncsa.daffodil.processors.{ Parser => DaffodilParser }
import edu.illinois.ncsa.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
import edu.illinois.ncsa.daffodil.util.LogLevel
import edu.illinois.ncsa.daffodil.processors.dfa.TextPaddingParser
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.LengthKind
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.EscapeKind
import edu.illinois.ncsa.daffodil.dsom.ElementBase
import edu.illinois.ncsa.daffodil.processors.parsers.HexBinaryVariableLengthInBytesParser
import edu.illinois.ncsa.daffodil.processors.unparsers.HexBinaryVariableLengthInBytesUnparser
import edu.illinois.ncsa.daffodil.processors.parsers.LiteralNilDelimitedEndOfDataParser
import edu.illinois.ncsa.daffodil.processors.parsers.StringDelimitedParser
import edu.illinois.ncsa.daffodil.processors.parsers.HexBinaryFixedLengthInBytesParser
import edu.illinois.ncsa.daffodil.processors.unparsers.HexBinaryFixedLengthInBytesUnparser
import edu.illinois.ncsa.daffodil.processors.parsers.HexBinaryDelimitedParser
import edu.illinois.ncsa.daffodil.dsom.Term
import edu.illinois.ncsa.daffodil.processors.dfa.TextDelimitedParserFactory
import edu.illinois.ncsa.daffodil.processors.parsers.OptionalInfixSepParser
import edu.illinois.ncsa.daffodil.processors.dfa.TextDelimitedParserFactory
import edu.illinois.ncsa.daffodil.processors.unparsers.LiteralNilDelimitedEndOfDataUnparser
import edu.illinois.ncsa.daffodil.processors.unparsers.StringDelimitedUnparser
import edu.illinois.ncsa.daffodil.processors.unparsers.OptionalInfixSepUnparser
import edu.illinois.ncsa.daffodil.processors.parsers.StringOfSpecifiedLengthParser
import edu.illinois.ncsa.daffodil.processors.unparsers.Unparser
import edu.illinois.ncsa.daffodil.processors.unparsers.StringOfSpecifiedLengthUnparser
import edu.illinois.ncsa.daffodil.processors.unparsers.StringLiteralForUnparser
abstract class HexBinaryLengthInBytes(e: ElementBase)
extends Terminal(e, true) {
// nothing here
}
case class HexBinaryFixedLengthInBytes(e: ElementBase, nBytes: Long)
extends HexBinaryLengthInBytes(e) {
lazy val parserName = "HexBinaryFixedLengthInBytes"
lazy val lengthText = nBytes.toString
def getLength(pstate: PState): Long = {
nBytes
}
override lazy val parser: DaffodilParser = new HexBinaryFixedLengthInBytesParser(nBytes,
e.elementRuntimeData)
override def unparser: DaffodilUnparser = new HexBinaryFixedLengthInBytesUnparser(nBytes,
e.elementRuntimeData)
}
case class HexBinaryVariableLengthInBytes(e: ElementBase)
extends HexBinaryLengthInBytes(e) {
lazy val parserName = "HexBinaryVariableLengthInBytes"
lazy val lengthText = e.length.prettyExpr
override lazy val parser: DaffodilParser = new HexBinaryVariableLengthInBytesParser(e.elementRuntimeData,
e.length)
override def unparser: DaffodilUnparser = new HexBinaryVariableLengthInBytesUnparser(e.elementRuntimeData,
e.length)
}
case class StringOfSpecifiedLength(e: ElementBase) extends Terminal(e, true) with Padded {
override lazy val parser: DaffodilParser =
new StringOfSpecifiedLengthParser(parsingPadChar,
justificationTrim,
e.elementRuntimeData)
// TODO: PERFORMANCE The unparser below currently uses the worst-case algorithm
// which is what is needed for utf-8 where we don't know the length in bytes/bits until we
// actually convert the utf-8 to bytes.
// However, it does this even if the string is ascii, i.e., known fixed width.
//
// Either here or in the grammar, we should be choosing an algorithm that
// if the encoding is known, if it is fixed width then we just calculate the fixed length in bytes/bits
// directly rather than calling a method that outputs the string to bytes in order to measure it.
override lazy val unparser: Unparser =
new StringOfSpecifiedLengthUnparser(unparsingPadChar,
justificationPad,
e.elementRuntimeData, true)
}
abstract class StringDelimited(e: ElementBase)
extends DelimParserBase(e, true)
with Padded {
// TODO: DFDL-451 - Has been placed on the backburner until we can figure out the appropriate behavior
//
// requiredEvaluations(gram.checkDelimiterDistinctness(esObj.escapeSchemeKind, optPadChar, optEscChar,
// optEscEscChar, optEscBlkStart, optEscBlkEnd, staticDelimsCooked, elemBase))
def isDelimRequired: Boolean
lazy val es = e.optionEscapeScheme
lazy val tm = e.allTerminatingMarkup
lazy val cname = toString
lazy val eName = e.toString()
lazy val hasDynamicDelims: Boolean =
e.allTerminatingMarkup.exists { case (delimValue, _, _) => !delimValue.isConstant }
lazy val leftPaddingOpt: Option[TextPaddingParser] = {
if (!parsingPadChar.isDefined) None
else Some(new TextPaddingParser(parsingPadChar.get, e.elementRuntimeData))
}
lazy val isEscapeSchemeConstant = {
if (es.isDefined) {
val scheme = es.get
val isConstant = scheme.escapeKind match {
case EscapeKind.EscapeBlock => {
(scheme.optionEscapeEscapeCharacter.isEmpty ||
scheme.optionEscapeEscapeCharacter.get.isConstant)
}
case EscapeKind.EscapeCharacter => {
(scheme.optionEscapeCharacter.isEmpty ||
scheme.optionEscapeCharacter.get.isConstant) &&
(scheme.optionEscapeEscapeCharacter.isEmpty ||
scheme.optionEscapeEscapeCharacter.get.isConstant)
}
}
isConstant
} else false
}
lazy val escapeSchemeFactory = createEscSchemeFactory
lazy val fieldFactory = createFieldFactory
lazy val parserFactory = createParserFactory
def createEscSchemeFactory: Option[EscapeSchemeFactoryBase] = {
if (es.isDefined) {
val scheme = es.get
val theScheme = {
if (isEscapeSchemeConstant) EscapeSchemeFactoryStatic(scheme.escapeScheme, context.runtimeData)
else EscapeSchemeFactoryDynamic(scheme.escapeScheme, context.runtimeData)
}
Some(theScheme)
} else None
}
def createFieldFactory: FieldFactoryBase = {
val fieldDFAFact = {
if (escapeSchemeFactory.isDefined) {
if (isEscapeSchemeConstant) FieldFactoryStatic(escapeSchemeFactory, context.runtimeData)
else FieldFactoryDynamic(escapeSchemeFactory, context.runtimeData)
} else { FieldFactoryStatic(escapeSchemeFactory, context.runtimeData) }
}
fieldDFAFact
}
def createParserFactory: TextDelimitedParserFactory = {
val theParserFact = TextDelimitedParserFactory(
justificationTrim, parsingPadChar, fieldFactory, escapeSchemeFactory, e.elementRuntimeData)
theParserFact
}
/**
* Called at compile time in static case, at runtime for dynamic case.
*/
def errorIfDelimsHaveWSPStar(delims: List[String]): Unit = {
if (delims.filter(x => x == "%WSP*;").length > 0) {
// We cannot detect this error until expressions have been evaluated!
log(LogLevel.Debug, "%s - Failed due to WSP* detected as a delimiter for lengthKind=delimited.", eName)
context.schemaDefinitionError("WSP* cannot be used as a delimiter when lengthKind=delimited.")
}
}
override def parser: DaffodilParser = new StringDelimitedParser(
e.elementRuntimeData,
justificationTrim,
parsingPadChar,
fieldFactory,
parserFactory,
isDelimRequired)
override def unparser: DaffodilUnparser = new StringDelimitedUnparser(e.elementRuntimeData, justificationPad, parsingPadChar, isDelimRequired)
}
case class StringDelimitedEndOfData(e: ElementBase)
extends StringDelimited(e) {
val isDelimRequired: Boolean = false
}
abstract class HexBinaryDelimited(e: ElementBase)
extends StringDelimited(e) {
override lazy val parser: DaffodilParser = new HexBinaryDelimitedParser(
e.elementRuntimeData,
fieldFactory,
parserFactory,
isDelimRequired)
override def unparser: DaffodilUnparser = new HexBinaryFixedLengthInBytesUnparser(
e.minLength.longValue,
e.elementRuntimeData)
}
case class HexBinaryDelimitedEndOfData(e: ElementBase)
extends HexBinaryDelimited(e) {
val isDelimRequired: Boolean = false
}
case class LiteralNilDelimitedEndOfData(eb: ElementBase)
extends StringDelimited(eb) {
lazy val isDelimRequired: Boolean = false
override lazy val parser: DaffodilParser =
new LiteralNilDelimitedEndOfDataParser(
eb.elementRuntimeData,
justificationTrim,
parsingPadChar,
fieldFactory,
parserFactory,
eb.cookedNilValuesForParse,
eb.rawNilValuesForParse)
private lazy val slNilValue =
StringLiteralForUnparser(eb.elementRuntimeData, eb.outputNewLine, eb.rawNilValuesForUnparse.head)
override lazy val unparser: DaffodilUnparser =
new LiteralNilDelimitedEndOfDataUnparser(eb.elementRuntimeData, slNilValue, justificationPad, parsingPadChar, isDelimRequired)
}
case class PrefixLength(e: ElementBase) extends UnimplementedPrimitive(e, e.lengthKind == LengthKind.Prefixed)
class OptionalInfixSep(term: Term, sep: => Gram, guard: Boolean = true) extends Terminal(term, guard) {
def parser: DaffodilParser = new OptionalInfixSepParser(term.runtimeData, sep.parser)
override def unparser: DaffodilUnparser = new OptionalInfixSepUnparser(term.runtimeData, sep.unparser)
}