blob: 5ddd0a32b96b153bfa27d2aaf30b31418e8d97fe [file] [log] [blame]
package edu.illinois.ncsa.daffodil.compiler
/* Copyright (c) 2012-2013 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.
*/
import java.io.File
import scala.xml.Node
import edu.illinois.ncsa.daffodil.ExecutionMode
import edu.illinois.ncsa.daffodil.api.DFDL
import edu.illinois.ncsa.daffodil.dsom.GlobalElementDecl
import edu.illinois.ncsa.daffodil.dsom.ImplementsThrowsSDE
import edu.illinois.ncsa.daffodil.dsom.SchemaComponentBase
import edu.illinois.ncsa.daffodil.dsom.SchemaSet
import edu.illinois.ncsa.daffodil.dsom.oolag.OOLAG
import edu.illinois.ncsa.daffodil.dsom.Fakes
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.processors.DataProcessor
import edu.illinois.ncsa.daffodil.processors.PrimitiveFactory
import edu.illinois.ncsa.daffodil.util.Compile
import edu.illinois.ncsa.daffodil.util.Error
import edu.illinois.ncsa.daffodil.util.Info
import edu.illinois.ncsa.daffodil.util.Logging
import edu.illinois.ncsa.daffodil.xml.NS
import edu.illinois.ncsa.daffodil.xml.XMLUtils
import edu.illinois.ncsa.daffodil.api.DFDL
import edu.illinois.ncsa.daffodil.externalvars.Binding
class ProcessorFactory(val sset: SchemaSet)
extends SchemaComponentBase(<pf/>, sset)
with ImplementsThrowsSDE
with DFDL.ProcessorFactory
with HavingRootSpec {
requiredEvaluations(rootElem, sset)
// println("Creating Processor Factory")
lazy val rootElem = rootElem_.value
private val rootElem_ = LV('rootElem) {
sset.rootElement(rootSpec)
}
override def isError = {
ExecutionMode.usingCompilerMode {
OOLAG.keepGoing(true) {
val valid = sset.isValid
val res = if (valid) sset.isError
else true
res
}
}
}
override def diagnostics = sset.diagnostics
override lazy val fileName = sset.fileName
def onPath(xpath: String): DFDL.DataProcessor = {
ExecutionMode.usingCompilerMode {
Assert.usage(canProceed)
if (xpath != "/") rootElem.notYetImplemented("""Path must be "/". Other path support is not yet implemented.""")
val dataProc = new DataProcessor(this, rootElem)
if (dataProc.isError) {
val diags = dataProc.getDiagnostics
log(Error("Compilation (DataProcessor) reports %s compile errors/warnings.", diags.length))
diags.foreach { diag => log(Error(diag.toString())) }
} else {
log(Compile("Parser = %s.", dataProc.parser.toString))
log(Compile("Unparser = %s.", dataProc.unparser.toString))
log(Compile("Compilation (DataProcesor) completed with no errors."))
}
dataProc
}
}
}
/**
* Both Compiler and ProcessorFactory share this same API call.
*/
trait HavingRootSpec extends Logging {
var rootSpec: Option[RootSpec] = None
def setDistinguishedRootNode(name: String, namespace: String): Unit = {
val ns =
if (namespace != null) Some(NS(namespace))
else None
rootSpec = Some(RootSpec(ns, name))
// log(Info("%s setDistinguishedRootNode to %s", Misc.getNameFromClass(this), rootSpec))
//
// null means we search for the namespace
// Must be only one answer.
//
}
}
class Compiler extends DFDL.Compiler with Logging with HavingRootSpec {
def setExternalDFDLVariable(name: String, namespace: String, value: String): Unit = {
Assert.notYetImplemented()
}
def setExternalDFDLVariables(source: Any): Unit = {
// Chicken before egg? Don't we need the VariableMap
// in order to set the external variables?
//
// But the VariableMap isn't constructed until after
// the creation of the SchemaSet.
Assert.notYetImplemented
}
/**
* Controls whether we check everything in the schema, or just the element
* we care about (and everything reachable from it.)
*
* You need this control, since many of the big TDML test files have many things
* in them, some of which use unimplemented features. Each time we run exactly one
* test from the set, we want to ignore errors in compilation of the others.
*/
private var checkAllTopLevel = false
def setCheckAllTopLevel(flag: Boolean) {
checkAllTopLevel = flag
}
/*
* for unit testing of front end
*/
def frontEnd(xml: Node, extVarsSrc: Seq[Binding] = Seq.empty): (SchemaSet, GlobalElementDecl) = {
val (sset, pf) = compileInternal(extVarsSrc, xml)
val ge = pf.rootElem
(sset, ge)
}
def reload(fileNameOfSavedParser: String): DFDL.ProcessorFactory = {
Assert.notYetImplemented()
}
/**
* Compilation works entirely off of schema files because that allows XMLCatalogs
* to work for Xerces without (much) pain.
*
* This method exposes both the schema set and processor factory as results because
* our tests often want to do things on the schema set.
*/
def compileInternal(externalVariablesSource: Seq[Binding] = Seq.empty, schemaFiles: Seq[File]): (SchemaSet, ProcessorFactory) = {
ExecutionMode.usingCompilerMode {
Assert.usage(schemaFiles.length >= 1)
val filesNotFound = schemaFiles.map { f => (f.exists(), f.getPath()) }.filter { case (exists, _) => !exists }.map { case (_, name) => name }
if (filesNotFound.length > 0) throw new java.io.FileNotFoundException("Failed to find the following file(s): " + filesNotFound.mkString(", "))
val sset = new SchemaSet(externalVariablesSource, PrimitiveFactory, schemaFiles, rootSpec, checkAllTopLevel)
val pf = new ProcessorFactory(sset)
val err = pf.isError
val diags = pf.getDiagnostics // might be warnings even if not isError
def printDiags = diags.foreach { diag =>
val msg = diag.toString()
log(Error(msg))
}
if (err) {
Assert.invariant(diags.length > 0)
log(Error("Compilation (ProcessorFactory) produced %d errors/warnings.", diags.length))
} else {
if (diags.length > 0) {
log(Info("Compilation (ProcessorFactory) produced %d warnings.", diags.length))
} else {
log(Compile("ProcessorFactory completed with no errors."))
}
}
printDiags
(sset, pf)
}
}
/**
* Just hides the schema set, and returns the processor factory only.
*/
def compile(extVarsSrc: Seq[Binding], files: File*): DFDL.ProcessorFactory = compileInternal(extVarsSrc, files)._2
//def compile(fNames: String*): DFDL.ProcessorFactory = compileInternal(fNames)._2
/**
* For convenient unit testing allow a literal XML node.
*/
def compile(extVarsSrc: Seq[Binding] = Seq.empty, xml: Node) = {
compileInternal(extVarsSrc, xml)._2
}
def compileInternal(extVarsSrc: Seq[Binding], xml: Node): (SchemaSet, ProcessorFactory) = {
val tempSchemaFile = XMLUtils.convertNodeToTempFile(xml)
compileInternal(extVarsSrc, List(tempSchemaFile))
}
}
/**
* Factory for Compiler instances
*/
object Compiler {
def apply() = new Compiler()
}