| /* |
| * 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.dpath.NodeInfo |
| import org.apache.daffodil.dsom.DPathCompileInfo |
| import org.apache.daffodil.dsom.ImplementsThrowsSDE |
| import org.apache.daffodil.dsom.CompiledExpression |
| import org.apache.daffodil.dsom.ConstantExpression |
| import org.apache.daffodil.exceptions.HasSchemaFileLocation |
| import org.apache.daffodil.exceptions.SchemaFileLocation |
| import org.apache.daffodil.schema.annotation.props.gen.BitOrder |
| import org.apache.daffodil.schema.annotation.props.gen.YesNo |
| import org.apache.daffodil.util.PreSerialization |
| import org.apache.daffodil.util.TransientParam |
| import org.apache.daffodil.util.Maybe |
| import org.apache.daffodil.util.Maybe._ |
| import org.apache.daffodil.xml.GlobalQName |
| import org.apache.daffodil.xml.RefQName |
| import org.apache.daffodil.schema.annotation.props.gen.BitOrder |
| import org.apache.daffodil.xml.NS |
| import org.apache.daffodil.dsom.FacetTypes |
| import scala.xml.NamespaceBinding |
| import org.apache.daffodil.util.Maybe |
| import org.apache.daffodil.util.Maybe._ |
| import org.apache.daffodil.xml._ |
| import org.apache.daffodil.dsom.DPathElementCompileInfo |
| import org.apache.daffodil.dsom.CompiledExpression |
| import org.apache.daffodil.schema.annotation.props.gen.Representation |
| import org.apache.daffodil.exceptions.SchemaFileLocation |
| import org.apache.daffodil.dpath.NodeInfo.PrimType |
| import org.apache.daffodil.infoset.NextElementResolver |
| import org.apache.daffodil.util.TransientParam |
| import org.apache.daffodil.schema.annotation.props.gen.YesNo |
| import org.apache.daffodil.exceptions._ |
| import org.apache.daffodil.Implicits._; object NoWarn { ImplicitsSuppressUnusedImportWarning() } |
| import org.apache.daffodil.infoset._ |
| import org.apache.daffodil.dpath.NodeInfo.PrimType |
| import org.apache.daffodil.util.OKOrError |
| import java.util.regex.Matcher |
| import org.apache.daffodil.api.DaffodilTunables |
| import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind |
| |
| /* |
| * 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 { |
| val schemaFileLocation: SchemaFileLocation |
| val diagnosticDebugName: String |
| val path: String |
| val namespaces: NamespaceBinding |
| |
| def variableMap: VariableMap |
| override def toString = diagnosticDebugName |
| |
| def tunable: DaffodilTunables |
| |
| } |
| |
| object TermRuntimeData { |
| |
| private var nextID = 0 |
| |
| def generateTermID: Int = synchronized { |
| val n = nextID |
| nextID += 1 |
| n |
| } |
| |
| } |
| |
| sealed abstract class TermRuntimeData( |
| /** |
| * These transient by-name args are part of how we |
| * hook these objects into a parent-child tree without |
| * having to use an assignment to a var. |
| */ |
| @TransientParam encodingInfoArg: => EncodingRuntimeData, |
| @TransientParam dpathCompileInfoArg: => DPathCompileInfo, |
| @TransientParam isRepresentedArg: => Boolean, |
| @TransientParam couldHaveTextArg: => Boolean, |
| @TransientParam alignmentValueInBitsArg: => Int, |
| @TransientParam hasNoSkipRegionsArg: => Boolean, |
| @TransientParam defaultBitOrderArg: => BitOrder, |
| @TransientParam optIgnoreCaseArg: => Option[YesNo], |
| @TransientParam maybeFillByteEvArg: => Maybe[FillByteEv], |
| @TransientParam maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv], |
| @TransientParam maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv]) |
| extends RuntimeData |
| with Serializable |
| with PreSerialization { |
| |
| 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 tunable = dpathCompileInfo.tunable |
| |
| lazy val encodingInfo = encodingInfoArg |
| lazy val dpathCompileInfo = dpathCompileInfoArg |
| lazy val isRepresented = isRepresentedArg |
| lazy val couldHaveText = couldHaveTextArg |
| lazy val alignmentValueInBits = alignmentValueInBitsArg |
| lazy val hasNoSkipRegions = hasNoSkipRegionsArg |
| lazy val defaultBitOrder = defaultBitOrderArg |
| lazy val optIgnoreCase = optIgnoreCaseArg |
| lazy val maybeFillByteEv = maybeFillByteEvArg |
| lazy val maybeCheckByteAndBitOrderEv = maybeCheckByteAndBitOrderEvArg |
| lazy val maybeCheckBitOrderAndCharsetEv = maybeCheckBitOrderAndCharsetEvArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| encodingInfo |
| dpathCompileInfo |
| isRepresented |
| couldHaveText |
| alignmentValueInBits |
| hasNoSkipRegions |
| defaultBitOrder |
| optIgnoreCase |
| maybeFillByteEv |
| tunable |
| maybeCheckByteAndBitOrderEv |
| maybeCheckBitOrderAndCharsetEv |
| } |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| |
| } |
| |
| sealed class NonTermRuntimeData( |
| /** |
| * These transient by-name args are part of how we |
| * hook these objects into a parent-child tree without |
| * having to use an assignment to a var. |
| */ |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam tunableArg: => DaffodilTunables) |
| extends RuntimeData |
| with PreSerialization { |
| |
| override lazy val variableMap = variableMapArg |
| lazy val schemaFileLocation = schemaFileLocationArg |
| lazy val diagnosticDebugName = diagnosticDebugNameArg |
| lazy val path = pathArg |
| lazy val namespaces = namespacesArg |
| lazy val tunable = tunableArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| variableMap |
| schemaFileLocation |
| diagnosticDebugName |
| path |
| namespaces |
| tunable |
| } |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| |
| } |
| |
| /** |
| * 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( |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam primTypeArg: => NodeInfo.PrimType, |
| @TransientParam noFacetChecksArg: => Boolean, |
| @TransientParam patternValuesArg: => Seq[FacetTypes.FacetValueR], |
| @TransientParam enumerationValuesArg: => Option[String], |
| @TransientParam minLengthArg: => Option[java.math.BigDecimal], |
| @TransientParam maxLengthArg: => Option[java.math.BigDecimal], |
| @TransientParam minInclusiveArg: => Option[java.math.BigDecimal], |
| @TransientParam maxInclusiveArg: => Option[java.math.BigDecimal], |
| @TransientParam minExclusiveArg: => Option[java.math.BigDecimal], |
| @TransientParam maxExclusiveArg: => Option[java.math.BigDecimal], |
| @TransientParam totalDigitsArg: => Option[java.math.BigDecimal], |
| @TransientParam fractionDigitsArg: => Option[java.math.BigDecimal], |
| @TransientParam unionMemberTypesArg: => Seq[SimpleTypeRuntimeData], |
| @TransientParam tunableArg: => DaffodilTunables, |
| @TransientParam repTypeRuntimeDataArg: => Option[SimpleTypeRuntimeData], |
| @TransientParam repValueSetArg: => Option[RepValueSet[AnyRef]], |
| @TransientParam typeCalculatorArg: => Option[TypeCalculator[AnyRef,AnyRef]], |
| @TransientParam optRepPrimTypeArg: => Option[PrimType] |
| ) extends NonTermRuntimeData(variableMapArg, schemaFileLocationArg, diagnosticDebugNameArg, |
| pathArg, namespacesArg, tunableArg) { |
| |
| import OKOrError._ |
| |
| lazy val primType = primTypeArg |
| lazy val noFacetChecks = noFacetChecksArg |
| lazy val patternValues = patternValuesArg |
| lazy val enumerationValues = enumerationValuesArg |
| lazy val minLength = minLengthArg |
| lazy val maxLength = maxLengthArg |
| lazy val minInclusive = minInclusiveArg |
| lazy val maxInclusive = maxInclusiveArg |
| lazy val minExclusive = minExclusiveArg |
| lazy val maxExclusive = maxExclusiveArg |
| lazy val totalDigits = totalDigitsArg |
| lazy val fractionDigits = fractionDigitsArg |
| lazy val unionMemberTypes = unionMemberTypesArg |
| lazy val repTypeRuntimeData = repTypeRuntimeDataArg |
| lazy val repValueSet = repValueSetArg |
| lazy val typeCalculator = typeCalculatorArg |
| lazy val optRepPrimType = optRepPrimTypeArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| primType |
| noFacetChecks |
| patternValues |
| enumerationValues |
| minLength |
| maxLength |
| minInclusive |
| maxInclusive |
| minExclusive |
| maxExclusive |
| totalDigits |
| fractionDigits |
| unionMemberTypes |
| repTypeRuntimeData |
| repValueSet |
| typeCalculator |
| optRepPrimType |
| } |
| |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| |
| /** |
| * 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) { |
| val patternStrings = e.patternValues.map { case (_, 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.asInstanceOf[String] |
| 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.asInstanceOf[Array[Byte]] |
| |
| 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.asInstanceOf[String] |
| 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.asInstanceOf[Array[Byte]] |
| // 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 number = new java.math.BigDecimal(scala.math.pow(10.0, digits.doubleValue())) |
| val biNumber = new java.math.BigInteger(number.intValueExact().toString()) |
| val bdData = diNode.dataValueAsBigDecimal.unscaledValue() |
| val isDataLessThanNumber = bdData.compareTo(biNumber) < 0 |
| isDataLessThanNumber |
| } |
| |
| 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 |
| } |
| |
| } |
| |
| /* |
| * These objects have become too big. Most processors don't need most of this stuff. |
| * |
| * The objective should be to take things OUT of this structure and pass directly to the |
| * constructor of the parser/unparser. |
| * |
| * These objects are for things that are generally heavily used everywhere like information for |
| * providing diagnostics. |
| */ |
| |
| final class ElementRuntimeData( |
| /** |
| * These transient by-name args are part of how we hook these objects into a |
| * parent-child tree without having to use an assignment to a var. Note that |
| * all transient elements must be added to the preSerialization method below |
| * to allow parser serialization/deserialization to work. |
| */ |
| @TransientParam childrenArg: => Seq[ElementRuntimeData], |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam nextElementResolverArg: => NextElementResolver, |
| @TransientParam childElementResolverArg: => NextElementResolver, |
| @TransientParam encInfoArg: => EncodingRuntimeData, |
| @TransientParam dpathElementCompileInfoArg: => DPathElementCompileInfo, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam minimizedScopeArg: => NamespaceBinding, |
| @TransientParam defaultBitOrderArg: => BitOrder, |
| @TransientParam optPrimTypeArg: => Option[PrimType], |
| @TransientParam targetNamespaceArg: => NS, |
| @TransientParam thisElementsNamespaceArg: => NS, |
| @TransientParam optSimpleTypeRuntimeDataArg: => Option[SimpleTypeRuntimeData], |
| @TransientParam minOccursArg: => Long, |
| @TransientParam maxOccursArg: => Long, |
| @TransientParam maybeOccursCountKindArg: => Maybe[OccursCountKind], |
| @TransientParam nameArg: => String, |
| @TransientParam targetNamespacePrefixArg: => String, |
| @TransientParam thisElementsNamespacePrefixArg: => String, |
| @TransientParam isHiddenArg: => Boolean, |
| @TransientParam isNillableArg: => Boolean, |
| @TransientParam isArrayArg: => Boolean, // can have more than 1 occurrence |
| @TransientParam isOptionalArg: => Boolean, // can have only 0 or 1 occurrence |
| @TransientParam isRequiredOrOptionalArg: => 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. |
| */ |
| @TransientParam namedQNameArg: => NamedQName, |
| @TransientParam isRepresentedArg: => Boolean, |
| @TransientParam couldHaveTextArg: => Boolean, |
| @TransientParam alignmentValueInBitsArg: => Int, |
| @TransientParam hasNoSkipRegionsArg: => Boolean, |
| @TransientParam impliedRepresentationArg: => Representation, |
| @TransientParam optIgnoreCaseArg: => Option[YesNo], |
| @TransientParam optDefaultValueArg: => Option[AnyRef], |
| // |
| // Unparser-specific arguments |
| // |
| @TransientParam optTruncateSpecifiedLengthStringArg: => Option[Boolean], |
| @TransientParam outputValueCalcExprArg: => Option[CompiledExpression[AnyRef]], |
| @TransientParam maybeBinaryFloatRepEvArg: => Maybe[BinaryFloatRepEv], |
| @TransientParam maybeByteOrderEvArg: => Maybe[ByteOrderEv], |
| @TransientParam maybeFillByteEvArg: => Maybe[FillByteEv], |
| @TransientParam maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv], |
| @TransientParam maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv], |
| @TransientParam isQuasiElementArg: => Boolean) |
| extends TermRuntimeData(encInfoArg, dpathElementCompileInfoArg, isRepresentedArg, couldHaveTextArg, alignmentValueInBitsArg, hasNoSkipRegionsArg, |
| defaultBitOrderArg, optIgnoreCaseArg, maybeFillByteEvArg, |
| maybeCheckByteAndBitOrderEvArg, |
| maybeCheckBitOrderAndCharsetEvArg) { |
| |
| override def isRequiredScalar = !isArray && isRequiredOrOptional |
| |
| lazy val children = childrenArg |
| lazy val variableMap = variableMapArg |
| lazy val nextElementResolver = nextElementResolverArg |
| lazy val childElementResolver = childElementResolverArg |
| lazy val encInfo = encInfoArg |
| lazy val dpathElementCompileInfo = dpathElementCompileInfoArg |
| lazy val schemaFileLocation = schemaFileLocationArg |
| lazy val diagnosticDebugName = diagnosticDebugNameArg |
| lazy val path = pathArg |
| lazy val namespaces = namespacesArg |
| lazy val minimizedScope = minimizedScopeArg |
| lazy val optPrimType = optPrimTypeArg |
| lazy val targetNamespace = targetNamespaceArg |
| lazy val thisElementsNamespace = thisElementsNamespaceArg |
| lazy val optSimpleTypeRuntimeData = optSimpleTypeRuntimeDataArg |
| lazy val minOccurs = minOccursArg |
| lazy val maxOccurs = maxOccursArg |
| lazy val maybeOccursCountKind = maybeOccursCountKindArg |
| lazy val name = nameArg |
| lazy val targetNamespacePrefix = targetNamespacePrefixArg |
| lazy val thisElementsNamespacePrefix = thisElementsNamespacePrefixArg |
| lazy val isHidden = isHiddenArg |
| lazy val isNillable = isNillableArg |
| override lazy val isArray = isArrayArg |
| lazy val isOptional = isOptionalArg |
| lazy val isRequiredOrOptional = isRequiredOrOptionalArg // if true, no uncertainty about number of occurrences. |
| lazy val namedQName = namedQNameArg |
| lazy val impliedRepresentation = impliedRepresentationArg |
| lazy val optDefaultValue = optDefaultValueArg |
| lazy val optTruncateSpecifiedLengthString = optTruncateSpecifiedLengthStringArg |
| lazy val outputValueCalcExpr = outputValueCalcExprArg |
| lazy val maybeBinaryFloatRepEv = maybeBinaryFloatRepEvArg |
| lazy val maybeByteOrderEv = maybeByteOrderEvArg |
| lazy val isQuasiElement = isQuasiElementArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| children |
| variableMap |
| nextElementResolver |
| childElementResolver |
| encInfo |
| dpathElementCompileInfo |
| schemaFileLocation |
| diagnosticDebugName |
| path |
| namespaces |
| minimizedScope |
| optPrimType |
| targetNamespace |
| thisElementsNamespace |
| optSimpleTypeRuntimeData |
| minOccurs |
| maxOccurs |
| maybeOccursCountKind |
| name |
| targetNamespacePrefix |
| thisElementsNamespacePrefix |
| isHidden |
| isNillable |
| isArray |
| isOptional |
| isRequiredOrOptional |
| namedQName |
| impliedRepresentation |
| optDefaultValue |
| optTruncateSpecifiedLengthString |
| outputValueCalcExpr |
| maybeBinaryFloatRepEv |
| maybeByteOrderEv |
| isQuasiElement |
| } |
| |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| |
| final def childERDs = children |
| |
| def isSimpleType = optPrimType.isDefined |
| |
| def schemaURIStringsForFullValidation = schemaURIStringsForFullValidation1.distinct |
| private def schemaURIStringsForFullValidation1: Seq[String] = (schemaFileLocation.uriString +: |
| childERDs.flatMap { _.schemaURIStringsForFullValidation1 }) |
| |
| def isComplexType = !isSimpleType |
| |
| def prefixedName = { |
| if (thisElementsNamespacePrefix != null) { |
| thisElementsNamespacePrefix + ":" + name |
| } else { |
| name |
| } |
| } |
| |
| } |
| |
| sealed abstract class ModelGroupRuntimeData( |
| /** |
| * These transient by-name args are part of how we |
| * hook these objects into a parent-child tree without |
| * having to use an assignment to a var. |
| */ |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam encInfoArg: => EncodingRuntimeData, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam ciArg: => DPathCompileInfo, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam defaultBitOrderArg: => BitOrder, |
| @TransientParam groupMembersArg: => Seq[TermRuntimeData], |
| @TransientParam isRepresentedArg: => Boolean, |
| @TransientParam couldHaveTextArg: => Boolean, |
| @TransientParam alignmentValueInBitsArg: => Int, |
| @TransientParam hasNoSkipRegionsArg: => Boolean, |
| @TransientParam optIgnoreCaseArg: => Option[YesNo], |
| @TransientParam maybeFillByteEvArg: => Maybe[FillByteEv], |
| @TransientParam maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv], |
| @TransientParam maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv]) |
| extends TermRuntimeData( |
| encInfoArg, ciArg, isRepresentedArg, couldHaveTextArg, alignmentValueInBitsArg, hasNoSkipRegionsArg, |
| defaultBitOrderArg, optIgnoreCaseArg, maybeFillByteEvArg, |
| maybeCheckByteAndBitOrderEvArg, |
| maybeCheckBitOrderAndCharsetEvArg) { |
| |
| final override def isRequiredScalar = true |
| final override def isArray = false |
| |
| lazy val variableMap = variableMapArg |
| lazy val encInfo = encInfoArg |
| lazy val schemaFileLocation = schemaFileLocationArg |
| lazy val ci = ciArg |
| lazy val diagnosticDebugName = diagnosticDebugNameArg |
| lazy val path = pathArg |
| lazy val namespaces = namespacesArg |
| lazy val groupMembers = groupMembersArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| variableMap |
| encInfo |
| schemaFileLocation |
| ci |
| diagnosticDebugName |
| path |
| namespaces |
| groupMembers |
| } |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| } |
| |
| final class SequenceRuntimeData( |
| /** |
| * These transient by-name args are part of how we |
| * hook these objects into a parent-child tree without |
| * having to use an assignment to a var. |
| */ |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam encInfoArg: => EncodingRuntimeData, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam ciArg: => DPathCompileInfo, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam defaultBitOrderArg: => BitOrder, |
| @TransientParam groupMembersArg: => Seq[TermRuntimeData], |
| @TransientParam isRepresentedArg: => Boolean, |
| @TransientParam couldHaveTextArg: => Boolean, |
| @TransientParam alignmentValueInBitsArg: => Int, |
| @TransientParam hasNoSkipRegionsArg: => Boolean, |
| @TransientParam optIgnoreCaseArg: => Option[YesNo], |
| @TransientParam maybeFillByteEvArg: => Maybe[FillByteEv], |
| @TransientParam maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv], |
| @TransientParam maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv]) |
| extends ModelGroupRuntimeData(variableMapArg, encInfoArg, schemaFileLocationArg, ciArg, diagnosticDebugNameArg, pathArg, namespacesArg, defaultBitOrderArg, groupMembersArg, |
| isRepresentedArg, couldHaveTextArg, alignmentValueInBitsArg, hasNoSkipRegionsArg, optIgnoreCaseArg, |
| maybeFillByteEvArg, |
| maybeCheckByteAndBitOrderEvArg, |
| maybeCheckBitOrderAndCharsetEvArg) |
| |
| final class ChoiceRuntimeData( |
| /** |
| * These transient by-name args are part of how we |
| * hook these objects into a parent-child tree without |
| * having to use an assignment to a var. |
| */ |
| @TransientParam variableMapArg: => VariableMap, |
| @TransientParam encInfoArg: => EncodingRuntimeData, |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam ciArg: => DPathCompileInfo, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam defaultBitOrderArg: => BitOrder, |
| @TransientParam groupMembersArg: => Seq[TermRuntimeData], |
| @TransientParam isRepresentedArg: => Boolean, |
| @TransientParam couldHaveTextArg: => Boolean, |
| @TransientParam alignmentValueInBitsArg: => Int, |
| @TransientParam hasNoSkipRegionsArg: => Boolean, |
| @TransientParam optIgnoreCaseArg: => Option[YesNo], |
| @TransientParam maybeFillByteEvArg: => Maybe[FillByteEv], |
| @TransientParam maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv], |
| @TransientParam maybeCheckBitOrderAndCharsetEvArg: => Maybe[CheckBitOrderAndCharsetEv]) |
| extends ModelGroupRuntimeData(variableMapArg, encInfoArg, schemaFileLocationArg, ciArg, diagnosticDebugNameArg, pathArg, namespacesArg, defaultBitOrderArg, groupMembersArg, |
| isRepresentedArg, couldHaveTextArg, alignmentValueInBitsArg, hasNoSkipRegionsArg, optIgnoreCaseArg, maybeFillByteEvArg, |
| maybeCheckByteAndBitOrderEvArg, |
| maybeCheckBitOrderAndCharsetEvArg) |
| |
| final class VariableRuntimeData( |
| @TransientParam schemaFileLocationArg: => SchemaFileLocation, |
| @TransientParam diagnosticDebugNameArg: => String, |
| @TransientParam pathArg: => String, |
| @TransientParam namespacesArg: => NamespaceBinding, |
| @TransientParam externalArg: => Boolean, |
| @TransientParam maybeDefaultValueExprArg: => Maybe[CompiledExpression[AnyRef]], |
| @TransientParam typeRefArg: => RefQName, |
| @TransientParam globalQNameArg: => GlobalQName, |
| @TransientParam primTypeArg: => NodeInfo.PrimType, |
| @TransientParam tunableArg: => DaffodilTunables) |
| extends NonTermRuntimeData( |
| null, // no variable map |
| schemaFileLocationArg, |
| diagnosticDebugNameArg, |
| pathArg, |
| namespacesArg, |
| tunableArg) |
| with Serializable { |
| |
| lazy val external = externalArg |
| lazy val maybeDefaultValueExpr = maybeDefaultValueExprArg |
| lazy val typeRef = typeRefArg |
| lazy val globalQName = globalQNameArg |
| lazy val primType = primTypeArg |
| |
| override def preSerialization: Unit = { |
| super.preSerialization |
| external |
| maybeDefaultValueExpr |
| typeRef |
| globalQName |
| primType |
| } |
| |
| @throws(classOf[java.io.IOException]) |
| final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out) |
| |
| private lazy val state = |
| if (!maybeDefaultValueExpr.isDefined) VariableUndefined |
| else VariableDefined |
| |
| private lazy val maybeValue: Maybe[AnyRef] = |
| if (maybeDefaultValueExpr.isEmpty) Nope |
| else { |
| val defaultValueExpr = maybeDefaultValueExpr.get |
| defaultValueExpr match { |
| case constExpr: ConstantExpression[_] => One(constExpr.constant.asInstanceOf[AnyRef]) |
| case _ => Nope |
| } |
| } |
| |
| def newVariableInstance: Variable = Variable(state, maybeValue, this, maybeDefaultValueExpr) |
| |
| } |