diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/DefaultMergeCellMigratorTest.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/DefaultMergeCellMigratorTest.groovy index e4f4ac5a1d..9416cd71bd 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/DefaultMergeCellMigratorTest.groovy +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/DefaultMergeCellMigratorTest.groovy @@ -446,6 +446,26 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { assertEquals(expectedFilter, filter.filterTerm) } + @CompileStatic(TypeCheckingMode.SKIP) + private void typeFilterCheck(Cell migrated, String expectedFilter) { + JaxbAlignmentIO.printCell(migrated, System.out) + + // the condition should be present on the source + def source = CellUtil.getFirstEntity(migrated.source).definition + assertNotNull(source.filter) + assertEquals(expectedFilter, source.filter.filterTerm) + } + + @CompileStatic(TypeCheckingMode.SKIP) + private void filterCheckNull(Cell migrated) { + JaxbAlignmentIO.printCell(migrated, System.out) + + // the condition should be present on the source + def source = CellUtil.getFirstEntity(migrated.source).definition + def filter = source.propertyPath.empty ? source.filter : source.propertyPath[0].condition?.filter + assertNull(filter) + } + @Test void testTypeFilter1() { def toMigrate = this.class.getResource('/testcases/type-filter/B-to-C.halex') @@ -481,7 +501,33 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { // filter assertEquals(1, migrated.size()) - filterCheck(migrated[0], "bb = 'test'") // the filter should be retained + // filter is now dropped (because bb is not mapped for B2) + filterCheckNull(migrated[0]) + + // there should be a message about the condition + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('removed because no matches') + }) + } + + @Test + void testTypeFilter2Present() { + def toMigrate = this.class.getResource('/testcases/type-filter-props-mapped/B-to-C.halex') + def cellId = 'B2-C2' // Retype + + def matching = this.class.getResource('/testcases/type-filter-props-mapped/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + filterCheck(migrated[0], "ab = 'test'") // there should be a message about the condition def messages = getMigrationMessages(migrated[0]) @@ -491,7 +537,6 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { } @Test - @CompileStatic(TypeCheckingMode.SKIP) void testTypeFilter2Property() { def toMigrate = this.class.getResource('/testcases/type-filter/B-to-C.halex') def cellId = 'B2C2a' // Rename @@ -502,11 +547,34 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { // do checks - // type filter should be retained - def source = CellUtil.getFirstEntity(migrated[0].source).definition - def filter = source.filter - assertNotNull(filter) - assertEquals("bb = 'test'", filter.filterTerm) + // filter is now dropped (because bb is not mapped for B2) + assertEquals(1, migrated.size()) + filterCheckNull(migrated[0]) + + // there should be a message about the condition + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('removed because no matches') + }) + } + + @Test + void testTypeFilter2PropertyPresent() { + def toMigrate = this.class.getResource('/testcases/type-filter-props-mapped/B-to-C.halex') + def cellId = 'B2C2a' // Rename + + def matching = this.class.getResource('/testcases/type-filter-props-mapped/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + typeFilterCheck(migrated[0], "ab = 'test'") // there should be a message about the condition def messages = getMigrationMessages(migrated[0]) @@ -526,9 +594,34 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { // do checks + // filter is now dropped (because bc is not mapped for B3) + assertEquals(1, migrated.size()) + filterCheckNull(migrated[0]) + + // there should be a message about the condition + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('removed because no matches') + }) + } + + @Test + void testTypeFilter3Present() { + def toMigrate = this.class.getResource('/testcases/type-filter-props-mapped/B-to-C.halex') + def cellId = 'B3-C3' // Merge + + def matching = this.class.getResource('/testcases/type-filter-props-mapped/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + // filter assertEquals(1, migrated.size()) - filterCheck(migrated[0], "bc = 'test'") + filterCheck(migrated[0], "ac = 'test'") // there should be a message about the condition def messages = getMigrationMessages(migrated[0]) @@ -572,6 +665,46 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { // do checks + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + // expect filters to be present on A5 source + assertNotNull(migrated[0].source) + assertEquals(2, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.definition.name.localPart == 'A5') { + // filter is now dropped (because ba and bb are not mapped for B5) + assertNull(filter) + } + else { + assertNull(filter) + } + } + + // there should be a message about the conditions being dropped + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('removed because no matches') + }) + } + + @CompileStatic(TypeCheckingMode.SKIP) + @Test + void testTypeFilter5Present() { + def toMigrate = this.class.getResource('/testcases/type-filter-props-mapped/B-to-C.halex') + def cellId = 'Join' // Join + + def matching = this.class.getResource('/testcases/type-filter-props-mapped/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + // filter assertEquals(1, migrated.size()) JaxbAlignmentIO.printCell(migrated[0], System.out) @@ -583,7 +716,8 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { def filter = e.definition.filter if (e.definition.definition.name.localPart == 'A5') { assertNotNull(filter) - assertEquals('ba = \'test\' AND NOT (bb = \'test\')', filter.filterTerm) + assertEquals('(aa = \'test\' AND NOT (ab = \'test\'))', filter.filterTerm) + // assertEquals('aa = \'test\' and ab <> \'test\'', filter.filterTerm) } else { assertNull(filter) @@ -608,6 +742,39 @@ class DefaultMergeCellMigratorTest extends AbstractMergeCellMigratorTest { // do checks + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + // expect filters to be present on both sources + assertNotNull(migrated[0].source) + assertEquals(2, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + assertNull(filter) + } + + // there should be a message about the conditions being dropped + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('removed because no matches') + }) + } + + @Test + void testTypeFilter6Present() { + def toMigrate = this.class.getResource('/testcases/type-filter-props-mapped/B-to-C.halex') + def cellId = 'GJoin' // Groovy Join + + def matching = this.class.getResource('/testcases/type-filter-props-mapped/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + // filter assertEquals(1, migrated.size()) JaxbAlignmentIO.printCell(migrated[0], System.out) diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/JoinRetainConditionsTest.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/JoinRetainConditionsTest.groovy new file mode 100644 index 0000000000..efb6e2ab7d --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/src/eu/esdihumboldt/hale/common/align/merge/test/impl/JoinRetainConditionsTest.groovy @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2024 wetransform GmbH + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution. If not, see . + * + * Contributors: + * wetransform GmbH + */ + +package eu.esdihumboldt.hale.common.align.merge.test.impl + +import static org.junit.Assert.* + +import org.junit.Test + +import eu.esdihumboldt.hale.common.align.io.impl.JaxbAlignmentIO +import eu.esdihumboldt.hale.common.align.merge.test.AbstractMergeCellMigratorTest +import eu.esdihumboldt.hale.common.align.model.CellUtil +import eu.esdihumboldt.hale.common.align.model.Entity +import eu.esdihumboldt.hale.common.align.model.functions.JoinFunction +import eu.esdihumboldt.hale.common.align.model.functions.join.JoinParameter + +/** + * Test for retaining conditions during Merge. + * + * @author Simon Templer + */ +class JoinRetainConditionsTest extends AbstractMergeCellMigratorTest { + + @Test + void testRetypeCondition() { + def toMigrate = this.class.getResource('/testcases/retain-join-conditions-retype/S-to-T.halex') + def cellId = 'SabcT' + + def matching = this.class.getResource('/testcases/retain-join-conditions-retype/ABC-to-S.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + + def expectedFilterA = 'NOT (a = \'100\')' + def expectedFilterB = '(b IN (\'1000\',\'1001\'))' + def expectedFilterC = 'NOT (c IS NULL)' + + assertNotNull(migrated[0].source) + assertEquals(3, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.definition.displayName == 'A') { + // expect filter part to have been propagated to A + assertNotNull(filter) + assertEquals(expectedFilterA, filter.filterTerm) + } + else if (e.definition.definition.displayName == 'B') { + // expect filter part to have been propagated to A + assertNotNull(filter) + assertEquals(expectedFilterB, filter.filterTerm) + } + else if (e.definition.definition.displayName == 'C') { + // expect filter part to have been propagated to A + assertNotNull(filter) + assertEquals(expectedFilterC, filter.filterTerm) + } + else { + fail('Unexpected entity') + } + } + JoinParameter param = CellUtil.getFirstParameter(migrated[0], JoinFunction.PARAMETER_JOIN).as(JoinParameter) + assertJoinOrder(param, ['A', 'B', 'C']) + + // there should be a condition on all join types, also in the order + def filter = param.types[0].filter + assertNotNull(filter) + assertEquals(expectedFilterA, filter.filterTerm) + + filter = param.types[1].filter + assertNotNull(filter) + assertEquals(expectedFilterB, filter.filterTerm) + + filter = param.types[2].filter + assertNotNull(filter) + assertEquals(expectedFilterC, filter.filterTerm) + + // there should also be a filter in the condition for each type + + // base properties + def base = param.conditions.collect { it.baseProperty }.findAll { it.type.displayName == 'A' }.toList() + assertEquals(1, base.size()) + assertNotNull(base[0].filter) + assertEquals(expectedFilterA, base[0].filter.filterTerm) + + base = param.conditions.collect { it.baseProperty }.findAll { it.type.displayName == 'B' }.toList() + assertEquals(1, base.size()) + assertNotNull(base[0].filter) + assertEquals(expectedFilterB, base[0].filter.filterTerm) + + // join properties + def join = param.conditions.collect { it.joinProperty }.findAll { it.type.displayName == 'B' }.toList() + assertEquals(1, join.size()) + assertNotNull(join[0].filter) + assertEquals(expectedFilterB, join[0].filter.filterTerm) + + join = param.conditions.collect { it.joinProperty }.findAll { it.type.displayName == 'C' }.toList() + assertEquals(1, join.size()) + assertNotNull(join[0].filter) + assertEquals(expectedFilterC, join[0].filter.filterTerm) + + // there should be a message about the condition having been translated automatically + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + } + + /** + * Test if conditions on the type are properly migrated for property relations. + */ + @Test + void testRetypeConditionRenameTypeFiltered() { + def toMigrate = this.class.getResource('/testcases/retain-join-conditions-retype/S-to-T.halex') + def cellId = 'aRename' + + def matching = this.class.getResource('/testcases/retain-join-conditions-retype/ABC-to-S.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + + def expectedFilterA = 'NOT (a = \'100\')' + + assertNotNull(migrated[0].source) + assertEquals(1, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.type.displayName == 'A') { + // expect filter part to have been propagated to A + assertNotNull(filter) + assertEquals(expectedFilterA, filter.filterTerm) + } + else { + fail('Unexpected entity') + } + } + + // there should be a message about the condition having been translated automatically + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + } + + /** + * Test if conditions on the type are properly migrated for property relations. + * Migrating conditions involves replacing property names in the filter. + */ + @Test + void testRetypeConditionRenameTypeFilteredTranslated() { + def toMigrate = this.class.getResource('/testcases/retain-join-conditions-retype2/S-to-T.halex') + def cellId = 'aRename' + + def matching = this.class.getResource('/testcases/retain-join-conditions-retype2/ABC-to-S.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + + def expectedFilterA = 'NOT (a = \'100\')' + + assertNotNull(migrated[0].source) + assertEquals(1, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.type.displayName == 'A') { + // expect filter part to have been propagated to A + assertNotNull(filter) + assertEquals(expectedFilterA, filter.filterTerm) + } + else { + fail('Unexpected entity') + } + } + + // there should be a message about the condition having been translated automatically + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + } + + /** + * Test that a property relation that previously had no type filter still has no type filter after migration. + */ + @Test + void testRetypeConditionRenameTypeInherited() { + def toMigrate = this.class.getResource('/testcases/retain-join-conditions-retype/S-to-T.halex') + def cellId = 'bRename' + + def matching = this.class.getResource('/testcases/retain-join-conditions-retype/ABC-to-S.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + + assertNotNull(migrated[0].source) + assertEquals(1, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.type.displayName == 'B') { + // there should be no filter + assertNull(filter) + } + else { + fail('Unexpected entity') + } + } + } + + @Test + void testJoinCondition() { + def toMigrate = this.class.getResource('/testcases/retain-condition-join/B-to-C.halex') + def cellId = 'B1toC1' + + def matching = this.class.getResource('/testcases/retain-condition-join/A-to-B.halex') + + def migrated = merge(cellId, toMigrate, matching) + + // do checks + + // filter + assertEquals(1, migrated.size()) + JaxbAlignmentIO.printCell(migrated[0], System.out) + + assertNotNull(migrated[0].source) + assertEquals(2, migrated[0].source.size()) + Collection source = migrated[0].source.values() + ((Collection) source).each { e -> + def filter = e.definition.filter + if (e.definition.definition.displayName == 'A1') { + // expect filter to have been propagated to A1 + assertNotNull(filter) + //assertEquals('a1 <> \'NIL\'', filter.filterTerm) + assertEquals('NOT (a1 = \'NIL\')', filter.filterTerm) + } + else { + assertEquals('A2', e.definition.definition.displayName) + + // there should be no filter + assertNull(filter) + } + } + + JoinParameter param = CellUtil.getFirstParameter(migrated[0], JoinFunction.PARAMETER_JOIN).as(JoinParameter) + assertJoinOrder(param, ['A1', 'A2']) + + // there should be a condition on the join focus, also in the order + assertNotNull(param.types[0].filter) + + // there should also be a filter in the condition + def base = param.conditions.collect { it.baseProperty }.findAll { it.type.displayName == 'A1' }.toList() + assertEquals(1, base.size()) + assertNotNull(base[0].filter) + + // there should be a message about the condition having been translated automatically + def messages = getMigrationMessages(migrated[0]) + assertTrue(messages.any { msg -> + msg.text.toLowerCase().contains('condition') + }) + // there should be a message about the filter being removed from A2 + assertTrue(messages.any { msg -> + msg.text.startsWith('A2') && msg.text.toLowerCase().contains('was removed') + }) + } + + // helpers + + void assertJoinOrder(JoinParameter param, List expected) { + def names = [] + param.types?.each { type -> + names << type.type.name.localPart + } + + assertEquals(expected, names) + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex index e42552830d..ff4af6c277 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex @@ -1,9 +1,9 @@ - + B to C (to be migrated) Simon Templer 2018-01-10T16:26:35.275+01:00 - 2018-07-17T14:47:20.404+02:00 + 2024-02-06T20:10:09.235+01:00 UTF-8 false diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.alignment.xml index 8a9f9d4248..17d2d9d3fd 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.alignment.xml +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.alignment.xml @@ -118,10 +118,8 @@ - - b6 <> 10 - - + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.styles.sld index e80edb03f6..ad34f540f6 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.styles.sld +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-condition-join/B-to-C.halex.styles.sld @@ -1,6 +1,3 @@ Default Styler - - name - diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex new file mode 100644 index 0000000000..1ec3f6a8e7 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex @@ -0,0 +1,27 @@ + + + ABC to S (matching) + Simon Templer + 2018-01-10T16:17:32.757+01:00 + 2024-02-05T11:12:38.984+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex + + + UTF-8 + 6534b899-7ef3-4ead-8e4f-9af3af16f030 + ABC.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + 67afcd12-5dd4-4adc-ab52-042786a4b3af + S.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.alignment.xml new file mode 100644 index 0000000000..afdede245f --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.alignment.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC-to-S.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC.groovy new file mode 100644 index 0000000000..29f9fb2411 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/ABC.groovy @@ -0,0 +1,15 @@ +schema('ABC') { + A { + a() + } + + B { + a() + b(cardinality: '?') + } + + C { + b() + c(cardinality: '?') + } +} \ No newline at end of file diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex new file mode 100644 index 0000000000..1e62ad98f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex @@ -0,0 +1,27 @@ + + + S to T (to be migrated) + Simon Templer + 2018-01-10T16:26:35.275+01:00 + 2024-03-12T13:27:44.936+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex + + + UTF-8 + bafa16f0-75ed-44e3-a0ed-10e5410d044d + S.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + e13b67b2-8b9a-45eb-b812-c8c675028b48 + T.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.alignment.xml new file mode 100644 index 0000000000..ab1ed4de7c --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.alignment.xml @@ -0,0 +1,69 @@ + + + + + + + a <> '100' AND (b = '1000' OR b = '1001') AND c IS NOT NULL + + + + + + + + + + + + + + + + a <> '100' AND (b = '1000' OR b = '1001') AND c IS NOT NULL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S-to-T.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S.groovy new file mode 100644 index 0000000000..1b76c88047 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/S.groovy @@ -0,0 +1,7 @@ +schema('S') { + S { + a() + b(cardinality: '?') + c(cardinality: '?') + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/T.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/T.groovy new file mode 100644 index 0000000000..f34690fedc --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype/T.groovy @@ -0,0 +1,7 @@ +schema('T') { + T { + a() + b(cardinality: '?') + c(cardinality: '?') + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex new file mode 100644 index 0000000000..ea371d8f71 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex @@ -0,0 +1,27 @@ + + + ABC to S (matching) + Simon Templer + 2018-01-10T16:17:32.757+01:00 + 2024-02-05T11:12:38.984+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex + + + UTF-8 + 6534b899-7ef3-4ead-8e4f-9af3af16f030 + ABC.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + 67afcd12-5dd4-4adc-ab52-042786a4b3af + S.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.alignment.xml new file mode 100644 index 0000000000..1e8f3bc303 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.alignment.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC-to-S.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC.groovy new file mode 100644 index 0000000000..29f9fb2411 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/ABC.groovy @@ -0,0 +1,15 @@ +schema('ABC') { + A { + a() + } + + B { + a() + b(cardinality: '?') + } + + C { + b() + c(cardinality: '?') + } +} \ No newline at end of file diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex new file mode 100644 index 0000000000..ead9673df5 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex @@ -0,0 +1,27 @@ + + + S to T (to be migrated) + Simon Templer + 2018-01-10T16:26:35.275+01:00 + 2024-03-12T13:27:44.936+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex + + + UTF-8 + bafa16f0-75ed-44e3-a0ed-10e5410d044d + S.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + e13b67b2-8b9a-45eb-b812-c8c675028b48 + T.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.alignment.xml new file mode 100644 index 0000000000..b999738459 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.alignment.xml @@ -0,0 +1,69 @@ + + + + + + + sourceA <> '100' AND (sourceB = '1000' OR sourceB = '1001') AND sourceC IS NOT NULL + + + + + + + + + + + + + + + + sourceA <> '100' AND (sourceB = '1000' OR sourceB = '1001') AND sourceC IS NOT NULL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S-to-T.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S.groovy new file mode 100644 index 0000000000..65cdddccbc --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/S.groovy @@ -0,0 +1,7 @@ +schema('S') { + S { + sourceA() + sourceB(cardinality: '?') + sourceC(cardinality: '?') + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/T.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/T.groovy new file mode 100644 index 0000000000..2bbca8b4ba --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/retain-join-conditions-retype2/T.groovy @@ -0,0 +1,7 @@ +schema('T') { + T { + targetA() + targetB(cardinality: '?') + targetC(cardinality: '?') + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex new file mode 100644 index 0000000000..600daa4e90 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex @@ -0,0 +1,27 @@ + + + A to B (matching) + Simon Templer + 2018-01-10T16:17:32.757+01:00 + 2024-03-13T10:56:32.007+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex + + + UTF-8 + 6534b899-7ef3-4ead-8e4f-9af3af16f030 + A.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + 67afcd12-5dd4-4adc-ab52-042786a4b3af + B.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.alignment.xml new file mode 100644 index 0000000000..386c1e1f5b --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.alignment.xml @@ -0,0 +1,422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notes from match + + + + + + + + + + + + + + + + ab + 'ist doof' + + + + + + + + + + + + + + + + + + + + + + + + + value = 'green lantern' + + + + + + + + + + + + + + + + + + + + + + + + + +_target { +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +_target { +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +_target { +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A-to-B.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A.groovy new file mode 100644 index 0000000000..ed44fadde7 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/A.groovy @@ -0,0 +1,36 @@ +schema('A') { + A1 { + aa() + ab() + ac() + ad() + } + + A2 { + aa() + ab() + ac() + ad() + } + + A3 { + aa() + ab() + ac() + ad() + } + + A5 { + aa() + ab() + ac() + ad() + } + + A6 { + aa() + ab() + ac() + ad() + } +} \ No newline at end of file diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex new file mode 100644 index 0000000000..b64bd20f0f --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex @@ -0,0 +1,27 @@ + + + B to C (to be migrated) + Simon Templer + 2018-01-10T16:26:35.275+01:00 + 2024-03-13T10:57:03.028+01:00 + + UTF-8 + false + eu.esdihumboldt.hale.io.project.hale25.xml + file:/home/simon/repos/hale/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex + + + UTF-8 + bafa16f0-75ed-44e3-a0ed-10e5410d044d + B.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + UTF-8 + e13b67b2-8b9a-45eb-b812-c8c675028b48 + C.groovy + eu.esdihumboldt.hale.io.schemabuilder + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.alignment.xml b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.alignment.xml new file mode 100644 index 0000000000..75a706d161 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.alignment.xml @@ -0,0 +1,190 @@ + + + + + + + ba = 'test' + + + + + + + + + + +_target { +} + + + B1 to C1 + + + + + + bb = 'test' + + + + + + + + + + + B2 to C2 + + + + + + bb = 'test' + + + + + + + + + + + + + + + + + + bc = 'test' + + + + + + + + + + + + + + + + bd = 'test' + + + + + + + + + + + B4 to C4 + + + + + + ba = 'test' and bb <> 'test' + + + + + + + bc = 'test' and bd <> 'test' + + + + + + + + + + + + + ba = 'test' and bb <> 'test' + + + + + bc = 'test' and bd <> 'test' + + + + + + ba = 'test' and bb <> 'test' + + + + + + bc = 'test' and bd <> 'test' + + + + + + + + +_target { +} + + + + + + + + ba = 'test' and bb <> 'test' + + + + + + + + + + + + + + + + + + ba = 'test' and bb <> 'test' + + + + + + + + + ba = 'test' and bb <> 'test' + + + + + + + + + + + + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.styles.sld b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.styles.sld new file mode 100644 index 0000000000..ad34f540f6 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B-to-C.halex.styles.sld @@ -0,0 +1,3 @@ + + Default Styler + diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B.groovy new file mode 100644 index 0000000000..eac2f7358d --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/B.groovy @@ -0,0 +1,43 @@ +schema('B') { + B1 { + ba() + bb() + bc() + bd() + } + + B2 { + ba() + bb() + bc() + bd() + } + + B3 { + ba() + bb() + bc() + bd() + } + + B4 { + ba() + bb() + bc() + bd() + } + + B5 { + ba() + bb() + bc() + bd() + } + + B6 { + ba() + bb() + bc() + bd() + } +} \ No newline at end of file diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/C.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/C.groovy new file mode 100644 index 0000000000..529a699424 --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge.test/testcases/type-filter-props-mapped/C.groovy @@ -0,0 +1,43 @@ +schema('C') { + C1 { + ca() + cb() + cc() + cd() + } + + C2 { + ca() + cb() + cc() + cd() + } + + C3 { + ca() + cb() + cc() + cd() + } + + C4 { + ca() + cb() + cc() + cd() + } + + C5 { + ca() + cb() + cc() + cd() + } + + C6 { + ca() + cb() + cc() + cd() + } +} \ No newline at end of file diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinContext.java b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinContext.java index a199bea7e9..385bd7b64c 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinContext.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinContext.java @@ -33,6 +33,7 @@ import eu.esdihumboldt.cst.functions.groovy.GroovyJoin; import eu.esdihumboldt.hale.common.align.merge.MergeUtil; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.CellUtil; @@ -245,12 +246,12 @@ private PropertyEntityDefinition processOriginalConditionProperty( }); if (!isStripped) { return (PropertyEntityDefinition) migration.entityReplacement(property, log) - .orElse(property); + .map(EntityMatch::getMatch).orElse(property); } else { EntityDefinition stripped = AlignmentUtil.getAllDefaultEntity(property); return (PropertyEntityDefinition) migration.entityReplacement(stripped, log) - .orElse(stripped); + .map(EntityMatch::getMatch).orElse(stripped); } } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinMergeMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinMergeMigrator.java index 97825d437c..64b0e17124 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinMergeMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/functions/JoinMergeMigrator.java @@ -29,6 +29,7 @@ import eu.esdihumboldt.hale.common.align.merge.impl.AbstractMergeCellMigrator; import eu.esdihumboldt.hale.common.align.merge.impl.AbstractMigration; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.CellUtil; @@ -188,9 +189,13 @@ private void addSources(MutableCell cell, EntityDefinition source, Cell match, Entity entity = entry.getValue(); if (transferContext.test(entity.getDefinition())) { + // XXX should the match be marked as from a Join? Unclear in + // this context + EntityMatch e = EntityMatch.of(entity.getDefinition()); + // transfer filter and contexts if possible - EntityDefinition withContexts = AbstractMigration.translateContexts(source, - entity.getDefinition(), migration, log); + EntityDefinition withContexts = AbstractMigration + .translateContexts(source, e, migration, null, log).getMatch(); if (withContexts.getFilter() != null) { context.addTypeFilter(withContexts.getType(), withContexts.getFilter()); } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMergeCellMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMergeCellMigrator.java index b7f7692b8d..23cdc486ad 100755 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMergeCellMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMergeCellMigrator.java @@ -20,7 +20,9 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; @@ -39,6 +41,7 @@ import eu.esdihumboldt.hale.common.align.merge.MergeUtil; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; import eu.esdihumboldt.hale.common.align.migrate.CellMigrator; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.migrate.impl.DefaultCellMigrator; import eu.esdihumboldt.hale.common.align.migrate.impl.MigrationOptionsImpl; @@ -60,6 +63,7 @@ import eu.esdihumboldt.hale.common.core.report.SimpleLog; import eu.esdihumboldt.hale.common.instance.model.Filter; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.model.constraint.type.GeometryType; /** @@ -155,11 +159,12 @@ else if (isDirectMatch(originalCell)) { AlignmentMigration cellMigration = new AbstractMigration() { @Override - protected Optional findMatch( - EntityDefinition entity) { + protected Optional findMatch(EntityDefinition entity, + TypeDefinition preferRoot) { Entity target = CellUtil.getFirstEntity(originalCell.getTarget()); if (target != null) { - return Optional.ofNullable(target.getDefinition()); + return Optional + .ofNullable(EntityMatch.of(target.getDefinition())); } return Optional.empty(); } @@ -357,7 +362,8 @@ else if (newSource.size() == 1) { Entity singleSource = CellUtil.getFirstEntity(newSource); if (singleSource != null) { EntityDefinition transferedSource = AbstractMigration.translateContexts( - original, singleSource.getDefinition(), migration, log); + original, EntityMatch.of(singleSource.getDefinition()), migration, + null, log).getMatch(); ListMultimap s = ArrayListMultimap.create(); s.put(newSource.keySet().iterator().next(), AlignmentUtil.createEntity(transferedSource)); @@ -371,11 +377,19 @@ else if (newSource.size() == 1) { // sources? // XXX for now only special case handling to support - // XtraServer use case + // XtraServer use case - if not enabled, continue if (applySourceContextsToJoinFocus(newCell, originalSource, migration, log)) { return; } + /* + * Generic handling of source contexts for Join that tries + * to split filters and apply parts to each source type. + */ + if (applySourceContextsToJoin(newCell, originalSource, migration, log)) { + return; + } + // no idea where to add contexts -> report log.warn( "Any conditions/contexts on the original source have been dropped because the new mapping has multiple sources and it is not clear where they should be attached: " @@ -385,6 +399,110 @@ else if (newSource.size() == 1) { } } + /** + * Apply source conditions to joined types. + * + * @param newCell the new cell to update the sources + * @param originalSource the original source + * @param migration the alignment migration + * @param log the operation log + * @return if the method handled the context transfer + */ + private boolean applySourceContextsToJoin(MutableCell newCell, Entity originalSource, + AlignmentMigration migration, SimpleLog log) { + String function = newCell.getTransformationIdentifier(); + switch (function) { + case GroovyJoin.ID: + case JoinFunction.ID: + break; + default: + return false; + } + + JoinParameter joinConfig = CellUtil.getFirstParameter(newCell, JoinFunction.PARAMETER_JOIN) + .as(JoinParameter.class); + + if (joinConfig == null || joinConfig.getTypes() == null + || joinConfig.getTypes().isEmpty()) { + return false; + } + + List joinTypes = joinConfig.getTypes().stream() + .map(TypeEntityDefinition::getDefinition).toList(); + Map joinTypeFilters = new HashMap(); + + // transfer context for each Join source type + newCell.setSource(ArrayListMultimap.create(Multimaps.transformValues(newCell.getSource(), + new com.google.common.base.Function() { + + @Override + public Entity apply(Entity input) { + TypeDefinition inputType = input.getDefinition().getType(); + if (input.getDefinition().getPropertyPath().isEmpty() + && joinTypes.contains(inputType)) { + EntityDefinition transferedSource = AbstractMigration + .translateContexts(originalSource.getDefinition(), + new EntityMatch(input.getDefinition(), + joinTypes.size() > 0, true), + migration, inputType, log) + .getMatch(); + joinTypeFilters.put(inputType, transferedSource.getFilter()); + return AlignmentUtil.createEntity(transferedSource); + } + else { + return input; + } + } + }))); + + // fix filter in order and conditions + // XXX only works like this because a type currently can only be present + // once in the source + if (!joinTypeFilters.isEmpty()) { + // order + List types = new ArrayList<>(); + for (int i = 0; i < joinConfig.getTypes().size(); i++) { + TypeEntityDefinition type = joinConfig.getTypes().get(i); + Filter filter = joinTypeFilters.get(type.getType()); + if (filter != null) { + type = new TypeEntityDefinition(type.getDefinition(), type.getSchemaSpace(), + filter); + } + types.add(type); + } + + // conditions + Set conditions = joinConfig.getConditions().stream().map(c -> { + Filter baseFilter = joinTypeFilters.get(c.baseProperty.getType()); + Filter joinFilter = joinTypeFilters.get(c.joinProperty.getType()); + + if (baseFilter != null || joinFilter != null) { + return new JoinCondition(applyFilter(c.baseProperty, baseFilter), + applyFilter(c.joinProperty, joinFilter)); + } + else { + return c; + } + }).collect(Collectors.toSet()); + + JoinParameter newConfig = new JoinParameter(types, conditions); + + ListMultimap modParams = ArrayListMultimap + .create(newCell.getTransformationParameters()); + List joinParams = modParams.get(JoinFunction.PARAMETER_JOIN); + if (!joinParams.isEmpty()) { + JoinParameter joinParam = joinParams.get(0).as(JoinParameter.class); + if (joinParam != null) { + joinParams.clear(); + joinParams.add(new ParameterValue(Value.complex(newConfig))); + } + } + newCell.setTransformationParameters(modParams); + } + + return true; + } + /** * Handle special case of applying source contexts to the entity that is the * Join focus. @@ -421,6 +539,8 @@ private boolean applySourceContextsToJoinFocus(MutableCell newCell, Entity origi TypeEntityDefinition focus = joinConfig.getTypes().iterator().next(); AtomicReference focusFilter = new AtomicReference<>(); + boolean multipleSources = newCell.getSource().size() > 0; + // transfer context newCell.setSource(ArrayListMultimap.create(Multimaps.transformValues(newCell.getSource(), new com.google.common.base.Function() { @@ -429,9 +549,12 @@ private boolean applySourceContextsToJoinFocus(MutableCell newCell, Entity origi public Entity apply(Entity input) { if (input.getDefinition().getPropertyPath().isEmpty() && input.getDefinition().getType().equals(focus.getType())) { - EntityDefinition transferedSource = AbstractMigration.translateContexts( - originalSource.getDefinition(), input.getDefinition(), - migration, log); + EntityDefinition transferedSource = AbstractMigration + .translateContexts(originalSource.getDefinition(), + new EntityMatch(input.getDefinition(), multipleSources, + true), + migration, focus.getType(), log) + .getMatch(); focusFilter.set(transferedSource.getFilter()); return AlignmentUtil.createEntity(transferedSource); } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMigration.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMigration.groovy index 014a1706df..20c114995c 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMigration.groovy +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/AbstractMigration.groovy @@ -17,6 +17,7 @@ package eu.esdihumboldt.hale.common.align.merge.impl import eu.esdihumboldt.hale.common.align.instance.EntityAwareFilter import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch import eu.esdihumboldt.hale.common.align.model.AlignmentUtil import eu.esdihumboldt.hale.common.align.model.ChildContext import eu.esdihumboldt.hale.common.align.model.Condition @@ -26,6 +27,7 @@ import eu.esdihumboldt.hale.common.core.report.SimpleLog import eu.esdihumboldt.hale.common.instance.extension.filter.FilterDefinitionManager import eu.esdihumboldt.hale.common.schema.SchemaSpaceID import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition import eu.esdihumboldt.hale.common.schema.model.constraint.type.GeometryType import groovy.transform.CompileStatic @@ -40,18 +42,19 @@ abstract class AbstractMigration implements AlignmentMigration { /** * Find a match for the given entity * @param entity the entity + * @param preferRoot hint on which entity to prefer if there are multiple matches * @return the match if found */ - protected abstract Optional findMatch(EntityDefinition entity); + protected abstract Optional findMatch(EntityDefinition entity, TypeDefinition preferRoot); @Override - Optional entityReplacement(EntityDefinition entity, SimpleLog log) { + Optional entityReplacement(EntityDefinition entity, TypeDefinition preferRoot, SimpleLog log) { EntityDefinition defaultEntity = AlignmentUtil.getAllDefaultEntity(entity) - Optional matchedEntity = findMatch(defaultEntity) + Optional matchedEntity = findMatch(defaultEntity, preferRoot) // special case handling if (!matchedEntity.isPresent()) { - matchedEntity = findParentMatch(defaultEntity) + matchedEntity = findParentMatch(defaultEntity, preferRoot) if (matchedEntity.present) { log.warn "Inaccurate match of $entity to ${matchedEntity.get()} via parent entity" } @@ -59,8 +62,7 @@ abstract class AbstractMigration implements AlignmentMigration { if (matchedEntity.present) { // entity contained contexts -> translate them if possible - - matchedEntity = Optional.ofNullable(translateContexts(entity, matchedEntity.get(), this, log)); + matchedEntity = Optional.ofNullable(translateContexts(entity, matchedEntity.get(), this, preferRoot, log)); } if (!matchedEntity.isPresent()) { @@ -70,14 +72,15 @@ abstract class AbstractMigration implements AlignmentMigration { return matchedEntity } - static EntityDefinition translateContexts(EntityDefinition original, EntityDefinition target, - AlignmentMigration migration, SimpleLog log) { + static EntityMatch translateContexts(EntityDefinition original, EntityMatch target, + AlignmentMigration migration, TypeDefinition preferRoot, SimpleLog log) { def defaultEntity = AlignmentUtil.getAllDefaultEntity(original) + def targetEntity = target.getMatch() if (original.filter) { // what about if match has filter? - if (target.filter && original.filter != target.filter) { + if (targetEntity.filter && original.filter != targetEntity.filter) { def filterString = FilterDefinitionManager.instance.asString(original.filter) def msg = "Filter condition applied to the original source type has been dropped because a filter already existed for the entity it was replaced with. Please check if you need to change the condition to match both original conditions." if (filterString) { @@ -89,14 +92,25 @@ abstract class AbstractMigration implements AlignmentMigration { // apply filter to entity def filter = original.filter - if (!sameEntity(original, target)) { + if (!sameEntity(original, targetEntity)) { // replacements in filter if possible if (filter instanceof EntityAwareFilter) { - def migrated = ((EntityAwareFilter) filter).migrateFilter(AlignmentUtil.getTypeEntity(original), migration, log) + if (preferRoot == null) { + // set preferRoot to target type + // to restrict conditions to the type they may reference + preferRoot = targetEntity.type + } + + def migrated = ((EntityAwareFilter) filter).migrateFilter(AlignmentUtil.getTypeEntity(original), target, migration, preferRoot, log) if (migrated.present) { filter = migrated.get() //TODO mark automatically migrated? } + else { + // drop filter (should have been documented by migrateFilter call above) + //TODO discern between filter dropped vs. error when migrating filter? (see AbstractGeotoolsFilter#migrateFilter) + filter = null + } } // mark unsafe if entity is not the same @@ -104,17 +118,17 @@ abstract class AbstractMigration implements AlignmentMigration { } // add filter to match - target = AlignmentUtil.createEntity(target.type, target.propertyPath, + targetEntity = AlignmentUtil.createEntity(targetEntity.type, targetEntity.propertyPath, SchemaSpaceID.SOURCE, filter) } } if (original.propertyPath && original != defaultEntity) { // likely a context was present - target = applyContexts(target, original, log) + targetEntity = applyContexts(targetEntity, original, log) } - return target + return target.withMatch(targetEntity) } /** @@ -206,10 +220,10 @@ abstract class AbstractMigration implements AlignmentMigration { } } - protected Optional findParentMatch(EntityDefinition entity) { + protected Optional findParentMatch(EntityDefinition entity, TypeDefinition preferRoot) { //XXX only allow parent matches for specific cases right now if (!(entity.definition instanceof PropertyDefinition) || - !((PropertyDefinition) entity.definition).propertyType.getConstraint(GeometryType).isGeometry()) { + !((PropertyDefinition) entity.definition).propertyType.getConstraint(GeometryType).isGeometry()) { // not a geometry return Optional.empty() } @@ -217,7 +231,7 @@ abstract class AbstractMigration implements AlignmentMigration { while (entity != null) { entity = AlignmentUtil.getParent(entity) - def matchedEntity = findMatch(entity); + def matchedEntity = findMatch(entity, preferRoot); if (matchedEntity.present) { return matchedEntity } @@ -225,5 +239,4 @@ abstract class AbstractMigration implements AlignmentMigration { return Optional.empty() } - } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/DefaultSchemaMigration.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/DefaultSchemaMigration.groovy index aa7031ca00..0eff252a04 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/DefaultSchemaMigration.groovy +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/DefaultSchemaMigration.groovy @@ -44,7 +44,7 @@ class DefaultSchemaMigration implements AlignmentMigration { } @Override - public Optional entityReplacement(EntityDefinition entity, SimpleLog log) { + public Optional entityReplacement(EntityDefinition entity, TypeDefinition preferRoot, SimpleLog log) { // default behavior - try to find entity in new schema, based on names w/o namespace @@ -184,5 +184,4 @@ class DefaultSchemaMigration implements AlignmentMigration { candidate } - } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/MatchingMigration.groovy b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/MatchingMigration.groovy index 5075af47e1..d63bae6174 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/MatchingMigration.groovy +++ b/common/plugins/eu.esdihumboldt.hale.common.align.merge/src/eu/esdihumboldt/hale/common/align/merge/impl/MatchingMigration.groovy @@ -17,11 +17,13 @@ package eu.esdihumboldt.hale.common.align.merge.impl import java.util.function.Function +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch import eu.esdihumboldt.hale.common.align.model.AlignmentUtil import eu.esdihumboldt.hale.common.align.model.Cell import eu.esdihumboldt.hale.common.align.model.EntityDefinition import eu.esdihumboldt.hale.common.headless.impl.ProjectTransformationEnvironment; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition import groovy.transform.CompileStatic;;; /** @@ -41,12 +43,56 @@ class MatchingMigration extends AbstractMigration { this.reverse = reverse } - protected Optional findMatch(EntityDefinition entity) { + @Override + protected Optional findMatch(EntityDefinition entity, TypeDefinition preferRoot) { findMatches(entity).flatMap({ list -> - list ? Optional.ofNullable(((List)list)[0]) : Optional.empty() + findPreferredCandidate((List)list, entity, preferRoot) } as Function) } + private Optional findPreferredCandidate(Collection entities, EntityDefinition original, TypeDefinition preferRoot) { + if (entities.empty) { + return Optional.empty() + } + + boolean multiple = entities.size() > 1 + boolean join = false + + if (original.propertyPath.isEmpty()) { + // this is about a type entity + + /* + * Currently, because findMatches only considers cases where exactly one cell is present, + * we can assume that if there are multiple candidates that there is a join for the match. + */ + join = multiple + } + else { + // this is about a property entity + + /* + * For a property we need to determine if the respective type match is part of a join + * and use that information. + */ + def typeEntity = AlignmentUtil.getTypeEntity(original) + def typeMatch = findMatch(typeEntity, preferRoot) + if (typeMatch.isPresent()) { + join = typeMatch.get().matchPartOfJoin + } + } + + + /* + * Note: preferRoot is used here to for example pick a specific type from a Join cell's sources + */ + def firstPreferred = entities.find { EntityDefinition it -> it.type == preferRoot } + def match = firstPreferred ? Optional.of(firstPreferred) : Optional.of(entities.iterator().next()) + + match.map { + new EntityMatch(it, multiple, join) + } + } + public Optional> findMatches(EntityDefinition entity) { if (reverse) { // match to target diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/META-INF/MANIFEST.MF b/common/plugins/eu.esdihumboldt.hale.common.align/META-INF/MANIFEST.MF index 3adb7e08d2..eec7c07d4a 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/META-INF/MANIFEST.MF +++ b/common/plugins/eu.esdihumboldt.hale.common.align/META-INF/MANIFEST.MF @@ -51,6 +51,7 @@ Import-Package: com.google.common.base;version="1.6.0", eu.esdihumboldt.util.io, javax.annotation;version="[1.2.0,1.2.0]", net.jcip.annotations, + org.apache.commons.io.output, org.apache.commons.lang;version="2.4.0", org.locationtech.jts.geom, org.slf4j;version="1.5.11", diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/instance/EntityAwareFilter.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/instance/EntityAwareFilter.java index 43c3e3376a..17c0f56284 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/instance/EntityAwareFilter.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/instance/EntityAwareFilter.java @@ -19,9 +19,11 @@ import java.util.Optional; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.core.report.SimpleLog; import eu.esdihumboldt.hale.common.instance.model.Filter; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; /** * Extended filter interface for filters aware of referenced entities. @@ -42,7 +44,7 @@ public interface EntityAwareFilter extends Filter { /** * States if the filter supports migration via - * {@link #migrateFilter(EntityDefinition, AlignmentMigration, SimpleLog)} + * {@link #migrateFilter(EntityDefinition, EntityMatch, AlignmentMigration, TypeDefinition, SimpleLog)} * * @return true if migration is supported, false * otherwise @@ -54,11 +56,15 @@ public interface EntityAwareFilter extends Filter { * migration. * * @param context the entity context + * @param targetMatch the match representing the entity the filter is + * applied to * @param migration the alignment migration + * @param preferRoot hint on which entity to prefer if there are multiple + * matches * @param log the operation log * @return the migrated filter, if migration is possible */ - Optional migrateFilter(EntityDefinition context, AlignmentMigration migration, - SimpleLog log); + Optional migrateFilter(EntityDefinition context, EntityMatch targetMatch, + AlignmentMigration migration, TypeDefinition preferRoot, SimpleLog log); } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/io/impl/JaxbAlignmentIO.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/io/impl/JaxbAlignmentIO.java index bed39c7e0e..d2f1386118 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/io/impl/JaxbAlignmentIO.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/io/impl/JaxbAlignmentIO.java @@ -19,8 +19,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PrintStream; import java.net.URI; +import org.apache.commons.io.output.CloseShieldOutputStream; + import eu.esdihumboldt.hale.common.align.io.EntityResolver; import eu.esdihumboldt.hale.common.align.io.impl.internal.AlignmentToJaxb; import eu.esdihumboldt.hale.common.align.io.impl.internal.JaxbToAlignment; @@ -171,7 +174,21 @@ public static void save(AlignmentType alignment, IOReporter reporter, OutputStre } /** - * Print a cell to an output stream (intended for tests/debugging). + * Print a cell to a {@link PrintStream} (intended for tests/debugging). The + * stream is prevented from being closed, which is intended to prevent for + * instance System.out from being closed. + * + * @param cell the cell to print + * @param out the output stream + * @throws Exception if an error occurs trying to print the cell + */ + public static void printCell(MutableCell cell, PrintStream out) throws Exception { + printCell(cell, CloseShieldOutputStream.wrap(out)); + } + + /** + * Print a cell to an output stream (intended for tests/debugging). The + * stream is closed when the cell was written. * * @param cell the cell to print * @param out the output stream diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/AlignmentMigration.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/AlignmentMigration.java index c65ef04431..52eee484bc 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/AlignmentMigration.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/AlignmentMigration.java @@ -19,6 +19,7 @@ import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.core.report.SimpleLog; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; /** * Interface describing an alignment migration. @@ -34,6 +35,20 @@ public interface AlignmentMigration { * @param log the migration process log (may be cell specific) * @return the replacement entity, if the entity should be replaced */ - Optional entityReplacement(EntityDefinition entity, SimpleLog log); + default Optional entityReplacement(EntityDefinition entity, SimpleLog log) { + return entityReplacement(entity, null, log); + } + + /** + * Yields a replacement for an entity existing in a given alignment. + * + * @param entity the entity to replace + * @param preferRoot hint on which entity to prefer if there are multiple + * matches + * @param log the migration process log (may be cell specific) + * @return the replacement entity, if the entity should be replaced + */ + Optional entityReplacement(EntityDefinition entity, TypeDefinition preferRoot, + SimpleLog log); } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/EntityMatch.groovy b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/EntityMatch.groovy new file mode 100644 index 0000000000..e9d222601a --- /dev/null +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/EntityMatch.groovy @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 wetransform GmbH + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution. If not, see . + * + * Contributors: + * wetransform GmbH + */ + +package eu.esdihumboldt.hale.common.align.migrate + +import eu.esdihumboldt.hale.common.align.model.EntityDefinition +import groovy.transform.CompileStatic +import groovy.transform.Immutable + +/** + * Type representing an entity match in an alignment migration with additional information. + * + * @author Simon Templer + */ +@CompileStatic +@Immutable(knownImmutableClasses = [EntityDefinition.class]) +class EntityMatch { + EntityDefinition match + boolean multipleCandidates + boolean matchPartOfJoin + + /** + * Create a copy of the match replacing the matched entity. + * + * @param newMatch the entity to use as new match + * @return the copy of the match with the replaced entity + */ + EntityMatch withMatch(EntityDefinition newMatch) { + return new EntityMatch(newMatch, multipleCandidates, matchPartOfJoin) + } + + /** + * Create an entity match using the provided entity with default values assuming + * there is only this candidate and there is no join involved. + * + * @param match the entity to use for the match + * @return the entity match using the provided entity + */ + static EntityMatch of(EntityDefinition match) { + return new EntityMatch(match, false, false) + } +} diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/DefaultCellMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/DefaultCellMigrator.java index 2cee521a51..ea95417315 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/DefaultCellMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/DefaultCellMigrator.java @@ -25,6 +25,7 @@ import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; import eu.esdihumboldt.hale.common.align.migrate.CellMigrator; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.Entity; @@ -60,9 +61,9 @@ public MutableCell updateCell(final Cell originalCell, final AlignmentMigration public Entity transformEntry(String key, Entity value) { EntityDefinition org = value.getDefinition(); - Optional replace = migration.entityReplacement(org, cellLog); + Optional replace = migration.entityReplacement(org, cellLog); - EntityDefinition entity = replace.orElse(org); + EntityDefinition entity = replace.map(m -> m.getMatch()).orElse(org); // FIXME what about null replacements / removals? if (!Objects.equal(entity, org)) { diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/UnmigratedCell.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/UnmigratedCell.java index 371ab0d75c..2284ef999a 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/UnmigratedCell.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/migrate/impl/UnmigratedCell.java @@ -24,11 +24,13 @@ import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigrationNameLookupSupport; import eu.esdihumboldt.hale.common.align.migrate.CellMigrator; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.MutableCell; import eu.esdihumboldt.hale.common.align.model.impl.MutableCellDecorator; import eu.esdihumboldt.hale.common.core.report.SimpleLog; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; /** * Decorator for a {@link MutableCell} that allows to do lazy migration. @@ -81,9 +83,10 @@ public MutableCell migrate(Map additionalMap AlignmentMigration migration = new AlignmentMigrationNameLookupSupport() { @Override - public Optional entityReplacement(EntityDefinition entity, - SimpleLog log) { - return Optional.ofNullable(joinedMappings.get(entity)); + public Optional entityReplacement(EntityDefinition entity, + TypeDefinition preferRoot, SimpleLog log) { + return Optional + .ofNullable(new EntityMatch(joinedMappings.get(entity), false, false)); } @Override diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/FormattedStringMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/FormattedStringMigrator.java index cf151635eb..b618cb21e9 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/FormattedStringMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/FormattedStringMigrator.java @@ -28,11 +28,11 @@ import com.google.common.collect.ListMultimap; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.migrate.impl.DefaultCellMigrator; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.Entity; -import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.MutableCell; import eu.esdihumboldt.hale.common.align.model.ParameterValue; import eu.esdihumboldt.hale.common.align.model.annotations.messages.CellLog; @@ -85,8 +85,8 @@ private String convertPattern(String pattern, ListMultimap replacements = new HashMap<>(); for (PropertyEntityDefinition var : oldVars) { - Optional replacement = migration.entityReplacement(var, log); - replacement.ifPresent(repl -> { + Optional replacement = migration.entityReplacement(var, log); + replacement.map(m -> m.getMatch()).ifPresent(repl -> { String newName = repl.getDefinition().getName().getLocalPart(); // XXX there might be name conflicts - check for those or use // long names? diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/join/JoinMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/join/JoinMigrator.java index 5d1cc10562..2e66689f40 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/join/JoinMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/join/JoinMigrator.java @@ -23,6 +23,7 @@ import com.google.common.collect.ListMultimap; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.migrate.impl.DefaultCellMigrator; import eu.esdihumboldt.hale.common.align.model.Cell; @@ -80,14 +81,17 @@ private JoinParameter convertJoinParameter(JoinParameter joinParam, AlignmentMigration migration, MigrationOptions options, SimpleLog log) { List types = joinParam.getTypes().stream().map(type -> { - return (TypeEntityDefinition) migration.entityReplacement(type, log).orElse(type); + return (TypeEntityDefinition) migration.entityReplacement(type, log) + .map(EntityMatch::getMatch).orElse(type); }).collect(Collectors.toList()); Set conditions = joinParam.getConditions().stream().map(condition -> { PropertyEntityDefinition baseProperty = (PropertyEntityDefinition) migration - .entityReplacement(condition.baseProperty, log).orElse(condition.baseProperty); + .entityReplacement(condition.baseProperty, log).map(EntityMatch::getMatch) + .orElse(condition.baseProperty); PropertyEntityDefinition joinProperty = (PropertyEntityDefinition) migration - .entityReplacement(condition.joinProperty, log).orElse(condition.joinProperty); + .entityReplacement(condition.joinProperty, log).map(EntityMatch::getMatch) + .orElse(condition.joinProperty); JoinCondition result = new JoinCondition(baseProperty, joinProperty); return result; }).collect(Collectors.toSet()); diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/merge/MergeMigrator.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/merge/MergeMigrator.java index 706dfbe129..6157ebcbf0 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/merge/MergeMigrator.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/merge/MergeMigrator.java @@ -27,6 +27,7 @@ import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigrationNameLookupSupport; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.migrate.MigrationOptions; import eu.esdihumboldt.hale.common.align.migrate.impl.DefaultCellMigrator; import eu.esdihumboldt.hale.common.align.model.Cell; @@ -106,7 +107,8 @@ private ParameterValue convertProperty(ParameterValue value, AlignmentMigration return value; } - Optional replacement = migration.entityReplacement(entity, log); + Optional replacement = migration.entityReplacement(entity, log) + .map(EntityMatch::getMatch); if (replacement.isPresent()) { return convertProperty(value, replacement.get(), log); } diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/impl/DefaultAlignment.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/impl/DefaultAlignment.java index 1c04a450be..d41590778f 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/impl/DefaultAlignment.java +++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/impl/DefaultAlignment.java @@ -419,9 +419,32 @@ public Collection getTypeCells(Cell queryCell) { .getDefinition().getType(); if (target == null || DefinitionUtil.isSuperType(target, typeCellTarget)) { // target matches - if (sources.isEmpty() || matchesSources(typeCell.getSource(), sources)) { - // source matches, too - result.add(typeCell); + + if (sources.size() == 1) { + /* + * Special case handling when there is exactly one type, + * that in this case also allows to find a Join that only + * uses one of the types. + * + * Unclear if this is intended, but the else case does not + * yield any results in such cases, which could be due to + * the implementation of `matchesSources` in that case being + * focused on property relations. + */ + TypeEntityDefinition type = sources.iterator().next(); + if (sources.isEmpty() || matchesSources(type, + typeCell.getSource().values().stream() + .map(e -> AlignmentUtil.getTypeEntity(e.getDefinition())) + .toList())) { + // source matches, too + result.add(typeCell); + } + } + else { + if (sources.isEmpty() || matchesSources(typeCell.getSource(), sources)) { + // source matches, too + result.add(typeCell); + } } } } diff --git a/common/plugins/eu.esdihumboldt.hale.common.filter/META-INF/MANIFEST.MF b/common/plugins/eu.esdihumboldt.hale.common.filter/META-INF/MANIFEST.MF index def593b6ad..9e3e50a3fa 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.filter/META-INF/MANIFEST.MF +++ b/common/plugins/eu.esdihumboldt.hale.common.filter/META-INF/MANIFEST.MF @@ -23,6 +23,7 @@ Export-Package: eu.esdihumboldt.hale.common.filter, eu.esdihumboldt.hale.common.filter.definition Require-Bundle: org.opengis;bundle-version="29.1.0", eu.esdihumboldt.util.groovy, - org.geotools;bundle-version="29.1.0" + org.geotools;bundle-version="29.1.0", + groovy;bundle-version="2.5.19" Bundle-Vendor: data harmonisation panel Automatic-Module-Name: eu.esdihumboldt.hale.common.filter diff --git a/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/AbstractGeotoolsFilter.java b/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/AbstractGeotoolsFilter.java index bd3880fdc4..c8c1e8d37d 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/AbstractGeotoolsFilter.java +++ b/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/AbstractGeotoolsFilter.java @@ -16,15 +16,22 @@ package eu.esdihumboldt.hale.common.filter; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import javax.xml.namespace.QName; +import org.geotools.factory.CommonFactoryFinder; import org.geotools.filter.FilterAttributeExtractor; import org.geotools.filter.text.cql2.CQLException; +import org.geotools.util.factory.GeoTools; +import org.opengis.filter.And; import org.opengis.filter.Filter; +import org.opengis.filter.Or; import org.opengis.filter.expression.PropertyName; import de.fhg.igd.slf4jplus.ALogger; @@ -34,11 +41,13 @@ import eu.esdihumboldt.hale.common.align.groovy.accessor.internal.EntityAccessorUtil; import eu.esdihumboldt.hale.common.align.instance.EntityAwareFilter; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.core.report.SimpleLog; import eu.esdihumboldt.hale.common.filter.internal.EntityReplacementVisitor; import eu.esdihumboldt.hale.common.instance.helper.PropertyResolver; import eu.esdihumboldt.hale.common.instance.model.Instance; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.util.groovy.paths.Path; /** @@ -52,6 +61,10 @@ public abstract class AbstractGeotoolsFilter implements eu.esdihumboldt.hale.common.instance.model.Filter, EntityAwareFilter { + private static enum SplitType { + AND, OR + } + private static final ALogger log = ALoggerFactory.getLogger(AbstractGeotoolsFilter.class); private final String filterTerm; @@ -179,15 +192,105 @@ public List> getReferencedEntities(EntityDefinition c @Override public Optional migrateFilter( - EntityDefinition context, AlignmentMigration migration, SimpleLog log) { + EntityDefinition context, EntityMatch targetMatch, AlignmentMigration migration, + TypeDefinition preferRoot, SimpleLog log) { + // determine how to split filter + boolean join = targetMatch.isMatchPartOfJoin(); + SplitType splitType = join ? SplitType.AND : SplitType.OR; + + // split filter + List filterParts = splitFilter(internFilter, splitType); + + // migrate each filter part + List acceptedParts = new ArrayList<>(); + for (Filter part : filterParts) { + EntityReplacementVisitor visitor = new EntityReplacementVisitor(migration, + name -> resolveProperty(name, context, log), preferRoot, log); + Object extraData = null; + Filter copy = (Filter) part.accept(visitor, extraData); + + /* + * Determine if part is relevant. Only accept filter parts that are + * not exclusively updated with other types than `preferRoot`. (This + * is used to handle the different types from a Join individually, + * also for properties that are mapped in the same context) + * + * TODO is usage of preferRoot OK or should we have an additional + * parameter to control this behavior? + * + * Inform about parts that are dropped + */ + TypeDefinition focusType = preferRoot; + String messagePrefix = (focusType == null) ? "" : focusType.getDisplayName() + ": "; + if (visitor.isAllMismatches(focusType)) { + // drop if there were no successful replacements at all + if (filterParts.size() == 1) { + try { + log.warn( + "{0}The filter \"{1}\" was removed because no matches for the respective properties were found", + messagePrefix, toFilterTerm(part)); + } catch (CQLException e) { + log.error( + "{0}The filter was removed because no matches for the respective properties were found; error converting filter to string", + messagePrefix, e); + } + } + else { + try { + log.warn( + "{0}The filter operand \"{1}\" part of the filter''s {2} condition was removed because no matches for the respective properties were found", + messagePrefix, toFilterTerm(part), splitType); + } catch (CQLException e) { + log.error( + "{0}A filter operand part of the filter's {1} condition was removed because no matches for the respective properties were found; error converting filter part to string", + messagePrefix, splitType, e); + } + } + } + else { + acceptedParts.add(copy); + + // log if there are replacements that don't match the focus type + if (focusType != null) { + List otherReplacements = visitor.getReplacements().stream() + .filter(entity -> !focusType.equals(entity.getType())).toList(); + if (!otherReplacements.isEmpty()) { + try { + log.warn( + "{0}The filter operand \"{1}\" part of the filter''s {3} condition contains references related to other types than {2}", + messagePrefix, toFilterTerm(part), focusType.getDisplayName(), + splitType); + } catch (CQLException e) { + log.error( + "{0}A filter operand part of the filter's {2} condition contains references related to other types than {1}; error converting filter part to string", + messagePrefix, focusType.getDisplayName(), splitType, e); + } + } + } + } + } - EntityReplacementVisitor visitor = new EntityReplacementVisitor(migration, - name -> resolveProperty(name, context, log), log); - Object extraData = null; - Filter copy = (Filter) internFilter.accept(visitor, extraData); + if (acceptedParts.isEmpty()) { + return Optional.empty(); + } + + // combine accepted filter parts + Filter combined; + switch (splitType) { + case AND: + combined = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints()) + .and(acceptedParts); + break; + case OR: + combined = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints()) + .or(acceptedParts); + break; + default: + throw new IllegalStateException("Unsupported filter split type " + splitType); + } try { - String filterString = toFilterTerm(copy); + String filterString = toFilterTerm(combined); return Optional.of(buildFilter(filterString)); } catch (CQLException e) { log.error("Filter could not be automatically migrated", e); @@ -195,6 +298,34 @@ public Optional migrateFilter } } + /** + * Split a filter into separate AND or OR conditions. + * + * @param filter the filter to split + * @param splitType on which operation to split + * @return the split filters as list + */ + private List splitFilter(Filter filter, SplitType splitType) { + List result = new ArrayList<>(); + + Deque toCheck = new LinkedList<>(); + toCheck.add(filter); + while (!toCheck.isEmpty()) { + Filter f = toCheck.poll(); + if (SplitType.AND.equals(splitType) && f instanceof And) { + toCheck.addAll(((And) f).getChildren()); + } + else if (SplitType.OR.equals(splitType) && f instanceof Or) { + toCheck.addAll(((Or) f).getChildren()); + } + else { + result.add(f); + } + } + + return result; + } + /** * Resolve a property name based on the given context. * diff --git a/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/internal/EntityReplacementVisitor.java b/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/internal/EntityReplacementVisitor.java index 6e3c5cf736..18f99cbd94 100644 --- a/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/internal/EntityReplacementVisitor.java +++ b/common/plugins/eu.esdihumboldt.hale.common.filter/src/eu/esdihumboldt/hale/common/filter/internal/EntityReplacementVisitor.java @@ -15,7 +15,10 @@ package eu.esdihumboldt.hale.common.filter.internal; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -23,9 +26,11 @@ import org.opengis.filter.expression.PropertyName; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.core.report.SimpleLog; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; /** * Filter visitor that replaces entities in filters with their @@ -37,19 +42,33 @@ public class EntityReplacementVisitor extends DuplicatingFilterVisitor { private final AlignmentMigration migration; private final Function> resolveProperty; + private final TypeDefinition preferRoot; private final SimpleLog log; + // counter for total attempted replacements (where the original could be + // resolved) + private int total = 0; + // counter for successful replacements + private int matched = 0; + + private final Set mismatches = new LinkedHashSet<>(); + private final Set replacements = new LinkedHashSet<>(); + /** * Create an entity replacement visitor. * * @param migration the alignment migration * @param resolveProperty the function resolving a property to an entity + * @param preferRoot hint on which entity to prefer if there are multiple + * matches * @param log the operation log */ public EntityReplacementVisitor(AlignmentMigration migration, - Function> resolveProperty, SimpleLog log) { + Function> resolveProperty, + TypeDefinition preferRoot, SimpleLog log) { this.migration = migration; this.resolveProperty = resolveProperty; + this.preferRoot = preferRoot; this.log = log; } @@ -58,10 +77,21 @@ public Object visit(PropertyName expression, Object extraData) { Optional resolved = resolveProperty.apply(expression) .map(p -> AlignmentUtil.getAllDefaultEntity(p)); if (resolved.isPresent()) { - Optional replace = migration.entityReplacement(resolved.get(), log); - if (replace.isPresent() && !resolved.get().equals(replace.get())) { - return getFactory(extraData).property(toPropertyName(replace.get()), - expression.getNamespaceContext()); + total++; + + Optional replace = migration + .entityReplacement(resolved.get(), preferRoot, log).map(EntityMatch::getMatch); + if (replace.isPresent()) { + matched++; + replacements.add(replace.get()); + + if (!resolved.get().equals(replace.get())) { + return getFactory(extraData).property(toPropertyName(replace.get()), + expression.getNamespaceContext()); + } + } + else { + mismatches.add(resolved.get()); } } else { @@ -81,4 +111,56 @@ private String toPropertyName(EntityDefinition entityDefinition) { return name; } + /** + * @return if none the attempted replacements matched. + */ + public boolean isAllMismatches() { + return total > 0 && matched == 0; + } + + /** + * @return if there were any failed attempted replacements + */ + public boolean hasMismatches() { + return !mismatches.isEmpty(); + } + + /** + * @return the set of entities where no replacement was found + */ + public Set getMismatches() { + return Collections.unmodifiableSet(mismatches); + } + + /** + * @return the set of entities that served as replacement + */ + public Set getReplacements() { + return Collections.unmodifiableSet(replacements); + } + + /** + * Determine if related to an expected parent type all replacement attempts + * were mismatches or matching entities with a different parent type. + * + * @param expectedParent the expected parent type + * @return true if related to the given expected parent type + * all replacement attempts were mismatches or matching entities + * with a different parent type, false if there were + * successful matches with the given parent type + */ + public boolean isAllMismatches(TypeDefinition expectedParent) { + if (expectedParent == null) { + return isAllMismatches(); + } + + if (total > 0) { + return !replacements.stream() + .anyMatch(entity -> expectedParent.equals(entity.getType())); + } + else { + return false; + } + } + } diff --git a/platform/hale-platform.target b/platform/hale-platform.target index dc448faab3..ef157877aa 100644 --- a/platform/hale-platform.target +++ b/platform/hale-platform.target @@ -43,10 +43,6 @@ - - - - @@ -55,5 +51,9 @@ + + + + \ No newline at end of file diff --git a/ui/plugins/eu.esdihumboldt.hale.ui/src/eu/esdihumboldt/hale/ui/service/align/migrate/UserMigration.java b/ui/plugins/eu.esdihumboldt.hale.ui/src/eu/esdihumboldt/hale/ui/service/align/migrate/UserMigration.java index d04fbbcd68..f6f84f37b6 100644 --- a/ui/plugins/eu.esdihumboldt.hale.ui/src/eu/esdihumboldt/hale/ui/service/align/migrate/UserMigration.java +++ b/ui/plugins/eu.esdihumboldt.hale.ui/src/eu/esdihumboldt/hale/ui/service/align/migrate/UserMigration.java @@ -18,6 +18,7 @@ import java.util.Optional; import eu.esdihumboldt.hale.common.align.migrate.AlignmentMigration; +import eu.esdihumboldt.hale.common.align.migrate.EntityMatch; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.Property; import eu.esdihumboldt.hale.common.align.model.Type; @@ -25,6 +26,7 @@ import eu.esdihumboldt.hale.common.align.model.impl.TypeEntityDefinition; import eu.esdihumboldt.hale.common.core.report.SimpleLog; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.ui.service.align.resolver.UserFallbackEntityResolver; import eu.esdihumboldt.hale.ui.service.align.resolver.internal.EntityCandidates; @@ -53,21 +55,22 @@ public UserMigration(SchemaSpaceID schemaSpace) { } @Override - public Optional entityReplacement(EntityDefinition entity, SimpleLog log) { + public Optional entityReplacement(EntityDefinition entity, + TypeDefinition preferRoot, SimpleLog log) { // use functionality from entity resolver if (entity instanceof TypeEntityDefinition) { EntityDefinition candidate = entity; Type type = UserFallbackEntityResolver.resolveType((TypeEntityDefinition) entity, candidate, schemaSpace); - return Optional.ofNullable(type).map(e -> e.getDefinition()); + return Optional.ofNullable(type).map(e -> EntityMatch.of(e.getDefinition())); } else if (entity instanceof PropertyEntityDefinition) { EntityDefinition candidate = entity; candidate = EntityCandidates.find((PropertyEntityDefinition) entity); Property property = UserFallbackEntityResolver .resolveProperty((PropertyEntityDefinition) entity, candidate, schemaSpace); - return Optional.ofNullable(property).map(e -> e.getDefinition()); + return Optional.ofNullable(property).map(e -> EntityMatch.of(e.getDefinition())); } else { log.error("Unrecognised entity type: " + entity.getClass());