blob: af5cb9f939c5d8c7ac4e5d4cdb420f431f78174b [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 scala.xml.Node
import scala.xml.NodeSeq.seqToNodeSeq
import edu.illinois.ncsa.daffodil.exceptions._
import edu.illinois.ncsa.daffodil.processors._
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TestKind
import com.ibm.icu.impl.UnicodeRegex
import java.util.regex.PatternSyntaxException
import java.util.regex.Pattern
abstract class DFDLAssertionBase(node: Node, decl: AnnotatedSchemaComponent)
extends DFDLStatement(node, decl) {
private lazy val testAttrib = getAttributeOption("test")
// tolerate whitespace. E.g.,
// <assert test="...">
// </assert>
// on two lines, indented, has whitespace in the body of the element.
// Just reformatting XML, or printing it out with a pretty printer
// can break assertions otherwise.
//
// even if you write <assert><![CDATA[{ ... }]]></assert>
// you can still lose because the implementation might convert
// the schema to a string (by pretty printing), and this may re-insert
// whitespace.
//
// So, we trim the body string.
private[dsom] lazy val testBody: Option[String] = node.child.text match {
case s if (s.trim().length() == 0) => None
case txt => Some(txt.trim())
} // package visible for unit testing
private lazy val testPattern = {
val optPattern = getAttributeOption("testPattern")
if (optPattern.isDefined) {
val thePattern = optPattern.get
try {
// val icu =
UnicodeRegex.compile(thePattern) // Check against ICU
// val java =
Pattern.compile(thePattern) // Check against Java
} catch { case e: PatternSyntaxException => SDE("The pattern contained invalid syntax: %s", e.getMessage()) }
val hasWord = thePattern.contains("\\w")
if (decl.term.termRuntimeData.encodingInfo.knownEncodingIsUnicode && hasWord) {
SDW("The encoding is '%s' and \\w was detected in the pattern '%s'. This is not recommended with Unicode encodings.",
decl.term.termRuntimeData.encodingInfo.knownEncodingName, thePattern)
}
}
optPattern
}
final lazy val testKind = getAttributeOption("testKind") match {
case Some(str) => TestKind(str, decl)
case None => TestKind.Expression
}
private lazy val messageAttrib = getAttributeOption("message")
final lazy val message = messageAttrib match {
case None => "%s failed".format(testTxt)
case Some(s) => s
}
final lazy val testTxt = {
val rawTxt = (testKind, testBody, testAttrib, testPattern) match {
case (TestKind.Expression, None, Some(txt), None) => txt
case (TestKind.Expression, txt, None, None) => txt.get
case (TestKind.Pattern, _, _, Some("")) => SDE("The attribute testPattern must not be empty for testKind='pattern'")
case (TestKind.Pattern, None, None, Some(pat)) => pat
case (TestKind.Expression, Some(bdy), Some(attrib), _) => SDE("You may not specify both test attribute and a body expression.")
case (TestKind.Expression, None, None, _) => SDE("You must specify either a test attribute or a body expression.")
case (TestKind.Pattern, Some(bdy), _, Some(txt)) => SDE("You may not specify both testPattern attribute and a body expression.")
case (TestKind.Pattern, None, _, None) => SDE("You must specify either a testPattern attribute or a body expression. for testKind='pattern'")
case (TestKind.Pattern, Some(bdy), None, None) => bdy // pattern as body of assert element
case (TestKind.Pattern, _, Some(tst), _) => SDE("You cannot specify test='%s' for testKind='pattern'", tst)
case (TestKind.Expression, _, _, Some(pat)) => SDE("You cannot specify testPattern='%s' for testKind='expression' (which is the default test kind.)", pat)
case _ => Assert.invariantFailed("unexpected case.")
}
// we need to be sure if it is an expression that it is surrounded by {...}
// after trimming whitespace from before and after. Jira issue DFDL-434
if (testKind == TestKind.Expression)
schemaDefinitionUnless(rawTxt.startsWith("{") && !rawTxt.startsWith("{{") && rawTxt.endsWith("}"),
"Expression must begin with a single '{' and end with a '}'")
rawTxt
}
}
final class DFDLAssert(node: Node, decl: AnnotatedSchemaComponent)
extends DFDLAssertionBase(node, decl) { // with Assert_AnnotationMixin // Note: don't use these generated mixins. Statements don't have format properties
final def gram = LV('gram) {
testKind match {
case TestKind.Pattern => AssertPatternPrim(decl, this)
case TestKind.Expression => AssertBooleanPrim(decl, this)
}
}.value
}
final class DFDLDiscriminator(node: Node, decl: AnnotatedSchemaComponent)
extends DFDLAssertionBase(node, decl) { // with Discriminator_AnnotationMixin
final def gram = LV('gram) {
testKind match {
case TestKind.Pattern => DiscriminatorPatternPrim(decl, this)
case TestKind.Expression => DiscriminatorBooleanPrim(decl, this)
}
}.value
}