Skip to content
Snippets Groups Projects
XPathTransformUseCase.kt 9.79 KiB
package unibz.cs.semint.kprime.usecase.common

import freemarker.cache.ClassTemplateLoader
import freemarker.template.Configuration
import freemarker.template.Template
import org.w3c.dom.NodeList
import unibz.cs.semint.kprime.adapter.service.XMLSerializerJacksonAdapter
import unibz.cs.semint.kprime.domain.ddl.Database
import unibz.cs.semint.kprime.domain.dml.ChangeSet
import unibz.cs.semint.kprime.usecase.service.FileIOService
import java.io.File
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory

class XPathTransformUseCase  {

    fun transform(
            dbFilePath: String,
            trasformerName: String,
            trasformerDirection: String,
            trasformerVersion: String,
            tranformerParmeters: MutableMap<String, Any>)
            :Database {
        val vdecomposeFilePath = "/transformer/${trasformerName}/${trasformerDirection}/${trasformerName}_${trasformerDirection}_${trasformerVersion}.paths"
        val vdecomposeTemplatePath = "transformer/${trasformerName}/${trasformerDirection}/${trasformerName}_${trasformerDirection}_${trasformerVersion}.template"
        val xPaths = File(vdecomposeFilePath).readLines()
        return transform(dbFilePath,vdecomposeTemplatePath,xPaths, tranformerParmeters)
    }

    /*
    It uses 'changeset' template
     */
    fun compute(
            dbFilePath: String,
            trasformerName: String,
            trasformerDirection: String,
            trasformerVersion: String,
            tranformerParmeters: MutableMap<String, Any>)
            :ChangeSet {
        val vdecomposeFilePath = "/transformer/${trasformerName}/${trasformerDirection}/${trasformerName}_${trasformerDirection}_${trasformerVersion}.paths"
        val vdecomposeTemplatePath = "transformer/${trasformerName}/${trasformerDirection}/${trasformerName}_changeset_${trasformerVersion}.template"
        val xPaths = File(vdecomposeFilePath).readLines()
        return compute(dbFilePath, vdecomposeTemplatePath, xPaths, tranformerParmeters)
    }

    fun transform(
            dbFilePath: String,
            templateFilePath: String,
            xPaths: List<String>,
            tranformerParmeters:
            MutableMap<String, Any>)
            : Database {
        val changeSet = compute(dbFilePath, templateFilePath, xPaths, tranformerParmeters)

        //val dbXml = XPathTransformUseCase::class.java.getResource("/${dbFilePath}").readText()
        val dbXml = FileIOService.readString(FileIOService.inputStreamFromPath(dbFilePath))
        val serializer = XMLSerializerJacksonAdapter()
        val db = serializer.deserializeDatabase(dbXml)
        val newdb = ApplyChangeSetUseCase(serializer).apply(db, changeSet)

        println("-----------------------NEW-DB---------------")
        //println(serializer.prettyDatabase(newdb))
        return newdb
    }

    private fun parametrized(line: String, tranformerParmeters: MutableMap<String, Any>): String {
        var newline = line
        for (key in tranformerParmeters.keys) {
            val newValue = (tranformerParmeters[key] as List<String>).get(0)
            newline = newline.replace("%%${key}%%", newValue)
        }
        return newline
    }


    private fun computeDerivedList(templModel: MutableMap<String, List<String>>, derivationRule: String): List<String> {
        var derivedList = mutableListOf<String>()
        // if derivationRule starts with + then compute union
        val splittedRule = derivationRule.split(" ")
        if (splittedRule[0]=="+") {
            val sourceLists = splittedRule.drop(1)
            //println("sourceLists:"+sourceLists)
            derivedList.addAll(templModel[sourceLists[0]] as List<String>)
            for (i in 1..(sourceLists.size-1)) {
                if (!templModel[sourceLists[i]]!!.isEmpty())
                    derivedList = derivedList.plus(templModel[sourceLists[i]] as MutableList<String>) as MutableList<String>
            }
            //println(derivedList)
        }
        if (splittedRule[0]=="-") {
            //println(splittedRule)
            val sourceLists = splittedRule.drop(1)
            //println("sourceLists:"+sourceLists)
            derivedList.addAll(templModel[sourceLists[0]] as List<String>)
            for (i in 1..(sourceLists.size-1)) {
                if (!templModel[sourceLists[i]]!!.isEmpty())
                    derivedList = derivedList.minus(templModel[sourceLists[i]] as MutableList<String>) as MutableList<String>
            }
            //println(" $derivedList")
        }
        // if derivationRule starts with - then compute intersection
        return derivedList.toSet().toList()
    }

    private fun asValueList(xpathResultNodes: NodeList): MutableList<String> {
        val listNodeValues = mutableListOf<String>()
        for (nodeId in 0..xpathResultNodes.length) {
            val item = xpathResultNodes.item(nodeId)
            if (item==null) continue
            listNodeValues.add(item.nodeValue)
        }
        return listNodeValues
    }

    fun compute(
            dbFilePath: String,
            templateFilePath: String,
            xPaths: List<String>,
            tranformerParmeters: MutableMap<String, Any>)
            : ChangeSet {

        val pair = getTemplateModel(dbFilePath, xPaths, tranformerParmeters)
        val templModel = pair.first
        var violation = pair.second

        if (!violation.isEmpty()) {
            println("Condition Failure")
            return ChangeSet()
        }

        println("22++++++++++++++++++++++++++++++++++++++++++-------------------------------")

        val templConfig = Configuration(Configuration.VERSION_2_3_29)
        val classTemplLoader = ClassTemplateLoader(XPathTransformUseCase::javaClass.javaClass, "/")
        templConfig.templateLoader = classTemplLoader

        lateinit var templ : Template
        if (templateFilePath.startsWith("/")||
                templateFilePath.startsWith("./")) {
            templ = Template.getPlainTextTemplate("template1",
                    File(templateFilePath).readText(Charsets.UTF_8), templConfig)
            val lastSlash = templateFilePath.lastIndexOf("/")
            val templateDir = templateFilePath.substring(0,lastSlash)
            val templateFileName = templateFilePath.substring(lastSlash)
            //templConfig.setDirectoryForTemplateLoading(File("/home/nipe/Temp/kprime/transformers/vertical/decompose/"))
            //templ = templConfig.getTemplate("vertical_decompose_1_changeset.xml")
            println("${templateDir}:${templateFileName}")
            templConfig.setDirectoryForTemplateLoading(File(templateDir))
            templ = templConfig.getTemplate(templateFileName)
        } else {
            templ = templConfig.getTemplate(templateFilePath)
        }
        val outWriter = StringWriter()
        templ.process(templModel, outWriter)

        println("33++++++++++++++++++++++++++++++++++++++++++-------------------------------")
        val changeSetXml = outWriter.buffer.toString()
        println(changeSetXml)
        println("44++++++++++++++++++++++++++++++++++++++++++-------------------------------")
        val serializer = XMLSerializerJacksonAdapter()
        val changeSet = XMLSerializeUseCase(serializer).deserializeChangeSet(changeSetXml).ok ?: ChangeSet()
        println("55++++++++++++++++++++++++++++++++++++++++++-------------------------------")

        return changeSet
    }

    fun getTemplateModel(dbFilePath: String, xPaths: List<String>, tranformerParameters: MutableMap<String, Any>): Pair<MutableMap<String, List<String>>, String> {
        val templModel = mutableMapOf<String, List<String>>()

        // compute xpath lists
        //val dbInputStream: InputStream = FileIOService.inputStreamFromPath(dbFilePath)
        val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
        //println("dbFilePath: ${dbFilePath}")
        val doc = docBuilder.parse(File(dbFilePath))
        val xpath = XPathFactory.newInstance().newXPath()
        var violation = ""
        for (xPathLine in xPaths) {
            //println("------------------------------------------")
            val xPathTokens = xPathLine.split("==")
            val name = xPathTokens[0]
            //println("name=|${name}|")
            val rule = xPathTokens[1]
            //println("rule=|${rule}|")
            val pathTokens = rule.split(" ")
            val value = parametrized(pathTokens[0], tranformerParameters)
            //println("value=|${value}|")
            if (value.startsWith("-") || value.startsWith("+")) {
                templModel[name] = computeDerivedList(templModel, rule)
            }
            else {
                templModel[name] = asValueList(xpath.compile(value).evaluate(doc, XPathConstants.NODESET) as NodeList)
                //println(" ${name} = ${value}")
                //println(" ${name} = ${templModel[name]}")
                if (!templModel[name]!!.isEmpty())
                tranformerParameters[name] = templModel[name]!!
                if (pathTokens.size == 3) {
                    //println(pathTokens)
                    val pathCondition = pathTokens[1]
                    val pathSize = pathTokens[2].toInt()
                    if (pathCondition == ">")
                        if ((templModel[name])!!.size <= pathSize) violation = "violation: ${name}:${rule} ${templModel[name]} size <= ${pathSize}"
                    if (pathCondition == "=")
                        if ((templModel[name])!!.size != pathSize) violation = "violation: ${name}:${rule} ${templModel[name]} size != ${pathSize}"
                }
            }
        }
        // adds all input parameters as template parameters
        for (parCouple in tranformerParameters) {
            templModel.put(parCouple.key, listOf(parCouple.value.toString()))
        }
        return Pair(templModel, violation)
    }
}