blob: bdb882c69076f920e7453d02c6ad1dabb965a062 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.daffodil.processors
import scala.xml.NamespaceBinding
import org.apache.daffodil.Implicits.ImplicitsSuppressUnusedImportWarning
import org.apache.daffodil.dpath.NodeInfo
import org.apache.daffodil.dpath.NodeInfo.PrimType
import org.apache.daffodil.dsom.CompiledExpression
import org.apache.daffodil.dsom.DPathCompileInfo
import org.apache.daffodil.dsom.DPathElementCompileInfo
import org.apache.daffodil.dsom.FacetTypes
import org.apache.daffodil.dsom.ImplementsThrowsSDE
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.exceptions.HasSchemaFileLocation
import org.apache.daffodil.exceptions.SchemaFileLocation
import org.apache.daffodil.exceptions.ThrowsSDE
import org.apache.daffodil.infoset.PartialNextElementResolver
import org.apache.daffodil.schema.annotation.props.gen.BitOrder
import org.apache.daffodil.schema.annotation.props.gen.Representation
import org.apache.daffodil.schema.annotation.props.gen.YesNo
import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
import org.apache.daffodil.util.Delay
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe.Nope
import org.apache.daffodil.xml.GlobalQName
import org.apache.daffodil.xml.LocalDeclQName
import org.apache.daffodil.xml.NS
import org.apache.daffodil.xml.NamedQName
import org.apache.daffodil.xml.QNameBase
import org.apache.daffodil.xml.RefQName
import org.apache.daffodil.xml.StepQName
import org.apache.daffodil.xml.XMLUtils
import scala.util.matching.Regex; object NoWarn { ImplicitsSuppressUnusedImportWarning() }
import java.util.regex.Matcher
import org.apache.daffodil.api.UnqualifiedPathStepPolicy
import org.apache.daffodil.infoset.DISimple
import org.apache.daffodil.infoset.DataValue
import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveOrUseNilForDefaultOrNull
import org.apache.daffodil.processors.unparsers.UnparseError
import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
import org.apache.daffodil.util.Misc
import org.apache.daffodil.util.OKOrError
/*
* NOTE: Any time you add a member to one of these objects, you must modify at least 3 places.
*
* 1) the argument list
* 2) the lazy vals
* 3) the pre-serialization method
*
* If however that member is passed to the constructor of a parent class, then steps 2 and 3
* are only needed at the top of the class hierarchy. The other classes can just pass the argument
* to the parent constructor.
*/
sealed trait RuntimeData
extends ImplementsThrowsSDE
with HasSchemaFileLocation
with Serializable {
def schemaFileLocation: SchemaFileLocation
def diagnosticDebugName: String
def path: String
def variableMap: VariableMap
override def toString = diagnosticDebugName
def unqualifiedPathStepPolicy: UnqualifiedPathStepPolicy
def namespaces: NamespaceBinding
}
object TermRuntimeData {
private var nextID = 0
def generateTermID: Int = synchronized {
val n = nextID
nextID += 1
n
}
}
/**
* Base runtime data structure for all terms
*
* These Delay-type args are part of how we
* create a structure here which contains some objects that
* somewhere within themselves, refer back to this structure.
*
* A Delay object is part of a functional programming idiom for creating
* a cyclic structure without using side-effects and therefore ordering-dependency.
* It is pretty similar to just passing an argument by-name (lazily) but
* has some additional sophistication to avoid dragging Scala closures
* around and ending up with objects being not garbage collectable.
*
* The relationhip between RuntimeData and PartialNextElementResolver is
* indeed cyclic. Simplest to think of it in terms of elements.
* An ERD has a partialNextElementResolver which exists to figure out
* the ERD of the next element, which could be this same ERD again, or
* could be some other ERD which can then be followed by an element with
* this same ERD again. So the cycle can be immediate or flow through
* many ERDs and partialNextElementResolvers. Turns out other kinds of
* terms can affect the next element resolver process (there's a stack
* of them) so partialNextElementResolver ends up on the TermRuntimeData
* to share the definition of the slot for this polymorphically.
*/
sealed abstract class TermRuntimeData(
val position: Int,
partialNextElementResolverDelay: Delay[PartialNextElementResolver],
val encodingInfo: EncodingRuntimeData,
val dpathCompileInfo: DPathCompileInfo,
val isRepresented: Boolean,
val couldHaveText: Boolean,
val alignmentValueInBits: Int, // depends ultimately on EncodingEv.isConstant
val hasNoSkipRegions: Boolean,
val defaultBitOrder: BitOrder,
val optIgnoreCase: Option[YesNo],
val fillByteEv: FillByteEv,
val maybeCheckByteAndBitOrderEv: Maybe[CheckByteAndBitOrderEv],
val maybeCheckBitOrderAndCharsetEv: Maybe[CheckBitOrderAndCharsetEv])
extends RuntimeData {
/**
* Cyclic structures require initialization
*/
lazy val initialize: Unit = initializeFunction
protected def initializeFunction: Unit = {
partialNextElementResolver
dpathCompileInfo.initialize
}
final def namespaces = dpathCompileInfo.namespaces
private val termID = TermRuntimeData.generateTermID
final override def hashCode(): Int = termID
final override def equals(other: Any) = other match {
case ref: AnyRef => this eq ref
case _ => false
}
def isRequiredScalar: Boolean
def isArray: Boolean
/**
* At some point TermRuntimeData is a ResolvesQNames which requires tunables:
*
* Anything to do with expressions might deal with namespace prefixes on
* qnames in expressions, or on NCNames in expressions, so has to have
* the namespace default policy tunable so as to be able to be compatible
* with IBM's implementation of DPath.
*
* TermRuntimeData refers to a DPathCompileInfo which has the namespace
* prefix, element name, etc. that are needed in order to compile DPath
* expressions at runtime (in the debugger, which is part of the runtime;
* hence, need this info at runtime.)
*/
def unqualifiedPathStepPolicy: UnqualifiedPathStepPolicy = dpathCompileInfo.unqualifiedPathStepPolicy
lazy val partialNextElementResolver =
partialNextElementResolverDelay.value
}
sealed class NonTermRuntimeData(
override val variableMap: VariableMap,
val schemaFileLocation: SchemaFileLocation,
val diagnosticDebugName: String,
val path: String,
override val namespaces: NamespaceBinding,
val unqualifiedPathStepPolicy: UnqualifiedPathStepPolicy)
extends RuntimeData
/**
* Singleton. If found as the default value, means to use nil as
* the default value instead of an actual value.
*/
object UseNilForDefault
final class SimpleTypeRuntimeData(
variableMapArg: VariableMap,
schemaFileLocationArg: SchemaFileLocation,
diagnosticDebugNameArg: String,
pathArg: String,
namespacesArg: NamespaceBinding,
val primType: NodeInfo.PrimType,
val noFacetChecks: Boolean,
val patternValues: Seq[FacetTypes.FacetValueR],
val enumerationValues: Option[String],
val minLength: Option[java.math.BigDecimal],
val maxLength: Option[java.math.BigDecimal],
val minInclusive: Option[java.math.BigDecimal],
val maxInclusive: Option[java.math.BigDecimal],
val minExclusive: Option[java.math.BigDecimal],
val maxExclusive: Option[java.math.BigDecimal],
val totalDigits: Option[java.math.BigDecimal],
val fractionDigits: Option[java.math.BigDecimal],
val unionMemberTypes: Seq[SimpleTypeRuntimeData],
unqualifiedPathStepPolicyArg: UnqualifiedPathStepPolicy,
val repTypeRuntimeData: Option[SimpleTypeRuntimeData],
val repValueSet: Option[RepValueSet],
val typeCalculator: Option[TypeCalculator],
val optRepPrimType: Option[PrimType])
extends NonTermRuntimeData(variableMapArg, schemaFileLocationArg, diagnosticDebugNameArg,
pathArg, namespacesArg, unqualifiedPathStepPolicyArg) {
import org.apache.daffodil.util.OKOrError._
/**
* These are creators of regex pattern matcher objects. we want to avoid
* allocating Matchers, so this is thread-safe in terms of allocating and returning the
* matchers, and we need this generally because matchers are stateful so cannot
* be shared across threads.
*/
def matchers: (Seq[Matcher], Option[Matcher]) = matcherTL.get()
private lazy val matcherTL = new ThreadLocal[(Seq[Matcher], Option[Matcher])] {
protected final override def initialValue() = {
val patternMatchers = patternValues.map { case (_, r) => r.pattern.matcher("") }
val optEnumMatcher = enumerationValues.map { en => en.r.pattern.matcher("") }
(patternMatchers, optEnumMatcher)
}
}
/**
* This recursively walks down a tree of restriction/union types.
*
* For each restriction, it checks facets. For each union, it stops at the
* first things satisfied, and sets the unionMember in the infoset element,
* which provides DFDL's unionMemberSchema functionality.
*
* Note that we don't pre-flatten unions into flat lists. We could but
* since a union can be a restriction derived from a union, and within that
* union can be other restrictions derived from other unions, we'd have to
* massage around the facet checking (push-down) to the "leaf level" types
* of the union, and that would result in them being checked redundantly, and
* potentially make diagnostics harder because the transformed type wouldn't
* match the written DFDL schema's structure.
*
* Better to just walk the nest recursively.
*/
def executeCheck(currentElement: DISimple): OKOrError = {
if (currentElement.isNilled) OK
else {
val facetResult = if (noFacetChecks) OK else executeFacetCheck(currentElement)
if (facetResult.isError) facetResult
else {
val umts = unionMemberTypes
if (umts.length == 0) OK
else {
val optUnionMemberRuntimeData = umts.find { mt =>
mt.executeCheck(currentElement).isOK
}
if (optUnionMemberRuntimeData.isDefined) {
if (currentElement.unionMemberRuntimeData.isEmpty) {
currentElement.setUnionMemberRuntimeData(optUnionMemberRuntimeData.get)
}
OK
} else
Error("Value '%s' is not one of the union members: %s".format(
currentElement.dataValueAsString,
umts.map { umt => umt.diagnosticDebugName }.mkString(", ")))
}
}
}
}
/**
* Performs the constraint checks using information contained within the
* PState object.
*
* @param pstate the current parser state.
*
* @return a Unit on success, String (message) on failure.
*/
def executeFacetCheck(currentElement: DISimple): OKOrError = {
Assert.usage(!noFacetChecks)
Assert.invariant(!currentElement.isNilled)
val e = this
lazy val (patternMatchers, optEnumMatcher) = this.matchers
if (e.patternValues.isDefinedAt(0)) {
val check = checkPatterns(currentElement, patternMatchers)
if (!check) {
// The escaping is important here as error messages were impossible to figure out when control chars were involved.
val patternStrings = e.patternValues.map { case (_, r: Regex) => XMLUtils.escape(r.pattern.pattern()) }.mkString(",")
return Error("facet pattern(s): %s".format(patternStrings))
}
}
if (e.enumerationValues.isDefined) {
val check = checkEnumerations(currentElement, optEnumMatcher.get)
if (!check) {
return Error("facet enumeration(s): %s".format(e.enumerationValues.mkString(",")))
}
}
// Check minLength
e.minLength.foreach { minLength =>
val minAsLong = minLength.longValue()
val isMinLengthGreaterThanEqToZero = minAsLong.compareTo(0L) >= 0
if (isMinLengthGreaterThanEqToZero) {
if (!checkMinLength(currentElement, minLength, e, primType))
return Error("facet minLength (%s)".format(minLength))
}
}
// Check maxLength
e.maxLength.foreach { maxLength =>
val maxAsLong = maxLength.longValue()
val isMaxLengthGreaterThanEqToZero = maxAsLong.compareTo(0L) >= 0
if (isMaxLengthGreaterThanEqToZero) {
if (!checkMaxLength(currentElement, maxLength, e, primType))
return Error("facet maxLength (%s)".format(maxLength))
}
}
// Check minInclusive
e.minInclusive.foreach { minInclusive =>
if (!checkMinInc(currentElement, minInclusive, primType, e))
return Error("facet minInclusive (%s)".format(minInclusive))
}
// Check maxInclusive
e.maxInclusive.foreach { maxInclusive =>
if (!checkMaxInc(currentElement, maxInclusive, primType, e))
return Error("facet maxInclusive (%s)".format(maxInclusive))
}
// Check minExclusive
e.minExclusive.foreach { minExclusive =>
if (!checkMinExc(currentElement, minExclusive, primType, e))
return Error("facet minExclusive (%s)".format(minExclusive))
}
// Check maxExclusive
e.maxExclusive.foreach { maxExclusive =>
if (!checkMaxExc(currentElement, maxExclusive, primType, e))
return Error("facet maxExclusive (%s)".format(maxExclusive))
}
// Check totalDigits
e.totalDigits.foreach { totalDigits =>
val tdLong = totalDigits.longValue()
val isTotalDigitsGreaterThanEqToZero = tdLong.compareTo(0L) >= 0
if (isTotalDigitsGreaterThanEqToZero) {
if (!checkTotalDigits(currentElement, tdLong))
return Error("facet totalDigits (%s)".format(totalDigits))
}
}
// Check fractionDigits
e.fractionDigits.foreach { fractionDigits =>
val fdLong = fractionDigits.longValue()
val isFractionDigitsGreaterThanEqToZero = fdLong.compareTo(0L) >= 0
if (isFractionDigitsGreaterThanEqToZero) {
if (!checkFractionDigits(currentElement, fdLong))
return Error("facet fractionDigits (%s)".format(fractionDigits))
}
}
// Note: dont check occurs counts // if(!checkMinMaxOccurs(e, pstate.arrayPos)) { return java.lang.Boolean.FALSE }
OK
}
private def checkMinLength(diNode: DISimple, minValue: java.math.BigDecimal,
e: ThrowsSDE, primType: PrimType): java.lang.Boolean = {
val minAsLong = minValue.longValueExact()
primType match {
case PrimType.String => {
val data = diNode.dataValue.getString
val dataLen = data.length.toLong
val isDataLengthLess = dataLen.compareTo(minAsLong) < 0
if (isDataLengthLess) java.lang.Boolean.FALSE
else java.lang.Boolean.TRUE
}
case PrimType.HexBinary => {
val data = diNode.dataValue.getByteArray
val dataLen = data.length.toLong
val isDataLengthEqual = dataLen.compareTo(minAsLong) == 0
if (isDataLengthEqual) java.lang.Boolean.TRUE
else java.lang.Boolean.FALSE
}
case _ => e.SDE("MinLength facet is only valid for string and hexBinary.")
}
}
private def checkMaxLength(diNode: DISimple, maxValue: java.math.BigDecimal,
e: ThrowsSDE, primType: PrimType): java.lang.Boolean = {
val maxAsLong = maxValue.longValueExact()
primType match {
case PrimType.String => {
val data: String = diNode.dataValue.getString
val dataLen: Long = data.length.toLong
val isDataLengthGreater = dataLen.compareTo(maxAsLong) > 0
if (isDataLengthGreater) java.lang.Boolean.FALSE
else java.lang.Boolean.TRUE
}
case PrimType.HexBinary => {
val data: Array[Byte] = diNode.dataValue.getByteArray
// Has to come through as a string in infoset
// hex string is exactly twice as long as number of bytes
// take length / 2 = length
val dataLen = data.length.toLong
val isDataLengthEqual = dataLen.compareTo(maxAsLong) == 0
if (isDataLengthEqual) java.lang.Boolean.TRUE
else java.lang.Boolean.FALSE
}
case _ => e.SDE("MaxLength facet is only valid for string and hexBinary.")
}
}
private def checkMinInc(diNode: DISimple, minValue: java.math.BigDecimal, primType: PrimType, e: ThrowsSDE): Boolean = {
val bdData = diNode.dataValueAsBigDecimal
val isDataGreaterThanEqToMinInc = bdData.compareTo(minValue) >= 0
isDataGreaterThanEqToMinInc
}
private def checkMinExc(diNode: DISimple, minValue: java.math.BigDecimal, primType: PrimType, e: ThrowsSDE): Boolean = {
val bdData = diNode.dataValueAsBigDecimal
val isDataGreaterThanEqToMinExc = bdData.compareTo(minValue) > 0
isDataGreaterThanEqToMinExc
}
private def checkMaxInc(diNode: DISimple, maxValue: java.math.BigDecimal, primType: PrimType, e: ThrowsSDE): Boolean = {
val bdData = diNode.dataValueAsBigDecimal
val isDataLessThanEqToMaxInc = bdData.compareTo(maxValue) <= 0
isDataLessThanEqToMaxInc
}
private def checkMaxExc(diNode: DISimple, maxValue: java.math.BigDecimal, primType: PrimType, e: ThrowsSDE): Boolean = {
val bdData = diNode.dataValueAsBigDecimal
val isDataLessThanMaxExc = bdData.compareTo(maxValue) < 0
isDataLessThanMaxExc
}
private def checkTotalDigits(diNode: DISimple, digits: Long): Boolean = {
// Per http://www.w3.org/TR/xmlschema-2/#rf-totalDigits
// |i| < 10^totalDigits
val bd = diNode.dataValueAsBigDecimal.stripTrailingZeros
val totalDigits =
if (bd.scale <= 0) bd.precision - bd.scale
else Math.max(bd.precision, bd.scale)
totalDigits <= digits
}
private def checkFractionDigits(diNode: DISimple, digits: Long): Boolean = {
val bdData = diNode.dataValueAsBigDecimal
// Rounding HALF_DOWN prevents us from accidentally increasing the value.
val rounded = bdData.setScale(digits.intValue(), java.math.RoundingMode.HALF_DOWN)
val isDataSameAsRounded = bdData.compareTo(rounded) == 0
isDataSameAsRounded
}
private def checkEnumerations(diNode: DISimple, enumMatcher: Matcher): Boolean = {
val data = diNode.dataValueAsString
enumMatcher.reset(data)
enumMatcher.matches()
}
private def checkPatterns(diNode: DISimple, matchers: Seq[Matcher]): Boolean = {
val data = diNode.dataValueAsString
var isSuccess: Boolean = true
var i: Int = 0
var done = false
val n = matchers.length
while (!done && i < n) {
val m = matchers(i)
i += 1
// each pattern within simpleType is OR'd
// each pattern between simpleType's is AND'd
// Each elem represents a simpleType
// each simpleType is allowed a facetPattern
// each facetPattern represents all patterns on this particular
// simpleType.
//
// Ex.
// <SimpleType name="A">
// <restriction base="B">
// <pattern value="1"/>
// <pattern value="2"/>
// </restriction>
// </SimpleType>
//
// <SimpleType name="B">
// <restriction base="int">
// <pattern value="3"/>
// <pattern value="4"/>
// </restriction>
// </SimpleType>
//
// Here facetPattern for SimpleType-A = "1|2" (OR'd)
// All patterns between simpleTypes must match (AND'd)
m.reset(data)
if (!m.matches()) {
isSuccess = false
done = true
}
}
isSuccess
}
}
/** Primary Runtime data structure for Elements
*
* These objects are for things that are generally heavily used everywhere like information for
* providing diagnostics.
*
* These Delay-type args are part of how we
* create a structure here which contains some objects that
* somewhere within themselves, refer back to this structure.
*
* These structures are inherently cyclic, particularly for ElementRuntimeData (ERD)
* the PartialNextElementResolver,
* which is about figuring out the next ERD given incoming name+namespace
* and context. Hence it by its very nature contains and returns ERDs and
* so involves cycles with the ERD structure.
*
* To construct this cyclic data structure but still be using functional
* programming, we use these Delay/lazy evaluation tricks.
*/
sealed class ElementRuntimeData(
positionArg: Int,
children: Seq[ElementRuntimeData],
val variableMap: VariableMap,
partialNextElementResolverDelay: Delay[PartialNextElementResolver],
val encInfo: EncodingRuntimeData,
val dpathElementCompileInfo: DPathElementCompileInfo,
val schemaFileLocation: SchemaFileLocation,
val diagnosticDebugName: String,
val path: String,
val minimizedScope: NamespaceBinding,
defaultBitOrderArg: BitOrder,
val optPrimType: Option[PrimType],
val targetNamespace: NS,
val optSimpleTypeRuntimeData: Option[SimpleTypeRuntimeData],
val optComplexTypeModelGroupRuntimeData: Option[ModelGroupRuntimeData],
val minOccurs: Long,
val maxOccurs: Long,
val maybeOccursCountKind: Maybe[OccursCountKind],
val name: String,
val targetNamespacePrefix: String,
val isNillable: Boolean,
val isArray: Boolean, // can have more than 1 occurrence
val isOptional: Boolean, // can have only 0 or 1 occurrence
val isRequiredInUnparseInfoset: Boolean, // must have at least 1 occurrence
/**
* This is the properly qualified name for recognizing this
* element.
*
* This takes into account xs:schema's elementFormDefault attribute.
* If 'qualified' then there will be a namespace component.
* If 'unqualified' the the namespace component will be No_Namespace.
*/
val namedQName: NamedQName,
isRepresentedArg: Boolean,
couldHaveTextArg: Boolean,
alignmentValueInBitsArg: Int,
hasNoSkipRegionsArg: Boolean,
val impliedRepresentation: Representation,
optIgnoreCaseArg: Option[YesNo],
val optDefaultValue: DataValuePrimitiveOrUseNilForDefaultOrNull,
//
// Unparser-specific arguments
//
val optTruncateSpecifiedLengthString: Option[Boolean],
val outputValueCalcExpr: Option[CompiledExpression[AnyRef]],
val maybeBinaryFloatRepEv: Maybe[BinaryFloatRepEv],
val maybeByteOrderEv: Maybe[ByteOrderEv],
fillByteEvArg: FillByteEv,
maybeCheckByteAndBitOrderEvArg: Maybe[CheckByteAndBitOrderEv],
maybeCheckBitOrderAndCharsetEvArg: Maybe[CheckBitOrderAndCharsetEv],
val isQuasiElement: Boolean)
extends TermRuntimeData(positionArg, partialNextElementResolverDelay,
encInfo, dpathElementCompileInfo, isRepresentedArg, couldHaveTextArg, alignmentValueInBitsArg, hasNoSkipRegionsArg,
defaultBitOrderArg, optIgnoreCaseArg, fillByteEvArg,
maybeCheckByteAndBitOrderEvArg,
maybeCheckBitOrderAndCharsetEvArg) {
override def isRequiredScalar = !isArray && isRequiredInUnparseInfoset
final def childERDs = children
def isSimpleType = optPrimType.isDefined
lazy val schemaURIStringsForFullValidation: Seq[String] = schemaURIStringsForFullValidation1.distinct
private def schemaURIStringsForFullValidation1: Seq[String] = (schemaFileLocation.uriString +:
childERDs.flatMap { _.schemaURIStringsForFullValidation1 })
def isComplexType = !isSimpleType
def prefix = this.minimizedScope.getPrefix(namedQName.namespace)
def prefixedName = {
if (prefix != null) {
prefix + ":" + name
} else {
name
}
}
}
/**
* Used when unparsing to indicate that an expected ERD could not be created.
*
* Subclasses of this are for specific reasons why.
*
* The purposes of this is to allow the InfosetInputter to actually construct an
* infoset event including a full infoset DIElement node, yet indicating that the
* event is invalid.
*
* This enables improved diagnostic behavior. For example, a StartElement event
* can be created for a name and namespace where that name + namespace are not
* expected, the resulting infoset event will contain a DIElement having an
* ErrorERD. The unparser can then inspect the infoset event, and
* if it is expecting an EndElement event, it can issue a UnparseError that
* correctly identifies that an expected EndElement event was not received.
*
* In all cases, there is no recovering from these errors in the Unparser; hence,
* we don't really care if these bogus DIElement nodes having these Error ERDs are
* spliced into the infoset or not.
*/
sealed abstract class ErrorERD(local: String, namespaceURI: String)
extends ElementRuntimeData(
0, // position
Nil, // children
null, // VariableMap
null, // PartialNextElementResolver
null, // EncodingRuntimeData
new DPathElementCompileInfo(
Delay('ErrorERDParents, getClass().getName, Seq[DPathElementCompileInfo]()).force, // parentsArg: => Seq[DPathElementCompileInfo],
null, // variableMap: => VariableMap,
Delay('ErrorERD, getClass().getName, Seq[DPathElementCompileInfo]()).force, // elementChildrenCompileInfoDelay: Delay[Seq[DPathElementCompileInfo]],
null, // namespaces: scala.xml.NamespaceBinding,
local, // path: String,
local, // val name: String,
false, // val isArray: Boolean,
LocalDeclQName(None, local, NS(namespaceURI)), // val namedQName: NamedQName,
None, // val optPrimType: Option[PrimType],
null, // sfl: SchemaFileLocation,
null, // override val unqualifiedPathStepPolicy : UnqualifiedPathStepPolicy,
null, // typeCalcMap: TypeCalcMap,
null, // val sscd: String),
false), // val hasOutputValueCalc: Boolean
null, // SchemaFileLocation
local, // diagnosticDebugName: String,
local, // pathArg: => String,
null, // minimizedScopeArg: => NamespaceBinding,
null, //defaultBitOrderArg: => BitOrder,
None, // optPrimTypeArg: => Option[PrimType],
null, // targetNamespaceArg: => NS,
null, // optSimpleTypeRuntimeDataArg: => Option[SimpleTypeRuntimeData],
null, // optComplexTypeModelGroupRuntimeDataArg: => Option[ModelGroupRuntimeData],
0L, // minOccursArg: => Long,
0L, // maxOccursArg: => Long,
Nope, // maybeOccursCountKindArg: => Maybe[OccursCountKind],
local, // nameArg: => String,
null, // targetNamespacePrefixArg: => String,
false, // isNillableArg: => Boolean,
false, // isArrayArg: => Boolean, // can have more than 1 occurrence
false, // isOptionalArg: => Boolean, // can have only 0 or 1 occurrence
false, // isRequiredInUnparseInfosetArg: => Boolean, // must have at least 1 occurrence
LocalDeclQName(None, local, NS(namespaceURI)), // namedQNameArg: => NamedQName,
false, // isRepresentedArg: => Boolean,
false, // couldHaveTextArg: => Boolean,
0, // alignmentValueInBitsArg: => Int,
false, // hasNoSkipRegionsArg: => Boolean,
null, // impliedRepresentationArg: => Representation,
null, // optIgnoreCaseArg: => Option[YesNo],
DataValue.NoValue, // optDefaultValueArg: => DataValuePrimitiveOrUseNilForDefaultOrNull,
null, // optTruncateSpecifiedLengthStringArg: => Option[Boolean],
null, // outputValueCalcExprArg: => Option[CompiledExpression[AnyRef]],
Nope, // maybeBinaryFloatRepEvArg: => Maybe[BinaryFloatRepEv],
Nope, // maybeByteOrderEvArg: => Maybe[ByteOrderEv],
null, // fillByteEvArg => FillByteEv
Nope, // maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv],
Nope, // maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv],
false // isQuasiElementArg: => Boolean
) {
override def toString() = Misc.getNameFromClass(this) + "(" + this.namedQName.toExtendedSyntax + ")"
}
/**
* Used when unparsing to indicate that a next element event was detected that is
* unexpected.
*/
final class UnexpectedElementErrorERD(
optTRD: Option[TermRuntimeData],
local: String,
namespaceURI: String,
val allPossibleNQNs: Seq[QNameBase])
extends ErrorERD(local, namespaceURI) {
}
/**
* Used when unparsing to indicate that multiple elements could be next that
* differ only by namespace. This means for some event sources (like JSON) we
* don't know which element to create.
*/
final class NamespaceAmbiguousElementErrorERD(
optTRD: Option[TermRuntimeData],
local: String,
namespaceURI: String,
val allPossibleNQNs: Seq[QNameBase])
extends ErrorERD(local, namespaceURI) {
/**
* Causes unparse error with diagnostic about unexpected element.
*
* Pass argument true if the context is one where no element was expected
* (e.g., because an EndElement was expected.)
*
* Pass false if the Term's ordinary nextElementResolver list of possibilities
* is what was expected.
*/
def toUnparseError(nothingWasExpected: Boolean = false) = {
val sqn = StepQName(None, name, namedQName.namespace)
val sqnx = sqn.toExtendedSyntax
val allPossiblesString =
allPossibleNQNs.map { _.toExtendedSyntax }.mkString(", ")
val maybeLoc: Maybe[SchemaFileLocation] = Maybe.toMaybe(optTRD.map { _.schemaFileLocation })
UnparseError(maybeLoc, Nope,
"Found multiple matches for element %s because infoset implementation ignores namespaces. Matches are %s",
sqnx, allPossiblesString)
}
}
/**
* Base class for model group runtime data
*
* These Delay-type args are part of how we
* create a structure here which contains some objects that
* somewhere within themselves, refer back to this structure.
*/
sealed abstract class ModelGroupRuntimeData(
positionArg: Int,
partialNextElementResolverDelay: Delay[PartialNextElementResolver],
val variableMap: VariableMap,
val encInfo: EncodingRuntimeData,
val schemaFileLocation: SchemaFileLocation,
ci: DPathCompileInfo,
val diagnosticDebugName: String,
val path: String,
defaultBitOrderArg: BitOrder,
val groupMembers: Seq[TermRuntimeData],
isRepresentedArg: Boolean,
couldHaveText: Boolean,
alignmentValueInBitsArg: Int,
hasNoSkipRegionsArg: Boolean,
optIgnoreCaseArg: Option[YesNo],
fillByteEvArg: FillByteEv,
maybeCheckByteAndBitOrderEvArg: Maybe[CheckByteAndBitOrderEv],
maybeCheckBitOrderAndCharsetEvArg: Maybe[CheckBitOrderAndCharsetEv])
extends TermRuntimeData(
positionArg, partialNextElementResolverDelay,
encInfo, ci, isRepresentedArg, couldHaveText, alignmentValueInBitsArg, hasNoSkipRegionsArg,
defaultBitOrderArg, optIgnoreCaseArg, fillByteEvArg,
maybeCheckByteAndBitOrderEvArg,
maybeCheckBitOrderAndCharsetEvArg) {
final override def isRequiredScalar = true
final override def isArray = false
}
/**
* These Delay-type args are part of how we
* create a structure here which contains some objects that
* somewhere within themselves, refer back to this structure.
*/
final class SequenceRuntimeData(
positionArg: Int,
partialNextElementResolverDelay: Delay[PartialNextElementResolver],
variableMapArg: VariableMap,
encInfo: EncodingRuntimeData,
schemaFileLocationArg: SchemaFileLocation,
ci: DPathCompileInfo,
diagnosticDebugNameArg: String,
pathArg: String,
defaultBitOrderArg: BitOrder,
groupMembersArg: Seq[TermRuntimeData],
isRepresentedArg: Boolean,
couldHaveText: Boolean,
alignmentValueInBitsArg: Int,
hasNoSkipRegionsArg: Boolean,
optIgnoreCaseArg: Option[YesNo],
fillByteEvArg: FillByteEv,
maybeCheckByteAndBitOrderEvArg: Maybe[CheckByteAndBitOrderEv],
maybeCheckBitOrderAndCharsetEvArg: Maybe[CheckBitOrderAndCharsetEv])
extends ModelGroupRuntimeData(positionArg, partialNextElementResolverDelay,
variableMapArg, encInfo, schemaFileLocationArg, ci, diagnosticDebugNameArg, pathArg, defaultBitOrderArg, groupMembersArg,
isRepresentedArg, couldHaveText, alignmentValueInBitsArg, hasNoSkipRegionsArg, optIgnoreCaseArg,
fillByteEvArg,
maybeCheckByteAndBitOrderEvArg,
maybeCheckBitOrderAndCharsetEvArg)
/*
* These Delay-type args are part of how we
* create a structure here which contains some objects that
* somewhere within themselves, refer back to this structure.
*/
final class ChoiceRuntimeData(
positionArg: Int,
partialNextElementResolverDelay: Delay[PartialNextElementResolver],
variableMapArg: VariableMap,
encInfo: EncodingRuntimeData,
schemaFileLocationArg: SchemaFileLocation,
ci: DPathCompileInfo,
diagnosticDebugNameArg: String,
pathArg: String,
defaultBitOrderArg: BitOrder,
groupMembersArg: Seq[TermRuntimeData],
isRepresentedArg: Boolean,
couldHaveText: Boolean,
alignmentValueInBitsArg: Int,
hasNoSkipRegionsArg: Boolean,
optIgnoreCaseArg: Option[YesNo],
fillByteEvArg: FillByteEv,
maybeCheckByteAndBitOrderEvArg: Maybe[CheckByteAndBitOrderEv],
maybeCheckBitOrderAndCharsetEvArg: Maybe[CheckBitOrderAndCharsetEv])
extends ModelGroupRuntimeData(positionArg, partialNextElementResolverDelay,
variableMapArg, encInfo, schemaFileLocationArg, ci, diagnosticDebugNameArg, pathArg, defaultBitOrderArg, groupMembersArg,
isRepresentedArg, couldHaveText, alignmentValueInBitsArg, hasNoSkipRegionsArg, optIgnoreCaseArg, fillByteEvArg,
maybeCheckByteAndBitOrderEvArg,
maybeCheckBitOrderAndCharsetEvArg)
final class VariableRuntimeData(
schemaFileLocationArg: SchemaFileLocation,
diagnosticDebugNameArg: String,
pathArg: String,
namespacesArg: NamespaceBinding,
val external: Boolean,
val direction: VariableDirection,
maybeDefaultValueExprDelay: Delay[Maybe[CompiledExpression[AnyRef]]],
val typeRef: RefQName,
val globalQName: GlobalQName,
val primType: NodeInfo.PrimType,
unqualifiedPathStepPolicyArg: UnqualifiedPathStepPolicy)
extends NonTermRuntimeData(
null, // no variable map
schemaFileLocationArg,
diagnosticDebugNameArg,
pathArg,
namespacesArg,
unqualifiedPathStepPolicyArg) {
/**
* Cyclic structures require initialization
*/
lazy val initialize: Unit = {
maybeDefaultValueExpr // demand this
}
lazy val maybeDefaultValueExpr: Maybe[CompiledExpression[AnyRef]] = maybeDefaultValueExprDelay.value
def createVariableInstance(): VariableInstance = VariableInstance(rd=this)
}