blob: be13d58c3992461ea790fae9d123d250cdd956c5 [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 edu.illinois.ncsa.daffodil.exceptions._
import edu.illinois.ncsa.daffodil.api.LocationInSchemaFile
import scala.xml.Node
import edu.illinois.ncsa.daffodil.ExecutionMode
import edu.illinois.ncsa.daffodil.processors.PState
import edu.illinois.ncsa.daffodil.api.Diagnostic
import edu.illinois.ncsa.daffodil.processors.ParseOrUnparseState
class SchemaDefinitionError(schemaContext: Option[SchemaFileLocation],
annotationContext: Option[SchemaFileLocation],
kind: String,
args: Any*)
extends SchemaDefinitionDiagnosticBase(schemaContext, None, annotationContext, kind, args: _*) {
def this(sc: SchemaFileLocation, kind: String, args: Any*) = this(Some(sc), None, kind, args: _*)
val diagnosticKind = "Error"
}
/**
* Specific class used for this specific error, because we need to pick this off
* in the debugger for special handling.
*/
class RelativePathPastRootError(kind: String)
extends SchemaDefinitionError(None, None, kind)
class RuntimeSchemaDefinitionError(schemaContext: SchemaFileLocation,
runtimeContext: ParseOrUnparseState,
kind: String,
args: Any*)
extends SchemaDefinitionDiagnosticBase(
Some(schemaContext), Some(runtimeContext), None, kind, args: _*) {
val diagnosticKind = "Error"
}
class RuntimeSchemaDefinitionWarning(schemaContext: SchemaFileLocation,
runtimeContext: ParseOrUnparseState,
kind: String,
args: Any*)
extends SchemaDefinitionDiagnosticBase(
Some(schemaContext), Some(runtimeContext), None, kind, args: _*) {
override def isError = false
val diagnosticKind = "Warning"
}
class SchemaDefinitionWarning(schemaContext: Option[SchemaFileLocation],
annotationContext: Option[SchemaFileLocation],
kind: String,
args: Any*)
extends SchemaDefinitionDiagnosticBase(schemaContext, None, annotationContext, kind, args: _*) {
def this(sc: SchemaFileLocation, kind: String, args: Any*) = this(Some(sc), None, kind, args: _*)
override def isError = false
val diagnosticKind = "Warning"
}
class ValidationError(schemaContext: Option[SchemaFileLocation],
runtimeContext: ParseOrUnparseState,
kind: String,
args: Any*)
extends SchemaDefinitionDiagnosticBase(
schemaContext, Some(runtimeContext), None, kind, args: _*) {
override def isError = false
val diagnosticKind = "Error"
override def contextInfo(msg: String,
diagnosticKind: String,
schContextLocDescription: String,
annContextLocDescription: String,
schemaContext: Option[SchemaFileLocation]): String = {
val dataLocDescription = currentLocation.map { " Data Context: " + _.toString + "." }.getOrElse("")
val res = "Validation " + diagnosticKind + ": " + msg +
"\nSchema context: " + Some(schemaContext).getOrElse("top level") + "." +
// TODO: should be one or the other, never(?) both
schContextLocDescription +
annContextLocDescription + dataLocDescription
res
}
}
abstract class SchemaDefinitionDiagnosticBase(
val schemaContext: Option[SchemaFileLocation],
runtimeContext: Option[ParseOrUnparseState],
val annotationContext: Option[SchemaFileLocation],
val kind: String,
val args: Any*) extends Exception with DiagnosticImplMixin {
protected val currentLocation = runtimeContext.map { _.currentLocation }
/**
* These are put into a collection to remove duplicates so equals and hash
* matter or we'll get duplicates we don't want.
*/
override def equals(b: Any): Boolean = {
b match {
case other: SchemaDefinitionDiagnosticBase => {
val isSCSame = schemaContext == other.schemaContext
val isACSame = annotationContext == other.annotationContext
val isKindSame = kind == other.kind
val isArgsSame = args == other.args
val isDiagnosticKindSame = diagnosticKind == other.diagnosticKind
val isCLSame = currentLocation == other.currentLocation
val res = isSCSame && isACSame && isKindSame &&
isArgsSame && isDiagnosticKindSame && isCLSame
res
}
case _ => false
}
}
override def hashCode = {
schemaContext.hashCode +
currentLocation.hashCode +
annotationContext.hashCode +
kind.hashCode +
args.hashCode +
diagnosticKind.hashCode
}
def diagnosticKind: String
override def getLocationsInSchemaFiles: Seq[LocationInSchemaFile] = schemaContext.toList
def contextInfo(msg: String,
diagnosticKind: String,
schContextLocDescription: String,
annContextLocDescription: String,
schemaContext: Option[SchemaFileLocation]): String = {
val runtime = if (currentLocation.isDefined) "Runtime " else ""
val dataLocDescription =
currentLocation.map { " Data Context: " + _.toString + "." }.getOrElse("")
val res = runtime + "Schema Definition " + diagnosticKind + ": " + msg +
"\nSchema context: " + schemaContext.getOrElse("top level") + "." +
// TODO: should be one or the other, never(?) both
schContextLocDescription +
annContextLocDescription + dataLocDescription
res
}
// TODO: Alternate constructor that allows data locations.
// Because some SDEs are caught only once Processing starts.
// They're still SDE but they will have data location information.
override def toString = {
//
// It is important that this routine is robust. It is used to print error messages
// so if something goes wrong in this, you run around in circles. I believe the
// stack-overaflow problems will be caught so long as one is running through lazy val aka
// OOLAG 'attributes' framework.
//
val res = {
//
// Right here is where we would lookup the symbolic error kind id, and
// choose a locale-based message string.
//
// For now, we'll just do an automatic English message.
//
val msg = if (args.size > 0)
try {
kind.format(args: _*)
} catch {
case th: Throwable => {
val exc = th
Assert.abort("Throw during toString of SDE object. kind='" + kind + "', args=" + args + " thrown type was: " + exc.getClass())
}
}
else kind
// this is where it gets kind of hairy. We're depending on fairly rich
// attribute calculations in order to generate the context information
// in these diagnostic messages. Break any of that stuff, and suddenly
// you will get circularity errors from OOLAG.
// beats a stack-overflow at least.
val schContextLocDescription =
schemaContext.map { " " + _.locationDescription + "." }.getOrElse("")
val annContextLocDescription =
annotationContext.map { " " + _.locationDescription + "." }.getOrElse("")
val res = contextInfo(msg, diagnosticKind, schContextLocDescription,
annContextLocDescription, schemaContext)
res
}
res
}
override def getMessage = toString
}
trait ImplementsThrowsSDE
extends ThrowsSDE {
def NoAnnotationContext: Option[SchemaFileLocation] = None
def SDE(id: String, args: Any*): Nothing = {
val sde = new SchemaDefinitionError(Some(schemaFileLocation), NoAnnotationContext, id, args: _*)
toss(sde)
}
}
trait ImplementsThrowsOrSavesSDE
extends ImplementsThrowsSDE with SavesErrorsAndWarnings {
def error(th: Diagnostic): Unit
def warn(th: Diagnostic): Unit
def SDEButContinue(id: String, args: Any*): Unit = {
ExecutionMode.requireCompilerMode
val sde = new SchemaDefinitionError(Some(schemaFileLocation), NoAnnotationContext, id, args: _*)
error(sde) // calls the error routine which records the error, but doesn't throw/toss it.
}
def SDW(id: String, args: Any*): Unit = {
ExecutionMode.requireCompilerMode
val sdw = new SchemaDefinitionWarning(Some(schemaFileLocation), NoAnnotationContext, id, args: _*)
warn(sdw)
}
/**
* Use for cases where it is an SDE because of something we've chosen
* not to implement. Not merely short term (haven't coded it yet, but intending to),
* more like things we've chosen to defer intentionally to some future release.
*/
def subset(testThatWillThrowIfFalse: Boolean, msg: String, args: Any*) = {
if (!testThatWillThrowIfFalse) subsetError(msg, args: _*)
}
def subsetError(msg: String, args: Any*) = {
val msgTxt = msg.format(args: _*)
SDE("Subset " + msgTxt)
}
}