blob: d44292c4fb3fddf01fc6e65e8a3c2936ace125ca [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.dsom
import java.math.BigInteger
import scala.xml.Node
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.util.Enum
import scala.util.matching.Regex
import edu.illinois.ncsa.daffodil.dpath.NodeInfo.PrimType
trait Facets { self: SimpleTypeDefBase =>
import edu.illinois.ncsa.daffodil.dsom.FacetTypes._
private def retrieveFacetValueFromRestrictionBase(xml: Node, facetName: Facet.Type): String = {
val res = xml \\ "restriction" \ facetName.toString() \ "@value"
if (res.length > 0) res.head.text else ""
}
private def retrieveFacetValuesFromRestrictionBase(xml: Node, facetName: Facet.Type): Seq[String] = {
val res = xml \\ "restriction" \ facetName.toString() \\ "@value"
if (res.length > 0) res.map(n => n.text).toList else List.empty
}
private def enumeration(xml: Node): Seq[String] = { retrieveFacetValuesFromRestrictionBase(xml, Facet.enumeration) }
private def fractionDigits(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.fractionDigits) }
private def maxExclusive(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.maxExclusive) }
private def maxInclusive(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.maxInclusive) }
private def maxLength(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.maxLength) }
private def minExclusive(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.minExclusive) }
private def minInclusive(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.minInclusive) }
private def minLength(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.minLength) }
private def pattern(xml: Node): Seq[String] = {
// Patterns are OR'd locally, AND'd remotely
retrieveFacetValuesFromRestrictionBase(xml, Facet.pattern).map(p => p)
}
private def totalDigits(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.totalDigits) }
private def whitespace(xml: Node): String = { retrieveFacetValueFromRestrictionBase(xml, Facet.whiteSpace) }
final lazy val localPatternValue: String = {
// Patterns within a type are OR'd
// per http://www.xfront.com/XML-Schema-library/papers/Algorithm-for-Merging-a-simpleType-Dependency-Chain.pdf
//
// Assumed to be valid RegEx
val patterns = pattern(xml)
patterns.mkString("|")
}
final lazy val localMinInclusiveValue: String = minInclusive(xml)
final lazy val localMaxInclusiveValue: String = maxInclusive(xml)
final lazy val localMinExclusiveValue: String = minExclusive(xml)
final lazy val localMaxExclusiveValue: String = maxExclusive(xml)
final lazy val localMinLengthValue: String = minLength(xml)
final lazy val localMaxLengthValue: String = maxLength(xml)
final lazy val localTotalDigitsValue: String = totalDigits(xml)
final lazy val localFractionDigitsValue: String = fractionDigits(xml)
final lazy val localEnumerationValue: String = {
// Enumerations are OR'd
// May be empty string
// Must be unique
val enumerations = enumeration(xml)
val distinctEnums = enumerations.distinct
if (enumerations.size != distinctEnums.size) context.SDE("Enumerations must be unique!")
// Not a regular expression, but we plan to use it as one
// so we must escape characters that can be interpreted as RegEx
enumerations.map(s => escapeForRegex(s)).mkString("|")
}
final lazy val localWhitespaceValue: String = {
whitespace(xml)
context.SDE("whitespaceValue is not implemented for DFDL v1.0 schemas but reserved for future use.")
}
private def escapeForRegex(s: String): String = {
val sb = new StringBuilder
s.foreach(c => {
c match {
case '[' => sb.append("\\[")
case '\\' => sb.append("\\\\")
case '^' => sb.append("\\^")
case '$' => sb.append("\\$")
case '.' => sb.append("\\.")
case '|' => sb.append("\\|")
case '?' => sb.append("\\?")
case '*' => sb.append("\\*")
case '+' => sb.append("\\+")
case '(' => sb.append("\\(")
case ')' => sb.append("\\)")
case '{' => sb.append("\\{")
case '}' => sb.append("\\}")
case x => sb.append(x)
}
})
sb.toString()
}
final lazy val hasEnumeration: Boolean = (localEnumerationValue.length > 0) || (getRemoteFacetValues(Facet.enumeration).size > 0)
final lazy val hasPattern: Boolean = (localPatternValue.length > 0) || (getRemoteFacetValues(Facet.pattern).size > 0)
final lazy val hasMinLength: Boolean = (localMinLengthValue != "") || (getRemoteFacetValues(Facet.minLength).size > 0)
final lazy val hasMaxLength: Boolean = (localMaxLengthValue != "") || (getRemoteFacetValues(Facet.maxLength).size > 0)
final lazy val hasMinInclusive: Boolean = (localMinInclusiveValue != "") || (getRemoteFacetValues(Facet.minInclusive).size > 0)
final lazy val hasMaxInclusive: Boolean = (localMaxInclusiveValue != "") || (getRemoteFacetValues(Facet.maxInclusive).size > 0)
final lazy val hasMinExclusive: Boolean = (localMinExclusiveValue != "") || (getRemoteFacetValues(Facet.minExclusive).size > 0)
final lazy val hasMaxExclusive: Boolean = (localMaxExclusiveValue != "") || (getRemoteFacetValues(Facet.maxExclusive).size > 0)
final lazy val hasTotalDigits: Boolean = (localTotalDigitsValue != "") || (getRemoteFacetValues(Facet.totalDigits).size > 0)
final lazy val hasFractionDigits: Boolean = (localFractionDigitsValue != "") || (getRemoteFacetValues(Facet.fractionDigits).size > 0)
final lazy val patternValues: Seq[FacetValueR] = {
val values = combinedBaseFacets.filter { case (f, _) => f == Facet.pattern }
if (values.size > 0) {
val res: Seq[FacetValueR] = values.map { case (f, v) => (f, v.r) }
res
} else Seq.empty
}
final lazy val enumerationValues: String = {
// Should only ever have one set per SimpleType
val values = combinedBaseFacets.filter { case (f, _) => f == Facet.enumeration }
if (values.size > 0) {
val (_, value) = values(0)
value
} else context.SDE("Enumeration was not found in this context.")
}
// TODO: Tidy up. Can likely replace getFacetValue with a similar call to combinedBaseFacets
// as combinedBaseFacets should contain the 'narrowed' values.
//
final lazy val minLengthValue: java.math.BigDecimal = getFacetValue(localMinLengthValue, Facet.minLength, hasMinLength)
final lazy val maxLengthValue: java.math.BigDecimal = getFacetValue(localMaxLengthValue, Facet.maxLength, hasMaxLength)
final lazy val minInclusiveValue: java.math.BigDecimal = getFacetValue(localMinInclusiveValue, Facet.minInclusive, hasMinInclusive)
final lazy val maxInclusiveValue: java.math.BigDecimal = getFacetValue(localMaxInclusiveValue, Facet.maxInclusive, hasMaxInclusive)
final lazy val minExclusiveValue: java.math.BigDecimal = getFacetValue(localMinExclusiveValue, Facet.minExclusive, hasMinExclusive)
final lazy val maxExclusiveValue: java.math.BigDecimal = getFacetValue(localMaxExclusiveValue, Facet.maxExclusive, hasMaxExclusive)
final lazy val totalDigitsValue: java.math.BigDecimal = getFacetValue(localTotalDigitsValue, Facet.totalDigits, hasTotalDigits)
final lazy val fractionDigitsValue: java.math.BigDecimal = getFacetValue(localFractionDigitsValue, Facet.fractionDigits, hasFractionDigits)
// private def errorOnLocalLessThanBaseFacet(local: Long, base: Long, theFacetType: Facet.Type) = {
// if (local < base) context.SDE("SimpleTypes: The local %s (%s) was less than the base %s (%s) ", theFacetType, local, theFacetType, base)
// }
// private def errorOnLocalGreaterThanBaseFacet(local: Long, base: Long, theFacetType: Facet.Type) = {
// if (local > base) context.SDE("SimpleTypes: The local %s (%s) was greater than the base %s (%s) ", theFacetType, local, theFacetType, base)
// }
private def errorOnLocalLessThanBaseFacet(local: BigInteger,
base: BigInteger, theFacetType: Facet.Type) = {
val res = local.compareTo(base)
if (res < 0) context.SDE("SimpleTypes: The local %s (%s) was less than the base %s (%s) ",
theFacetType, local, theFacetType, base)
}
private def errorOnLocalGreaterThanBaseFacet(local: BigInteger,
base: BigInteger, theFacetType: Facet.Type) = {
val res = local.compareTo(base)
if (res > 0) context.SDE("SimpleTypes: The local %s (%s) was greater than the base %s (%s) ",
theFacetType, local, theFacetType, base)
}
private def errorOnLocalLessThanBaseFacet(local: java.math.BigDecimal,
base: java.math.BigDecimal, theFacetType: Facet.Type) = {
val res = local.compareTo(base)
if (res < 0) context.SDE("SimpleTypes: The local %s (%s) was less than the base %s (%s) ",
theFacetType, local, theFacetType, base)
}
private def errorOnLocalGreaterThanBaseFacet(local: java.math.BigDecimal,
base: java.math.BigDecimal, theFacetType: Facet.Type) = {
val res = local.compareTo(base)
if (res > 0) context.SDE("SimpleTypes: The local %s (%s) was greater than the base %s (%s) ",
theFacetType, local, theFacetType, base)
}
// private def getRemoteFacets(theFacetType: Facet.Type): Seq[FacetValueR] = {
// val remoteValues = remoteBaseFacets.filter { case (f, _) => f == theFacetType }
// if (remoteValues.size > 0) {
// val res: Seq[FacetValueR] = remoteValues.map { case (f, v) => (f, v.r) }
// res
// } else Seq.empty
// }
private def getRemoteFacetValues(theFacetType: Facet.Type): Seq[FacetValue] = {
val res = remoteBaseFacets.filter { case (f, _) => f == theFacetType }
res
}
private def getRemoteFacetValue(theFacetType: Facet.Type): String = {
// Filtering works more appropriately here
val res = remoteBaseFacets.filter { case (f, v) => f == theFacetType }
if (res.size > 0) {
val (_, theFacetValue) = res(0)
return theFacetValue
}
"" // Indicates the facet doesn't exist
}
private def getFacetValue(theLocalValue: String, theRemoteValue: String, theType: Facet.Type, exists: Boolean): java.math.BigDecimal = {
if (!exists) context.SDE("The facet %s was not found.", theType)
else if (theLocalValue != "" && theRemoteValue != "") {
val resFacet = doNumericFacetNarrowing(theLocalValue, theRemoteValue, theType)
new java.math.BigDecimal(resFacet)
} else if (theLocalValue != "") {
checkValueSpaceFacetRange(theLocalValue, theType)
} else {
checkValueSpaceFacetRange(theRemoteValue, theType)
}
}
private def getFacetValue(theLocalValue: String, theType: Facet.Type, exists: Boolean): java.math.BigDecimal = {
val remoteFacets = getRemoteFacetValues(theType)
if (!exists) context.SDE("The facet %s was not found.", theType)
else if (theLocalValue != "" && remoteFacets.size > 0) {
val (_, remoteValue) = getRemoteFacetValues(theType)(0)
val resFacet = doNumericFacetNarrowing(theLocalValue, remoteValue, theType)
new java.math.BigDecimal(resFacet)
} else if (theLocalValue != "") {
checkValueSpaceFacetRange(theLocalValue, theType)
} else {
val (_, remoteValue) = remoteFacets(0)
checkValueSpaceFacetRange(remoteValue, theType)
}
}
private def narrowNonNegativeFacets(localFacet: String, remoteFacet: String, facetType: Facet.Type): String = {
val theLocalFacet = new BigInteger(localFacet)
val theRemoteFacet = new BigInteger(remoteFacet)
if (theLocalFacet.signum() != 1) context.SDE("The %s facet must be a non-negative integer.", facetType)
facetType match {
case Facet.minLength => {
errorOnLocalLessThanBaseFacet(theLocalFacet, theRemoteFacet, facetType)
localFacet
}
case Facet.maxLength | Facet.fractionDigits => {
errorOnLocalGreaterThanBaseFacet(theLocalFacet, theRemoteFacet, facetType)
localFacet
}
case _ => {
val errMsg = "narrowNonNegativeFacets is not valid for %s facet".format(facetType)
Assert.usageError(errMsg)
}
}
}
private def narrowPositiveIntegerFacets(localFacet: String, remoteFacet: String, facetType: Facet.Type): String = {
val theLocalFacet = new BigInteger(localFacet)
val theRemoteFacet = new BigInteger(remoteFacet)
if ((theLocalFacet.signum() != 1) || (theLocalFacet.compareTo(BigInteger.ZERO) == 0)) context.SDE("The %s facet must be a positive integer.", facetType)
facetType match {
case Facet.totalDigits => {
errorOnLocalGreaterThanBaseFacet(theLocalFacet, theRemoteFacet, facetType)
localFacet
}
case _ => {
val errMsg = "narrowPositiveIntegerFacets is not valid for %s facet".format(facetType)
Assert.usageError(errMsg)
}
}
}
private def narrowValueSpaceFacets(localFacet: String, remoteFacet: String, facetType: Facet.Type) = {
val (theLocalFacet, theRemoteFacet) = checkValueSpaceFacetRange(localFacet, remoteFacet, facetType)
// Made it here so range checks were successful
// Now just validate/compare local and base/remote facet
facetType match {
case Facet.minInclusive => { errorOnLocalLessThanBaseFacet(theLocalFacet, theRemoteFacet, facetType) }
case Facet.maxInclusive => { errorOnLocalGreaterThanBaseFacet(theLocalFacet, theRemoteFacet, facetType) }
case Facet.minExclusive => { errorOnLocalLessThanBaseFacet(theLocalFacet, theRemoteFacet, facetType) }
case Facet.maxExclusive => { errorOnLocalGreaterThanBaseFacet(theLocalFacet, theRemoteFacet, facetType) }
case _ => {
val errMsg = "Unrecognized facet type (%s) for narrowing of value-space facets.".format(facetType)
Assert.usageError(errMsg)
}
}
localFacet
}
private def convertFacetToBigDecimal(facet: String): java.math.BigDecimal = {
self.primitiveType match {
case PrimType.DateTime => dateToBigDecimal(facet, "uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxx", PrimType.DateTime.toString(), context)
case PrimType.Date => dateToBigDecimal(facet, "uuuu-MM-ddxxx", PrimType.Date.toString(), context)
case PrimType.Time => dateToBigDecimal(facet, "HH:mm:ss.SSSSSSxxx", PrimType.Time.toString(), context)
case _ => new java.math.BigDecimal(facet)
}
}
private def checkValueSpaceFacetRange(localFacet: String, facetType: Facet.Type): java.math.BigDecimal = {
// Necessary for min/max Inclusive/Exclusive Facets
// Perform conversions once
//val theLocalFacet = new java.math.BigDecimal(localFacet)
val theLocalFacet = convertFacetToBigDecimal(localFacet)
facetType match {
case Facet.maxExclusive | Facet.maxInclusive |
Facet.minExclusive | Facet.minInclusive | Facet.enumeration => {
// Here we're just doing range checking for the
// specified primitive type
primitiveType match {
case PrimType.Int => {
if (!isFacetInIntRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Int range.",
facetType, localFacet)
}
}
case PrimType.Byte => {
if (!isFacetInByteRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Byte range.",
facetType, localFacet)
}
}
case PrimType.Short => {
if (!isFacetInShortRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Short range.",
facetType, localFacet)
}
}
case PrimType.Long => {
if (!isFacetInLongRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Long range.",
facetType, localFacet)
}
}
case PrimType.Integer => {
// Unbounded integer
if (!isFacetInIntegerRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Integer range.",
facetType, localFacet)
}
}
case PrimType.UnsignedInt => {
if (!isFacetInUnsignedIntRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of unsigned int range.",
facetType, localFacet)
}
}
case PrimType.UnsignedByte => {
if (!isFacetInUnsignedByteRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of unsigned byte range.",
facetType, localFacet)
}
}
case PrimType.UnsignedShort => {
if (!isFacetInUnsignedShortRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of unsigned short range.",
facetType, localFacet)
}
}
case PrimType.UnsignedLong => {
if (!isFacetInUnsignedLongRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of unsigned long range.",
facetType, localFacet)
}
}
case PrimType.Double => {
if (!isFacetInDoubleRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Double range.",
facetType, localFacet)
}
}
case PrimType.Float => {
if (!isFacetInFloatRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of Float range.",
facetType, localFacet)
}
}
case PrimType.NonNegativeInteger => {
// Unsigned Unbounded Integer
if (!isFacetInNonNegativeIntegerRange(theLocalFacet)) {
context.SDE("%s facet value (%s) was found to be outside of NonNegativeInteger range.",
facetType, localFacet)
}
}
case PrimType.DateTime => { /* Nothing to do here */ }
case PrimType.Date => { /* Nothing to do here */ }
case PrimType.Time => { /* Nothing to do here */ }
case PrimType.Boolean => notYetImplemented("checkValueSpaceFacetRange - Boolean")
case PrimType.HexBinary => { /* Nothing to do here */ }
case PrimType.String => { /* Nothing to do here */ }
case _ => schemaDefinitionError("checkValueSpaceFacetRange - Unrecognized primitive type: %s", primitiveType.name)
}
}
case _ => { /* Nothing to do */ }
}
theLocalFacet
}
private def checkValueSpaceFacetRange(localFacet: String,
remoteFacet: String, facetType: Facet.Type): (java.math.BigDecimal, java.math.BigDecimal) = {
// Neccessary for min/max Inclusive/Exclusive Facets
// TODO: I think the performance here can be improved.
//
// Consider storing the remoteBaseFacets as their actual evaluated values
// rather than just as String. This would prevent us from having to perform
// the checkValueSpaceFacetRange on the remoteFacet here as it would've already
// been done in the base. --TRW
// Perform conversions once
val theRemoteFacet = checkValueSpaceFacetRange(remoteFacet, facetType) //new java.math.BigDecimal(remoteFacet)
val theLocalFacet = checkValueSpaceFacetRange(localFacet, facetType)
(theLocalFacet, theRemoteFacet)
}
private def isFacetInByteRange(facet: java.math.BigDecimal): Boolean = self.isInByteRange(facet)
private def isFacetInShortRange(facet: java.math.BigDecimal): Boolean = self.isInShortRange(facet)
private def isFacetInIntRange(facet: java.math.BigDecimal): Boolean = self.isInIntRange(facet)
private def isFacetInIntegerRange(facet: java.math.BigDecimal): Boolean = self.isInIntegerRange(facet)
private def isFacetInLongRange(facet: java.math.BigDecimal): Boolean = self.isInLongRange(facet)
private def isFacetInDoubleRange(facet: java.math.BigDecimal): Boolean = self.isInDoubleRange(facet)
private def isFacetInFloatRange(facet: java.math.BigDecimal): Boolean = self.isInFloatRange(facet)
// private def isFacetInDecimalRange(facet: java.math.BigDecimal): Boolean = {
// BigDecimal is unbounded? So nothing outside of its range?
// true
// }
// private def isFacetInNegativeIntegerRange(facet: java.math.BigDecimal): Boolean = self.isInNonNegativeIntegerRange(facet)
private def isFacetInNonNegativeIntegerRange(facet: java.math.BigDecimal): Boolean = self.isInNonNegativeIntegerRange(facet)
private def isFacetInUnsignedLongRange(facet: java.math.BigDecimal): Boolean =
isInUnsignedLongRange(facet)
private def isFacetInUnsignedIntRange(facet: java.math.BigDecimal): Boolean =
isInUnsignedIntRange(facet)
private def isFacetInUnsignedShortRange(facet: java.math.BigDecimal): Boolean =
isInUnsignedShortRange(facet)
private def isFacetInUnsignedByteRange(facet: java.math.BigDecimal): Boolean =
isInUnsignedByteRange(facet)
protected def doNumericFacetNarrowing(localFacet: String, remoteFacet: String, facetType: Facet.Type) = {
// Assumes both local and remote facets exist
// Only for Numeric facets
//
// Can likely do narrowing checks here
//
// BigInt use compareTo
// a negative number, zero, or a positive number as this BigInteger is numerically less than,
// equal to, or greater than o, which must be a BigInteger.
facetType match {
case Facet.minLength | Facet.maxLength | Facet.fractionDigits => {
// Non-negative Integers. BigInt
narrowNonNegativeFacets(localFacet, remoteFacet, facetType)
}
case Facet.minInclusive | Facet.maxInclusive | Facet.minExclusive | Facet.maxExclusive => {
// In value-space of base type. BigDecimal?
narrowValueSpaceFacets(localFacet, remoteFacet, facetType)
}
case Facet.totalDigits => {
// Positive Integer (value greater than 0). BigInt
narrowPositiveIntegerFacets(localFacet, remoteFacet, facetType)
}
case _ => Assert.usageError("Call to 'doNumericFacetNarrowing' only valid for Numeric Facets.")
}
}
private def getLocalValue(theType: Facet.Type) = {
val res = localBaseFacets.filter { case (f, v) => f == theType }
if (res.length > 0) {
val (_, theFacetValue) = res(0)
theFacetValue
} else ""
}
protected def getCombinedValue(theType: Facet.Type) = {
val lValue = getLocalValue(theType)
val rValue = getRemoteFacetValue(theType)
val cValue = getFacetValue(lValue, rValue, theType, true)
cValue
}
protected def getCombinedValueEnum = {
val lValue = getLocalValue(Facet.enumeration)
val rValue = getRemoteFacetValue(Facet.enumeration)
lValue.foreach(e => {
if (rValue.length() > 0 && !rValue.contains(e)) context.SDE("Local enumerations must be a subset of base enumerations.")
})
if (lValue.length() > 0) { lValue }
else { rValue }
}
}