Skip to content
Snippets Groups Projects
Schema.kt 10.12 KiB
package unibz.cs.semint.kprime.domain.ddl

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
import unibz.cs.semint.kprime.domain.ddl.schemalgo.check3NF
import unibz.cs.semint.kprime.domain.ddl.schemalgo.checkBCNF
import unibz.cs.semint.kprime.domain.ddl.schemalgo.decomposeTo3NF
import unibz.cs.semint.kprime.domain.ddl.schemalgo.decomposeToBCNF

@JacksonXmlRootElement(localName = "schema")
class Schema () {
    @JacksonXmlProperty(isAttribute = true)
    var name: String =""
    @JacksonXmlProperty(isAttribute = true)
    var id: String=""

    var tables: ArrayList<Table>? = ArrayList<Table>()

    var constraints: MutableList<Constraint>? = ArrayList<Constraint>()

    fun table(name: String): Table? {
        if (tables().isEmpty()) return null
        return tables().filter { t -> t.name==name }.firstOrNull()
    }

    fun constraints(): MutableList<Constraint> {
        if (constraints!=null) return  constraints as MutableList<Constraint>
        return ArrayList()
    }

    fun tables():ArrayList<Table> {
        if (tables!=null) return tables as ArrayList<Table>
        return ArrayList()
    }

    fun constraint(name: String): Constraint? {
        if (constraints().isEmpty()) return null
        return constraints().filter { c -> c.name==name}.firstOrNull()
    }

    fun keys(tableName: String): List<Constraint> {
        val first = constraints().filter { c ->
            c.type == Constraint.TYPE.PRIMARY_KEY.name &&
                    c.source.table == "${tableName}"
        }.toList()
        return first
    }

    fun key(tableName: String): Set<Column> {
        var resultCols = mutableSetOf<Column>()
        val keys = keys(tableName)
        if (keys.isEmpty()) return mutableSetOf()
        return keys[0].source.columns.toSet()
    }

    fun addKey(tableName:String, k:Set<Column>): Constraint {
        val primaryConstraint = Constraint()
        primaryConstraint.name="pkey_$tableName"
        primaryConstraint.source.table="$tableName"
        primaryConstraint.source.columns.addAll(k)
        primaryConstraint.target.columns.addAll(k)
        primaryConstraint.type= Constraint.TYPE.PRIMARY_KEY.name
        constraints().add(primaryConstraint)
        return primaryConstraint
    }

    fun keys(): List<Constraint> {
        return constraints().filter { c -> c.type.equals(Constraint.TYPE.PRIMARY_KEY.name) }
    }
    fun foreignKeys(): List<Constraint> {
        return constraints().filter { c -> c.type.equals(Constraint.TYPE.FOREIGN_KEY.name) }
    }

    fun foreignsTable(tableName: String): List<Constraint> {
        return foreignKeys().filter { f -> f.source.name.equals(tableName) }
    }

    fun foreignsTargets(tableName: String): List<Constraint> {
        return foreignKeys().filter { f -> f.target.name.equals(tableName) }
    }

    fun doubleIncs(): List<Constraint> {
        return constraints().filter { c -> c.type.equals(Constraint.TYPE.DOUBLE_INCLUSION.name) }
    }

    fun functionalLHS(tableName: String): Set<Column> {
        var resultCols = mutableSetOf<Column>()
        val first = constraints().filter { c ->
            c.type == Constraint.TYPE.FUNCTIONAL.name &&
                    c.name == "functional.${tableName}"
        }.toList()
        if (first.isEmpty()) return mutableSetOf()
        return first[0].source.columns.toSet()
    }

    fun functionals(): Set<Constraint> {
        var resultCols = mutableSetOf<Column>()
        return constraints().filter { c ->
            c.type == Constraint.TYPE.FUNCTIONAL.name }.toSet()
    }

    fun functionalsTable(tableName:String): List<Constraint> {
        return functionals().filter { f -> f.source.table.equals(tableName) }
    }

    fun functionalRHS(tableName: String): Set<Column> {
        var resultCols = mutableSetOf<Column>()
        val first = constraints().filter { c ->
            c.type == Constraint.TYPE.FUNCTIONAL.name &&
                    c.name == "functional.${tableName}"
        }.toList()
        if (first.isEmpty()) return mutableSetOf()
        return first[0].target.columns.toSet()
    }

    internal fun addFunctional(tableName:String, lhs:Set<Column>, rhs:Set<Column>){
        val functionalConstraint = Constraint()
        functionalConstraint.name="functional.$tableName"
        functionalConstraint.source.table="$tableName"
        functionalConstraint.source.columns.addAll(lhs)
        functionalConstraint.target.table="$tableName"
        functionalConstraint.target.columns.addAll(rhs)
        functionalConstraint.type= Constraint.TYPE.FUNCTIONAL.name
        constraints().add(functionalConstraint)

    }

    fun addFunctional(commandArgs:String): Schema {
        val tableName:String = commandArgs.split(":")[0]
        val setExpression: String= commandArgs.split(":")[1]
        return addFunctional(tableName,setExpression)
    }

    fun addTable(commandArgs:String) : Schema {
        val table = SchemaCmdParser.parseTable(commandArgs)
        tables().add(table)
        return this
    }
    fun dropTable(commandArgs:String) : Schema {
        var tableNames = commandArgs.split(" ")
        for (tableName  in tableNames) {
            val t= table(tableName)
            if (t!=null){ tables().remove(t)}
            val toRemove = mutableListOf<Constraint>()
            for (constr in constraints()) {
                if (constr.source.table.equals(tableName))
                    toRemove.add(constr)
                if (constr.target.table.equals(tableName))
                    toRemove.add(constr)
            }
            constraints().removeAll(toRemove)
        }
        return this
    }

    fun dropConstraint(commnadArgs:String) : Schema {
        var constraintNames = commnadArgs.split(" ")
        for (constraintName in constraintNames) {
            var constraint = constraint(constraintName)
            constraints().remove(constraint)
        }
        return this
    }

    fun addFunctional(tableName:String, setExpression: String): Schema {
        val constraintsToAdd = Constraint.set(setExpression)
        for (constraint in constraintsToAdd) {
            constraint.name=tableName+".functional"
            constraint.type=Constraint.TYPE.FUNCTIONAL.name
            constraint.source.table=tableName
            constraint.target.table=tableName
        }
        constraints().addAll(constraintsToAdd)
        return this
    }

    fun addKey(commandArgs:String):Schema {
        val tableName:String = commandArgs.split(":")[0]
        val attributeNames = commandArgs.split(":")[1]
        val constraint = Constraint.addKey {}
        constraint.name = tableName+".primaryKey"
        constraint.source.table=tableName
        constraint.target.table=tableName
        constraint.source.columns.addAll(Column.set(attributeNames))
        constraint.target.columns.addAll(Column.set(attributeNames))
        constraints().add(constraint)
        return this
    }

    fun addForeignKey(commandArgs:String):Schema {
        val source:String = commandArgs.split("-->")[0]
        val target:String = commandArgs.split("-->")[1]

        val sourceTableName:String = source.split(":")[0]
        val sourceAttributeNames = source.split(":")[1]

        val targetTableName:String = target.split(":")[0]
        val targetAttributeNames = target.split(":")[1]

        val constraint = Constraint.foreignkey {}
        constraint.name = "${sourceTableName}_${targetTableName}.foreignKey"
        constraint.source.table=sourceTableName
        constraint.target.table=targetTableName
        constraint.source.columns.addAll(Column.set(sourceAttributeNames))
        constraint.target.columns.addAll(Column.set(targetAttributeNames))
        constraints().add(constraint)
        return this
    }

    fun addDoubleInc(commandArgs:String):Schema {
        val source:String = commandArgs.split("-->")[0]
        val target:String = commandArgs.split("-->")[1]

        val sourceTableName:String = source.split(":")[0]
        val sourceAttributeNames = source.split(":")[1]

        val targetTableName:String = target.split(":")[0]
        val targetAttributeNames = target.split(":")[1]

        val constraint = Constraint.doubleInclusion {}
        constraint.name = "${sourceTableName}_${targetTableName}.doubleInc"
        constraint.source.table=sourceTableName
        constraint.target.table=targetTableName
        constraint.source.columns.addAll(Column.set(sourceAttributeNames))
        constraint.target.columns.addAll(Column.set(targetAttributeNames))
        constraints().add(constraint)
        return this
    }

    fun decomposeBCNF(): Set<Relation> {
        var allDecomposed = mutableSetOf<Relation>()
        var tables = tables()
        for ( table in tables) {
            var fds = functionalsTable(table.name)
            allDecomposed.addAll(decomposeToBCNF(table.columns.toSet(), fds.toSet()))
        }
        return allDecomposed
    }

    fun decompose3NF(): Set<Relation> {
        var allDecomposed = mutableSetOf<Relation>()
        var tables = tables()
        for ( table in tables) {
            var fds = functionalsTable(table.name)
            allDecomposed.addAll(decomposeTo3NF(table.columns.toSet(), fds.toSet()))
        }
        return allDecomposed
    }

    fun violations3NF(): Set<Constraint> {
        var allViolations = mutableSetOf<Constraint>()
        var tables = tables()
        for ( table in tables) {
            var fds = functionalsTable(table.name)
            allViolations.addAll(check3NF(table.columns.toSet(), fds.toSet()))
        }
        return allViolations

    }

    fun violationsBCNF(): Set<Constraint> {
        var allViolations = mutableSetOf<Constraint>()
        var tables = tables()
        for ( table in tables) {
            var fds = functionalsTable(table.name)
            allViolations.addAll(checkBCNF(table.columns.toSet(), fds.toSet()))
        }
        return allViolations

    }

    /**
     * TODO weak if has primarykey with-a column used as foreignkey.
    fun isTableWeak(tableName:String):Boolean {
        val key = key(tableName)
        for (keycol in key) {
            foreignKeys().forEach{fkey -> fkey.target.columns. }
        }
        return false
    }
     */

}