From 8c7a4a73ed2f9a2cbcca9f0a923e751753ae7954 Mon Sep 17 00:00:00 2001
From: npedot <nicola.pedot@gmail.com>
Date: Thu, 23 Jan 2020 08:33:25 +0100
Subject: [PATCH] adds person xpath scenario

---
 README.md                                     |  9 +++-
 pom.xml                                       |  6 +++
 .../domain/Constraint.kt                      |  1 +
 .../usecase/HJoinUseCase.kt                   |  3 ++
 .../usecase/VSplitUseCase.kt                  | 39 +++++++++------
 .../scenario/PersonVSplitScenarioTI.kt        |  6 +++
 .../scenario/PersonXPathScenarioTI.kt         | 39 +++++++++++++++
 src/test/resources/db/person.xml              | 38 ++++++++++++++
 src/test/resources/db/person_out.template     | 49 +++++++++++++++++++
 9 files changed, 174 insertions(+), 16 deletions(-)
 create mode 100644 src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt
 create mode 100644 src/test/resources/db/person.xml
 create mode 100644 src/test/resources/db/person_out.template

diff --git a/README.md b/README.md
index 5f18680..b5ea283 100644
--- a/README.md
+++ b/README.md
@@ -73,4 +73,11 @@ technology depenent packages
 * schema clone, builder, immutable
 * schema pattern matcher
 * schema variable extrator
-* sql view generator
\ No newline at end of file
+* sql view generator
+
+## references
+
+https://www.baeldung.com/java-xpath
+https://freemarker.apache.org/
+https://github.com/ostap/relations-java
+https://github.com/JSQLParser/JSqlParser/wiki/Examples-of-SQL-parsing
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0221a52..f4ea53e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,12 @@
             <artifactId>postgresql</artifactId>
             <version>42.2.8</version>
         </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.29</version>
+        </dependency>
+
 
 
         <!-- test -->
diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt
index 02efb4e..c136be4 100644
--- a/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt
+++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
 
 @JacksonXmlRootElement(localName = "constraint")
 class Constraint () {
+
     enum class TYPE {
         FOREIGN_KEY,PRIMARY_KEY,FUNCTIONAL,DOUBLE_INCLUSION,INCLUSION,DISJUNCTION,COVER
     }
diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt
index 0cbb420..a17d9d3 100644
--- a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt
+++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt
@@ -1,2 +1,5 @@
 package unibz.cs.semint.kprime.usecase
 
+class HJoinUseCase {
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt
index 9bdd18e..cacf67c 100644
--- a/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt
+++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt
@@ -5,25 +5,33 @@ import unibz.cs.semint.kprime.domain.*
 class VSplitUseCase {
 
     fun compute(metadataDatabase: Database): ChangeSet {
-        // check for functional dep
         // create changeset
         var changeSet = ChangeSet()
 
+        // precondition: check for first table with functional dep, get the name
+        var tableWithFunctionalName= metadataDatabase.schema.constraints
+                .filter { c -> c.type==Constraint.TYPE.FUNCTIONAL.name }
+                .map { c -> c.source.table }.first()
+        if (tableWithFunctionalName.isEmpty()) return changeSet
+
         // compute K
-        val keyCols = metadataDatabase.schema.key("person")
+        val keyCols = metadataDatabase.schema.key(tableWithFunctionalName)
         var key = keyCols.map { x -> x.name }.toSet()
         println("key $key")
+
         // compute LHS
-        var lhsCols = metadataDatabase.schema.functionalLHS("person")
+        var lhsCols = metadataDatabase.schema.functionalLHS(tableWithFunctionalName)
                 var lhs= lhsCols.map { x -> x.name }.toSet()
         println("lhs $lhs")
         if (lhs.isEmpty()) return changeSet
+
         // compute RHS
-        val rhsCols = metadataDatabase.schema.functionalRHS("person")
+        val rhsCols = metadataDatabase.schema.functionalRHS(tableWithFunctionalName)
         var rhs = rhsCols.map { x -> x.name }.toSet()
         println("rhs $rhs")
+
         // compute Rest
-        val allCols = metadataDatabase.schema.table("person").columns.toSet()
+        val allCols = metadataDatabase.schema.table(tableWithFunctionalName).columns.toSet()
         val all = allCols.map { x -> x.name }.toSet()
         var rest = all.minus(key).minus(lhs).minus(rhs)
         val allNotKey = all.minus(key)
@@ -32,25 +40,26 @@ class VSplitUseCase {
         println("rest $rest")
 
         // create view1 = select K,LHS,Rest
-        var view1cols = "select "+key.plus(lhs).plus(rest).joinToString()+" from person"
+        var view1cols = "select "+key.plus(lhs).plus(rest).joinToString()+" from $tableWithFunctionalName"
         val view1 = CreateView()
         view1.viewName="tableName1"
         view1.text=view1cols
         changeSet.createView.add(view1)
+
         // create view2 = select LHS,RHS
-        var view2cols = "select "+lhs.plus(rhs).joinToString()+" from person"
+        var view2cols = "select "+lhs.plus(rhs).joinToString()+" from $tableWithFunctionalName"
         val view2 = CreateView()
         view2.viewName="tableName2"
         view2.text=view2cols
         changeSet.createView.add(view2)
-        // create inclusion constraint tab1 tab2
-        val inclusionTab1Tab2 = Constraint()
-        inclusionTab1Tab2.type=Constraint.TYPE.INCLUSION.name
-        inclusionTab1Tab2.source.table="tableName1"
-        inclusionTab1Tab2.source.columns.addAll(lhsCols)
-        inclusionTab1Tab2.target.table="tableName2"
-        inclusionTab1Tab2.target.columns.addAll(lhsCols)
-        changeSet.createConstraint.add(inclusionTab1Tab2)
+
+        // create key constraint tab2
+        val keyTab2 = Constraint()
+        keyTab2.type=Constraint.TYPE.PRIMARY_KEY.name
+        keyTab2.source.table="tableName2"
+        keyTab2.source.columns.addAll(lhsCols)
+        changeSet.createConstraint.add(keyTab2)
+
         // create inclusion constraint tab2 tab1
         val inclusionTab2Tab1 = Constraint()
         inclusionTab2Tab1.type=Constraint.TYPE.DOUBLE_INCLUSION.name
diff --git a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt
index 86dd0e8..70911ea 100644
--- a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt
+++ b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt
@@ -51,4 +51,10 @@ class PersonVSplitScenarioTI {
         // prints changeset
         println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyChangeSet(changeSet))
     }
+
+    @Test
+    fun test_print_input_db() {
+        val db = buildPersonMetadata()
+        println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyDatabase(db).ok)
+    }
 }
\ No newline at end of file
diff --git a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt
new file mode 100644
index 0000000..ec92474
--- /dev/null
+++ b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt
@@ -0,0 +1,39 @@
+package unibz.cs.semint.kprime.scenario
+
+import freemarker.cache.ClassTemplateLoader
+import freemarker.template.Configuration
+import freemarker.template.Template
+import org.junit.Test
+import java.io.OutputStreamWriter
+
+class PersonXPathScenarioTI {
+
+    @Test
+    fun test_xpath_extraction_on_person_db() {
+        // given
+        // input person db
+        val personDbXml = PersonXPathScenarioTI::class.java.getResource("/db/person.xml").readText()
+        //println(personDbXml)
+        // input person out template
+        val personTemplate = PersonXPathScenarioTI::class.java.getResource("/db/person_out.template").readText()
+        //println(personTemplate)
+        // extract vars as value attributes via xpaths
+        val keys = listOf<String>("SSN")
+        val lhss = listOf<String>("T")
+        val rhss = listOf<String>("S")
+        val rests = listOf<String>("X")
+        // use vars in template
+        val templConfig = Configuration(Configuration.VERSION_2_3_29)
+        val classTemplLoader = ClassTemplateLoader(PersonXPathScenarioTI::javaClass.javaClass,"/")
+        templConfig.templateLoader= classTemplLoader
+        val templModel = mapOf<String,Any>(
+                "keys" to keys,
+                "lhss" to lhss,
+                "rhss" to rhss,
+                "rests" to rests)
+        val templ = //Template.getPlainTextTemplate("templ1",personTemplate,templConfig)
+                templConfig.getTemplate("db/person_out.template")
+        val out = OutputStreamWriter(System.out)
+        templ.process(templModel,out)
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/db/person.xml b/src/test/resources/db/person.xml
new file mode 100644
index 0000000..40ddf5e
--- /dev/null
+++ b/src/test/resources/db/person.xml
@@ -0,0 +1,38 @@
+<database name="" id="">
+    <schema name="" id="">
+        <tables>
+            <tables name="person" id="" view="" condition="">
+                <columns>
+                    <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/>
+                    <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/>
+                    <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/>
+                    <columns name="X" id="id.X" dbname="dbname.X" nullable="true" dbtype=""/>
+                </columns>
+            </tables>
+        </tables>
+        <constraints>
+            <constraints name="primaryKey.person" id="" type="PRIMARY_KEY">
+                <source name="" id="" table="person">
+                    <columns>
+                        <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/>
+                    </columns>
+                </source>
+                <target name="" id="" table="">
+                    <columns/>
+                </target>
+            </constraints>
+            <constraints name="functional.person" id="" type="FUNCTIONAL">
+                <source name="" id="" table="person">
+                    <columns>
+                        <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/>
+                    </columns>
+                </source>
+                <target name="" id="" table="person">
+                    <columns>
+                        <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/>
+                    </columns>
+                </target>
+            </constraints>
+        </constraints>
+    </schema>
+</database>
diff --git a/src/test/resources/db/person_out.template b/src/test/resources/db/person_out.template
new file mode 100644
index 0000000..7468553
--- /dev/null
+++ b/src/test/resources/db/person_out.template
@@ -0,0 +1,49 @@
+<database name="" id="">
+    <schema name="" id="">
+        <tables>
+            <tables name="person1" id="" view="" condition="">
+                <columns>
+                    <#list keys as key>
+                    <columns name=${key} id="id.${key}" nullable="false" dbtype=""/>
+                    </#list>
+                    <#list lhss as lhs>
+                    <columns name="${lhs}" id="id.${lhs}" nullable="false" dbtype=""/>
+                    </#list>
+                    <#list rests as rest>
+                    <columns name="${rest}" id="id.${rest}" nullable="true" dbtype=""/>
+                    </#list>
+                </columns>
+            </tables>
+            <tables name="person2" id="" view="" condition="">
+                <columns>
+                    <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/>
+                    <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/>
+                </columns>
+            </tables>
+        </tables>
+        <constraints>
+            <constraints name="primaryKey.person" id="" type="PRIMARY_KEY">
+                <source name="" id="" table="person">
+                    <columns>
+                        <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/>
+                    </columns>
+                </source>
+                <target name="" id="" table="">
+                    <columns/>
+                </target>
+            </constraints>
+            <constraints name="functional.person" id="" type="FUNCTIONAL">
+                <source name="" id="" table="person">
+                    <columns>
+                        <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/>
+                    </columns>
+                </source>
+                <target name="" id="" table="person">
+                    <columns>
+                        <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/>
+                    </columns>
+                </target>
+            </constraints>
+        </constraints>
+    </schema>
+</database>
-- 
GitLab